mirror of
https://github.com/docker/compose.git
synced 2025-09-22 17:27:50 +02:00
Merge pull request #4835 from AlexeyRokhin/master
Add cpu_count, cpu_percent, cpus parameters.
This commit is contained in:
commit
6ed91c3b11
@ -38,6 +38,7 @@ from .types import VolumeSpec
|
|||||||
from .validation import match_named_volumes
|
from .validation import match_named_volumes
|
||||||
from .validation import validate_against_config_schema
|
from .validation import validate_against_config_schema
|
||||||
from .validation import validate_config_section
|
from .validation import validate_config_section
|
||||||
|
from .validation import validate_cpu
|
||||||
from .validation import validate_depends_on
|
from .validation import validate_depends_on
|
||||||
from .validation import validate_extends_file_path
|
from .validation import validate_extends_file_path
|
||||||
from .validation import validate_links
|
from .validation import validate_links
|
||||||
@ -52,8 +53,11 @@ DOCKER_CONFIG_KEYS = [
|
|||||||
'cap_drop',
|
'cap_drop',
|
||||||
'cgroup_parent',
|
'cgroup_parent',
|
||||||
'command',
|
'command',
|
||||||
|
'cpu_count',
|
||||||
|
'cpu_percent',
|
||||||
'cpu_quota',
|
'cpu_quota',
|
||||||
'cpu_shares',
|
'cpu_shares',
|
||||||
|
'cpus',
|
||||||
'cpuset',
|
'cpuset',
|
||||||
'detach',
|
'detach',
|
||||||
'devices',
|
'devices',
|
||||||
@ -640,6 +644,7 @@ def validate_service(service_config, service_names, config_file):
|
|||||||
validate_service_constraints(service_dict, service_name, config_file)
|
validate_service_constraints(service_dict, service_name, config_file)
|
||||||
validate_paths(service_dict)
|
validate_paths(service_dict)
|
||||||
|
|
||||||
|
validate_cpu(service_config)
|
||||||
validate_ulimits(service_config)
|
validate_ulimits(service_config)
|
||||||
validate_network_mode(service_config, service_names)
|
validate_network_mode(service_config, service_names)
|
||||||
validate_depends_on(service_config, service_names)
|
validate_depends_on(service_config, service_names)
|
||||||
|
@ -74,8 +74,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"container_name": {"type": "string"},
|
"container_name": {"type": "string"},
|
||||||
|
"cpu_count": {"type": "integer", "minimum": 0},
|
||||||
|
"cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100},
|
||||||
"cpu_shares": {"type": ["number", "string"]},
|
"cpu_shares": {"type": ["number", "string"]},
|
||||||
"cpu_quota": {"type": ["number", "string"]},
|
"cpu_quota": {"type": ["number", "string"]},
|
||||||
|
"cpus": {"type": "number", "minimum": 0},
|
||||||
"cpuset": {"type": "string"},
|
"cpuset": {"type": "string"},
|
||||||
"depends_on": {
|
"depends_on": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
@ -15,6 +15,7 @@ from jsonschema import RefResolver
|
|||||||
from jsonschema import ValidationError
|
from jsonschema import ValidationError
|
||||||
|
|
||||||
from ..const import COMPOSEFILE_V1 as V1
|
from ..const import COMPOSEFILE_V1 as V1
|
||||||
|
from ..const import NANOCPUS_SCALE
|
||||||
from .errors import ConfigurationError
|
from .errors import ConfigurationError
|
||||||
from .errors import VERSION_EXPLANATION
|
from .errors import VERSION_EXPLANATION
|
||||||
from .sort_services import get_service_name_from_network_mode
|
from .sort_services import get_service_name_from_network_mode
|
||||||
@ -387,6 +388,16 @@ def validate_service_constraints(config, service_name, config_file):
|
|||||||
handle_errors(validator.iter_errors(config), handler, None)
|
handle_errors(validator.iter_errors(config), handler, None)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_cpu(service_config):
|
||||||
|
cpus = service_config.config.get('cpus')
|
||||||
|
if not cpus:
|
||||||
|
return
|
||||||
|
nano_cpus = cpus * NANOCPUS_SCALE
|
||||||
|
if isinstance(nano_cpus, float) and not nano_cpus.is_integer():
|
||||||
|
raise ConfigurationError(
|
||||||
|
"cpus must have nine or less digits after decimal point")
|
||||||
|
|
||||||
|
|
||||||
def get_schema_path():
|
def get_schema_path():
|
||||||
return os.path.dirname(os.path.abspath(__file__))
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ LABEL_NETWORK = 'com.docker.compose.network'
|
|||||||
LABEL_VERSION = 'com.docker.compose.version'
|
LABEL_VERSION = 'com.docker.compose.version'
|
||||||
LABEL_VOLUME = 'com.docker.compose.volume'
|
LABEL_VOLUME = 'com.docker.compose.volume'
|
||||||
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
|
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
|
||||||
|
NANOCPUS_SCALE = 1000000000
|
||||||
|
|
||||||
SECRETS_PATH = '/run/secrets'
|
SECRETS_PATH = '/run/secrets'
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ from .const import LABEL_ONE_OFF
|
|||||||
from .const import LABEL_PROJECT
|
from .const import LABEL_PROJECT
|
||||||
from .const import LABEL_SERVICE
|
from .const import LABEL_SERVICE
|
||||||
from .const import LABEL_VERSION
|
from .const import LABEL_VERSION
|
||||||
|
from .const import NANOCPUS_SCALE
|
||||||
from .container import Container
|
from .container import Container
|
||||||
from .errors import HealthCheckFailed
|
from .errors import HealthCheckFailed
|
||||||
from .errors import NoHealthCheckConfigured
|
from .errors import NoHealthCheckConfigured
|
||||||
@ -52,7 +53,10 @@ HOST_CONFIG_KEYS = [
|
|||||||
'cap_add',
|
'cap_add',
|
||||||
'cap_drop',
|
'cap_drop',
|
||||||
'cgroup_parent',
|
'cgroup_parent',
|
||||||
|
'cpu_count',
|
||||||
|
'cpu_percent',
|
||||||
'cpu_quota',
|
'cpu_quota',
|
||||||
|
'cpus',
|
||||||
'devices',
|
'devices',
|
||||||
'dns',
|
'dns',
|
||||||
'dns_search',
|
'dns_search',
|
||||||
@ -798,6 +802,10 @@ class Service(object):
|
|||||||
init_path = options.get('init')
|
init_path = options.get('init')
|
||||||
options['init'] = True
|
options['init'] = True
|
||||||
|
|
||||||
|
nano_cpus = None
|
||||||
|
if 'cpus' in options:
|
||||||
|
nano_cpus = int(options.get('cpus') * NANOCPUS_SCALE)
|
||||||
|
|
||||||
return self.client.create_host_config(
|
return self.client.create_host_config(
|
||||||
links=self._get_links(link_to_self=one_off),
|
links=self._get_links(link_to_self=one_off),
|
||||||
port_bindings=build_port_bindings(
|
port_bindings=build_port_bindings(
|
||||||
@ -837,6 +845,9 @@ class Service(object):
|
|||||||
init=options.get('init', None),
|
init=options.get('init', None),
|
||||||
init_path=init_path,
|
init_path=init_path,
|
||||||
isolation=options.get('isolation'),
|
isolation=options.get('isolation'),
|
||||||
|
cpu_count=options.get('cpu_count'),
|
||||||
|
cpu_percent=options.get('cpu_percent'),
|
||||||
|
nano_cpus=nano_cpus,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_secret_volumes(self):
|
def get_secret_volumes(self):
|
||||||
|
2
setup.py
2
setup.py
@ -37,7 +37,7 @@ install_requires = [
|
|||||||
'requests >= 2.6.1, != 2.11.0, < 2.12',
|
'requests >= 2.6.1, != 2.11.0, < 2.12',
|
||||||
'texttable >= 0.8.1, < 0.9',
|
'texttable >= 0.8.1, < 0.9',
|
||||||
'websocket-client >= 0.32.0, < 1.0',
|
'websocket-client >= 0.32.0, < 1.0',
|
||||||
'docker >= 2.2.1, < 3.0',
|
'docker >= 2.3.0, < 3.0',
|
||||||
'dockerpty >= 0.4.1, < 0.5',
|
'dockerpty >= 0.4.1, < 0.5',
|
||||||
'six >= 1.3.0, < 2',
|
'six >= 1.3.0, < 2',
|
||||||
'jsonschema >= 2.5.1, < 3',
|
'jsonschema >= 2.5.1, < 3',
|
||||||
|
@ -19,6 +19,7 @@ from .testcases import pull_busybox
|
|||||||
from compose import __version__
|
from compose import __version__
|
||||||
from compose.config.types import VolumeFromSpec
|
from compose.config.types import VolumeFromSpec
|
||||||
from compose.config.types import VolumeSpec
|
from compose.config.types import VolumeSpec
|
||||||
|
from compose.const import IS_WINDOWS_PLATFORM
|
||||||
from compose.const import LABEL_CONFIG_HASH
|
from compose.const import LABEL_CONFIG_HASH
|
||||||
from compose.const import LABEL_CONTAINER_NUMBER
|
from compose.const import LABEL_CONTAINER_NUMBER
|
||||||
from compose.const import LABEL_ONE_OFF
|
from compose.const import LABEL_ONE_OFF
|
||||||
@ -33,6 +34,7 @@ from compose.service import ConvergenceStrategy
|
|||||||
from compose.service import NetworkMode
|
from compose.service import NetworkMode
|
||||||
from compose.service import Service
|
from compose.service import Service
|
||||||
from tests.integration.testcases import v2_1_only
|
from tests.integration.testcases import v2_1_only
|
||||||
|
from tests.integration.testcases import v2_2_only
|
||||||
from tests.integration.testcases import v2_only
|
from tests.integration.testcases import v2_only
|
||||||
from tests.integration.testcases import v3_only
|
from tests.integration.testcases import v3_only
|
||||||
|
|
||||||
@ -110,6 +112,31 @@ class ServiceTest(DockerClientTestCase):
|
|||||||
container.start()
|
container.start()
|
||||||
self.assertEqual(container.get('HostConfig.CpuQuota'), 40000)
|
self.assertEqual(container.get('HostConfig.CpuQuota'), 40000)
|
||||||
|
|
||||||
|
@v2_2_only()
|
||||||
|
def test_create_container_with_cpu_count(self):
|
||||||
|
self.require_api_version('1.25')
|
||||||
|
service = self.create_service('db', cpu_count=2)
|
||||||
|
container = service.create_container()
|
||||||
|
service.start_container(container)
|
||||||
|
self.assertEqual(container.get('HostConfig.CpuCount'), 2)
|
||||||
|
|
||||||
|
@v2_2_only()
|
||||||
|
@pytest.mark.skipif(not IS_WINDOWS_PLATFORM, reason='cpu_percent is not supported for Linux')
|
||||||
|
def test_create_container_with_cpu_percent(self):
|
||||||
|
self.require_api_version('1.25')
|
||||||
|
service = self.create_service('db', cpu_percent=12)
|
||||||
|
container = service.create_container()
|
||||||
|
service.start_container(container)
|
||||||
|
self.assertEqual(container.get('HostConfig.CpuPercent'), 12)
|
||||||
|
|
||||||
|
@v2_2_only()
|
||||||
|
def test_create_container_with_cpus(self):
|
||||||
|
self.require_api_version('1.25')
|
||||||
|
service = self.create_service('db', cpus=1)
|
||||||
|
container = service.create_container()
|
||||||
|
service.start_container(container)
|
||||||
|
self.assertEqual(container.get('HostConfig.NanoCpus'), 1000000000)
|
||||||
|
|
||||||
def test_create_container_with_shm_size(self):
|
def test_create_container_with_shm_size(self):
|
||||||
self.require_api_version('1.22')
|
self.require_api_version('1.22')
|
||||||
service = self.create_service('db', shm_size=67108864)
|
service = self.create_service('db', shm_size=67108864)
|
||||||
|
@ -15,6 +15,7 @@ from compose.const import API_VERSIONS
|
|||||||
from compose.const import COMPOSEFILE_V1 as V1
|
from compose.const import COMPOSEFILE_V1 as V1
|
||||||
from compose.const import COMPOSEFILE_V2_0 as V2_0
|
from compose.const import COMPOSEFILE_V2_0 as V2_0
|
||||||
from compose.const import COMPOSEFILE_V2_0 as V2_1
|
from compose.const import COMPOSEFILE_V2_0 as V2_1
|
||||||
|
from compose.const import COMPOSEFILE_V2_2 as V2_2
|
||||||
from compose.const import COMPOSEFILE_V3_2 as V3_2
|
from compose.const import COMPOSEFILE_V3_2 as V3_2
|
||||||
from compose.const import LABEL_PROJECT
|
from compose.const import LABEL_PROJECT
|
||||||
from compose.progress_stream import stream_output
|
from compose.progress_stream import stream_output
|
||||||
@ -70,10 +71,14 @@ def v2_1_only():
|
|||||||
return build_version_required_decorator((V1, V2_0))
|
return build_version_required_decorator((V1, V2_0))
|
||||||
|
|
||||||
|
|
||||||
def v3_only():
|
def v2_2_only():
|
||||||
return build_version_required_decorator((V1, V2_0, V2_1))
|
return build_version_required_decorator((V1, V2_0, V2_1))
|
||||||
|
|
||||||
|
|
||||||
|
def v3_only():
|
||||||
|
return build_version_required_decorator((V1, V2_0, V2_1, V2_2))
|
||||||
|
|
||||||
|
|
||||||
class DockerClientTestCase(unittest.TestCase):
|
class DockerClientTestCase(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
@ -27,6 +27,7 @@ from compose.config.types import VolumeSpec
|
|||||||
from compose.const import COMPOSEFILE_V1 as V1
|
from compose.const import COMPOSEFILE_V1 as V1
|
||||||
from compose.const import COMPOSEFILE_V2_0 as V2_0
|
from compose.const import COMPOSEFILE_V2_0 as V2_0
|
||||||
from compose.const import COMPOSEFILE_V2_1 as V2_1
|
from compose.const import COMPOSEFILE_V2_1 as V2_1
|
||||||
|
from compose.const import COMPOSEFILE_V2_2 as V2_2
|
||||||
from compose.const import COMPOSEFILE_V3_0 as V3_0
|
from compose.const import COMPOSEFILE_V3_0 as V3_0
|
||||||
from compose.const import COMPOSEFILE_V3_1 as V3_1
|
from compose.const import COMPOSEFILE_V3_1 as V3_1
|
||||||
from compose.const import COMPOSEFILE_V3_2 as V3_2
|
from compose.const import COMPOSEFILE_V3_2 as V3_2
|
||||||
@ -174,6 +175,9 @@ class ConfigTest(unittest.TestCase):
|
|||||||
cfg = config.load(build_config_details({'version': '2.1'}))
|
cfg = config.load(build_config_details({'version': '2.1'}))
|
||||||
assert cfg.version == V2_1
|
assert cfg.version == V2_1
|
||||||
|
|
||||||
|
cfg = config.load(build_config_details({'version': '2.2'}))
|
||||||
|
assert cfg.version == V2_2
|
||||||
|
|
||||||
for version in ['3', '3.0']:
|
for version in ['3', '3.0']:
|
||||||
cfg = config.load(build_config_details({'version': version}))
|
cfg = config.load(build_config_details({'version': version}))
|
||||||
assert cfg.version == V3_0
|
assert cfg.version == V3_0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user