Skip to content

Commit

Permalink
CPU requests and limits for build pod
Browse files Browse the repository at this point in the history
  • Loading branch information
noroutine committed Aug 24, 2021
1 parent 5ba7fe1 commit 0395ca4
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 4 deletions.
24 changes: 23 additions & 1 deletion binderhub/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
DataverseProvider)
from .metrics import MetricsHandler

from .utils import ByteSpecification, url_path_join
from .utils import CPUSpecification, ByteSpecification, url_path_join
from .events import EventLog


Expand Down Expand Up @@ -335,6 +335,26 @@ def _valid_badge_base_url(self, proposal):
config=True
)

build_cpu_request = CPUSpecification(
0,
help="""
Amount of cpu to request when scheduling a build
0 reserves no cpu.
""",
config=True,
)
build_cpu_limit = CPUSpecification(
0,
help="""
Max amount of cpu allocated for each image build process.
0 sets no limit.
""",
config=True,
)

build_memory_request = ByteSpecification(
0,
help="""
Expand Down Expand Up @@ -773,6 +793,8 @@ def initialize(self, *args, **kwargs):
"jinja2_env": jinja_env,
"build_memory_limit": self.build_memory_limit,
"build_memory_request": self.build_memory_request,
"build_cpu_limit": self.build_cpu_limit,
"build_cpu_request": self.build_cpu_request,
"build_docker_host": self.build_docker_host,
"build_docker_config": self.build_docker_config,
"base_url": self.base_url,
Expand Down
20 changes: 18 additions & 2 deletions binderhub/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def __init__(
image_name,
git_credentials=None,
push_secret=None,
cpu_limit=0,
cpu_request=0,
memory_limit=0,
memory_request=0,
node_selector=None,
Expand Down Expand Up @@ -95,6 +97,18 @@ def __init__(
https://git-scm.com/docs/gitcredentials for more information.
push_secret : str
Kubernetes secret containing credentials to push docker image to registry.
cpu_limit
CPU limit for the docker build process. Can be an integer (1), fraction (0.5) or
millicore specification (100m). Value should adhere to K8s specification
for CPU meaning. See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
for more information
cpu_request
CPU request of the build pod. The actual building happens in the
docker daemon, but setting request in the build pod makes sure that
cpu is reserved for the docker build in the node by the kubernetes
scheduler. Value should adhere to K8s specification for CPU meaning.
See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
for more information
memory_limit
Memory limit for the docker build process. Can be an integer in
bytes, or a byte specification (like 6M).
Expand Down Expand Up @@ -129,6 +143,8 @@ def __init__(
self.push_secret = push_secret
self.build_image = build_image
self.main_loop = IOLoop.current()
self.cpu_limit = cpu_limit
self.cpu_request = cpu_request
self.memory_limit = memory_limit
self.memory_request = memory_request
self.docker_host = docker_host
Expand Down Expand Up @@ -343,8 +359,8 @@ def submit(self):
args=self.get_cmd(),
volume_mounts=volume_mounts,
resources=client.V1ResourceRequirements(
limits={'memory': self.memory_limit},
requests={'memory': self.memory_request},
limits={'memory': self.memory_limit, 'cpu': self.cpu_limit},
requests={'memory': self.memory_request, 'cpu': self.cpu_request},
),
env=env
)
Expand Down
2 changes: 2 additions & 0 deletions binderhub/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ async def get(self, provider_prefix, _unescaped_spec):
image_name=image_name,
push_secret=push_secret,
build_image=self.settings['build_image'],
cpu_limit=self.settings['build_cpu_limit'],
cpu_request=self.settings['build_cpu_request'],
memory_limit=self.settings['build_memory_limit'],
memory_request=self.settings['build_memory_request'],
docker_host=self.settings['build_docker_host'],
Expand Down
70 changes: 69 additions & 1 deletion binderhub/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import ipaddress
import time

from traitlets import Integer, TraitError
from traitlets import Unicode, Integer, TraitError


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


class CPUSpecification(Unicode):
"""
Allows specifying CPU limits
Suffixes allowed are:
- m -> millicore
"""

# Default to allowing None as a value
allow_none = True

def validate(self, obj, value):
"""
Validate that the passed in value is a valid cpu specification
in the K8s CPU meaning.
See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-cpu
It could either be a pure int or float, when it is taken as a value.
In case of integer it can optionally have 'm' suffix to designate millicores.
"""

if isinstance(value, int):
return int(value)

if isinstance(value, float):
return float(value)

# Try treat it as integer
_int_value = None
try:
_int_value = int(value)
except ValueError:
pass

if isinstance(_int_value, int):
return _int_value

# Try treat it as float
_float_value = None
try:
_float_value = float(value)
except ValueError:
pass

if isinstance(_float_value, float):
return _float_value

# Try treat it as millicore spec
try:
_unused = int(value[:-1])
except ValueError:
raise TraitError(
"{val} is not a valid cpu specification".format(
val=value
)
)

if value[-1] not in ['m']:
raise TraitError(
"{val} is not a valid cpu specification".format(
val=value
)
)

return value

class ByteSpecification(Integer):
"""
Allow easily specifying bytes in units of 1024 with suffixes
Expand Down

0 comments on commit 0395ca4

Please sign in to comment.