mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 23:45:44 +07:00
[IMP] runbot: add cpus parameter
When build eats all the CPU's resources, it may interfere with other builds and cause collateral damages. With this commit, a new settings parameter `Containers CPUs` is added in order to limit the usage of available CPU's on runbot instances. If left to 0, no limts are applied. Otherwise, the cpu_quota docker parameter is computed as Containers CPU's * (logical cpu's count / nb parallel builds) * cpu period which defaults to 100000. e.g.: - on a host with 16 logical CPU's - with 8 parallel builds allowed - with Containers CPUs set to 1.5 - with the default cpu_period cpu_quota will be: (16/8) * 1.5 * 100000 = 300000 This system parameter can be overridden by the `container_cpus` field on steps.
This commit is contained in:
parent
0fc1daeac9
commit
d0a96faf84
@ -125,7 +125,7 @@ def docker_run(*args, **kwargs):
|
||||
return _docker_run(*args, **kwargs)
|
||||
|
||||
|
||||
def _docker_run(cmd=False, log_path=False, build_dir=False, container_name=False, image_tag=False, exposed_ports=None, cpu_limit=None, memory=None, preexec_fn=None, ro_volumes=None, env_variables=None):
|
||||
def _docker_run(cmd=False, log_path=False, build_dir=False, container_name=False, image_tag=False, exposed_ports=None, cpu_limit=None, cpu_period=100000, cpus=0, memory=None, preexec_fn=None, ro_volumes=None, env_variables=None):
|
||||
"""Run tests in a docker container
|
||||
:param run_cmd: command string to run in container
|
||||
:param log_path: path to the logfile that will contain odoo stdout and stderr
|
||||
@ -134,6 +134,8 @@ def _docker_run(cmd=False, log_path=False, build_dir=False, container_name=False
|
||||
:param container_name: used to give a name to the container for later reference
|
||||
:param image_tag: Docker image tag name to select which docker image to use
|
||||
:param exposed_ports: if not None, starting at 8069, ports will be exposed as exposed_ports numbers
|
||||
:param cpu_period: Specify the CPU CFS scheduler period, which is used alongside cpu_quota
|
||||
:param cpus: used to compute cpu_quota = cpu_period * cpus (equivalent of --cpus in docker CLI)
|
||||
:param memory: memory limit in bytes for the container
|
||||
:params ro_volumes: dict of dest:source volumes to mount readonly in builddir
|
||||
:params env_variables: list of environment variables
|
||||
@ -187,6 +189,8 @@ def _docker_run(cmd=False, log_path=False, build_dir=False, container_name=False
|
||||
mem_limit=memory,
|
||||
ports=ports,
|
||||
ulimits=ulimits,
|
||||
cpu_period=cpu_period,
|
||||
cpu_quota=int(cpus * cpu_period ) if cpus else None,
|
||||
environment=env_variables,
|
||||
init=True,
|
||||
command=['/bin/bash', '-c',
|
||||
|
@ -843,6 +843,7 @@ class BuildResult(models.Model):
|
||||
containers_memory_limit = self.env['ir.config_parameter'].sudo().get_param('runbot.runbot_containers_memory', 0)
|
||||
if containers_memory_limit and 'memory' not in kwargs:
|
||||
kwargs['memory'] = int(float(containers_memory_limit) * 1024 ** 3)
|
||||
|
||||
self.docker_start = now()
|
||||
if self.job_start:
|
||||
start_step_time = int(dt2time(self.docker_start) - dt2time(self.job_start))
|
||||
|
@ -3,6 +3,7 @@ import glob
|
||||
import json
|
||||
import logging
|
||||
import fnmatch
|
||||
import psutil
|
||||
import re
|
||||
import shlex
|
||||
import time
|
||||
@ -134,6 +135,7 @@ class ConfigStep(models.Model):
|
||||
install_modules = fields.Char('Modules to install', help="List of module patterns to install, use * to install all available modules, prefix the pattern with dash to remove the module.", default='')
|
||||
db_name = fields.Char('Db Name', compute='_compute_db_name', inverse='_inverse_db_name', tracking=True)
|
||||
cpu_limit = fields.Integer('Cpu limit', default=3600, tracking=True)
|
||||
container_cpus = fields.Integer('Allowed CPUs', help='Allowed container CPUs. Fallback on config parameter if 0.', default=0, tracking=True)
|
||||
coverage = fields.Boolean('Coverage', default=False, tracking=True)
|
||||
paths_to_omit = fields.Char('Paths to omit from coverage', tracking=True)
|
||||
flamegraph = fields.Boolean('Allow Flamegraph', default=False, tracking=True)
|
||||
@ -263,6 +265,12 @@ class ConfigStep(models.Model):
|
||||
docker_params = run_method(build, **kwargs)
|
||||
if docker_params:
|
||||
return build._docker_run(self, **docker_params)
|
||||
container_cpus = float(self.container_cpus or self.env['ir.config_parameter'].sudo().get_param('runbot.runbot_containers_cpus', 0))
|
||||
if 'cpus' not in docker_params and container_cpus:
|
||||
logical_cpu_count = psutil.cpu_count(logical=True)
|
||||
physical_cpu_count = psutil.cpu_count(logical=False)
|
||||
docker_params['cpus'] = float((logical_cpu_count / physical_cpu_count) * container_cpus)
|
||||
build._docker_run(**docker_params)
|
||||
return True
|
||||
|
||||
def _run_create_build(self, build):
|
||||
|
@ -11,6 +11,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
|
||||
runbot_workers = fields.Integer('Default number of workers')
|
||||
runbot_containers_memory = fields.Float('Containers Mem limit (in GiB)')
|
||||
runbot_containers_cpus = fields.Float('Allowed Containers CPUs (0 means no limit)')
|
||||
runbot_memory_bytes = fields.Float('Bytes', compute='_compute_memory_bytes')
|
||||
runbot_running_max = fields.Integer('Max running builds')
|
||||
runbot_timeout = fields.Integer('Max step timeout (in seconds)')
|
||||
@ -59,6 +60,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
get_param = self.env['ir.config_parameter'].sudo().get_param
|
||||
res.update(runbot_workers=int(get_param('runbot.runbot_workers', default=2)),
|
||||
runbot_containers_cpus=float(get_param('runbot.runbot_containers_cpus', default=0)),
|
||||
runbot_containers_memory=float(get_param('runbot.runbot_containers_memory', default=0)),
|
||||
runbot_running_max=int(get_param('runbot.runbot_running_max', default=5)),
|
||||
runbot_timeout=int(get_param('runbot.runbot_timeout', default=10000)),
|
||||
@ -82,6 +84,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
set_param = self.env['ir.config_parameter'].sudo().set_param
|
||||
set_param("runbot.runbot_workers", self.runbot_workers)
|
||||
set_param("runbot.runbot_containers_cpus", self.runbot_containers_cpus)
|
||||
set_param("runbot.runbot_containers_memory", self.runbot_containers_memory)
|
||||
set_param("runbot.runbot_running_max", self.runbot_running_max)
|
||||
set_param("runbot.runbot_timeout", self.runbot_timeout)
|
||||
|
@ -47,6 +47,9 @@
|
||||
<field name="runbot_containers_memory"/>
|
||||
<field name="runbot_memory_bytes" readonly='1' class="text-muted"/>
|
||||
</setting>
|
||||
<setting>
|
||||
<field name="runbot_containers_cpus"/>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<block title="GC">
|
||||
|
Loading…
Reference in New Issue
Block a user