Skip to content

Commit 0395ca4

Browse files
committed
CPU requests and limits for build pod
1 parent 5ba7fe1 commit 0395ca4

File tree

4 files changed

+112
-4
lines changed

4 files changed

+112
-4
lines changed

binderhub/app.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
DataverseProvider)
5858
from .metrics import MetricsHandler
5959

60-
from .utils import ByteSpecification, url_path_join
60+
from .utils import CPUSpecification, ByteSpecification, url_path_join
6161
from .events import EventLog
6262

6363

@@ -335,6 +335,26 @@ def _valid_badge_base_url(self, proposal):
335335
config=True
336336
)
337337

338+
build_cpu_request = CPUSpecification(
339+
0,
340+
help="""
341+
Amount of cpu to request when scheduling a build
342+
343+
0 reserves no cpu.
344+
345+
""",
346+
config=True,
347+
)
348+
build_cpu_limit = CPUSpecification(
349+
0,
350+
help="""
351+
Max amount of cpu allocated for each image build process.
352+
353+
0 sets no limit.
354+
""",
355+
config=True,
356+
)
357+
338358
build_memory_request = ByteSpecification(
339359
0,
340360
help="""
@@ -773,6 +793,8 @@ def initialize(self, *args, **kwargs):
773793
"jinja2_env": jinja_env,
774794
"build_memory_limit": self.build_memory_limit,
775795
"build_memory_request": self.build_memory_request,
796+
"build_cpu_limit": self.build_cpu_limit,
797+
"build_cpu_request": self.build_cpu_request,
776798
"build_docker_host": self.build_docker_host,
777799
"build_docker_config": self.build_docker_config,
778800
"base_url": self.base_url,

binderhub/build.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def __init__(
5151
image_name,
5252
git_credentials=None,
5353
push_secret=None,
54+
cpu_limit=0,
55+
cpu_request=0,
5456
memory_limit=0,
5557
memory_request=0,
5658
node_selector=None,
@@ -95,6 +97,18 @@ def __init__(
9597
https://git-scm.com/docs/gitcredentials for more information.
9698
push_secret : str
9799
Kubernetes secret containing credentials to push docker image to registry.
100+
cpu_limit
101+
CPU limit for the docker build process. Can be an integer (1), fraction (0.5) or
102+
millicore specification (100m). Value should adhere to K8s specification
103+
for CPU meaning. See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
104+
for more information
105+
cpu_request
106+
CPU request of the build pod. The actual building happens in the
107+
docker daemon, but setting request in the build pod makes sure that
108+
cpu is reserved for the docker build in the node by the kubernetes
109+
scheduler. Value should adhere to K8s specification for CPU meaning.
110+
See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
111+
for more information
98112
memory_limit
99113
Memory limit for the docker build process. Can be an integer in
100114
bytes, or a byte specification (like 6M).
@@ -129,6 +143,8 @@ def __init__(
129143
self.push_secret = push_secret
130144
self.build_image = build_image
131145
self.main_loop = IOLoop.current()
146+
self.cpu_limit = cpu_limit
147+
self.cpu_request = cpu_request
132148
self.memory_limit = memory_limit
133149
self.memory_request = memory_request
134150
self.docker_host = docker_host
@@ -343,8 +359,8 @@ def submit(self):
343359
args=self.get_cmd(),
344360
volume_mounts=volume_mounts,
345361
resources=client.V1ResourceRequirements(
346-
limits={'memory': self.memory_limit},
347-
requests={'memory': self.memory_request},
362+
limits={'memory': self.memory_limit, 'cpu': self.cpu_limit},
363+
requests={'memory': self.memory_request, 'cpu': self.cpu_request},
348364
),
349365
env=env
350366
)

binderhub/builder.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ async def get(self, provider_prefix, _unescaped_spec):
411411
image_name=image_name,
412412
push_secret=push_secret,
413413
build_image=self.settings['build_image'],
414+
cpu_limit=self.settings['build_cpu_limit'],
415+
cpu_request=self.settings['build_cpu_request'],
414416
memory_limit=self.settings['build_memory_limit'],
415417
memory_request=self.settings['build_memory_request'],
416418
docker_host=self.settings['build_docker_host'],

binderhub/utils.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import ipaddress
55
import time
66

7-
from traitlets import Integer, TraitError
7+
from traitlets import Unicode, Integer, TraitError
88

99

1010
# default _request_timeout for kubernetes api requests
@@ -42,6 +42,74 @@ def rendezvous_rank(buckets, key):
4242
return [b for (s, b) in sorted(ranking, reverse=True)]
4343

4444

45+
class CPUSpecification(Unicode):
46+
"""
47+
Allows specifying CPU limits
48+
49+
Suffixes allowed are:
50+
- m -> millicore
51+
52+
"""
53+
54+
# Default to allowing None as a value
55+
allow_none = True
56+
57+
def validate(self, obj, value):
58+
"""
59+
Validate that the passed in value is a valid cpu specification
60+
in the K8s CPU meaning.
61+
62+
See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
63+
64+
It could either be a pure int or float, when it is taken as a value.
65+
In case of integer it can optionally have 'm' suffix to designate millicores.
66+
"""
67+
68+
if isinstance(value, int):
69+
return int(value)
70+
71+
if isinstance(value, float):
72+
return float(value)
73+
74+
# Try treat it as integer
75+
_int_value = None
76+
try:
77+
_int_value = int(value)
78+
except ValueError:
79+
pass
80+
81+
if isinstance(_int_value, int):
82+
return _int_value
83+
84+
# Try treat it as float
85+
_float_value = None
86+
try:
87+
_float_value = float(value)
88+
except ValueError:
89+
pass
90+
91+
if isinstance(_float_value, float):
92+
return _float_value
93+
94+
# Try treat it as millicore spec
95+
try:
96+
_unused = int(value[:-1])
97+
except ValueError:
98+
raise TraitError(
99+
"{val} is not a valid cpu specification".format(
100+
val=value
101+
)
102+
)
103+
104+
if value[-1] not in ['m']:
105+
raise TraitError(
106+
"{val} is not a valid cpu specification".format(
107+
val=value
108+
)
109+
)
110+
111+
return value
112+
45113
class ByteSpecification(Integer):
46114
"""
47115
Allow easily specifying bytes in units of 1024 with suffixes

0 commit comments

Comments
 (0)