add ulimit support for kolla_docker

By default, docker containers inherit ulimit from limits of docker
deamon. On CentOS 7, docker daemon default NOFILE is 1048576.
It can found in /usr/lib/systemd/system/docker.service.
The big limit will cause many problem. we should control it in
production environment.

Change-Id: Iab962446a94ef092977728259d9818b86cfa7f68
This commit is contained in:
binhong.hua 2019-01-21 11:51:10 +08:00
parent cd0d8cca9b
commit 3d3f5f1613
5 changed files with 125 additions and 19 deletions

View File

@ -474,7 +474,8 @@ class DockerWorker(object):
'memswap_limit': 'MemorySwap', 'cpu_period': 'CpuPeriod',
'cpu_quota': 'CpuQuota', 'cpu_shares': 'CpuShares',
'cpuset_cpus': 'CpusetCpus', 'cpuset_mems': 'CpusetMems',
'kernel_memory': 'KernelMemory', 'blkio_weight': 'BlkioWeight'}
'kernel_memory': 'KernelMemory', 'blkio_weight': 'BlkioWeight',
'ulimits': 'Ulimits'}
unsupported = set(new_dimensions.keys()) - \
set(dimension_map.keys())
if unsupported:
@ -486,13 +487,29 @@ class DockerWorker(object):
# NOTE(mgoddard): If a resource has been explicitly requested,
# check for a match. Otherwise, ensure is is set to the default.
if key1 in new_dimensions:
if new_dimensions[key1] != current_dimensions[key2]:
if key1 == 'ulimits':
if self.compare_ulimits(new_dimensions[key1],
current_dimensions[key2]):
return True
elif new_dimensions[key1] != current_dimensions[key2]:
return True
elif current_dimensions[key2]:
# The default values of all currently supported resources are
# '' or 0 - both falsey.
return True
def compare_ulimits(self, new_ulimits, current_ulimits):
# The new_ulimits is dict, we need make it to a list of Ulimit
# instance.
new_ulimits = self.build_ulimits(new_ulimits)
def key(ulimit):
return ulimit['Name']
if current_ulimits is None:
current_ulimits = []
return sorted(new_ulimits, key=key) != sorted(current_ulimits, key=key)
def compare_command(self, container_info):
new_command = self.params.get('command')
if new_command is not None:
@ -606,6 +623,40 @@ class DockerWorker(object):
return vol_list, vol_dict
def parse_dimensions(self, dimensions):
# When the data object contains types such as
# docker.types.Ulimit, Ansible will fail when these are
# returned via exit_json or fail_json. HostConfig is derived from dict,
# but its constructor requires additional arguments.
# to avoid that, here do copy the dimensions and return a new one.
dimensions = dimensions.copy()
supported = {'cpu_period', 'cpu_quota', 'cpu_shares',
'cpuset_cpus', 'cpuset_mems', 'mem_limit',
'mem_reservation', 'memswap_limit',
'kernel_memory', 'blkio_weight', 'ulimits'}
unsupported = set(dimensions) - supported
if unsupported:
self.module.exit_json(failed=True,
msg=repr("Unsupported dimensions"),
unsupported_dimensions=unsupported)
ulimits = dimensions.get('ulimits')
if ulimits:
dimensions['ulimits'] = self.build_ulimits(ulimits)
return dimensions
def build_ulimits(self, ulimits):
ulimits_opt = []
for key, value in ulimits.items():
soft = value.get('soft')
hard = value.get('hard')
ulimits_opt.append(docker.types.Ulimit(name=key,
soft=soft,
hard=hard))
return ulimits_opt
def build_host_config(self, binds):
options = {
'network_mode': 'host',
@ -617,17 +668,11 @@ class DockerWorker(object):
'volumes_from': self.params.get('volumes_from')
}
if self.params.get('dimensions'):
supported = {'cpu_period', 'cpu_quota', 'cpu_shares',
'cpuset_cpus', 'cpuset_mems', 'mem_limit',
'mem_reservation', 'memswap_limit',
'kernel_memory', 'blkio_weight'}
unsupported = set(self.params.get('dimensions')) - supported
if unsupported:
self.module.exit_json(failed=True,
msg=repr("Unsupported dimensions"),
unsupported_dimensions=unsupported)
options.update(self.params.get('dimensions'))
dimensions = self.params.get('dimensions')
if dimensions:
dimensions = self.parse_dimensions(dimensions)
options.update(dimensions)
if self.params.get('restart_policy') in ['on-failure',
'always',
@ -940,5 +985,6 @@ def main():
module.fail_json(changed=True, msg=repr(traceback.format_exc()),
**dw.result)
if __name__ == '__main__':
main()

View File

@ -27,6 +27,7 @@ The resources currently supported by Kolla Ansible are:
memswap_limit
kernel_memory
blkio_weight
ulimits
Pre-deployment Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -67,6 +68,9 @@ Dimensions are defined as a mapping from a Docker resource name
* - cpuset_mems
- String
- ''(Empty String)
* - ulimits
- Dict
- {}
The variable ``default_container_dimensions`` sets the default dimensions
@ -96,6 +100,23 @@ options section in ``/etc/kolla/globals.yml``:
nova_libvirt_dimensions:
cpuset_cpus: "2"
How to config ulimits in kolla
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: yaml
<container_name>_dimensions:
ulimits:
nofile:
soft: 131072
hard: 131072
fsize:
soft: 131072
hard: 131072
A list of valid names can be found [here]
(https://github.com/docker/go-units/blob/d4a9b9617350c034730bc5051c605919943080bf/ulimit.go#L46-L63)
Deployment
~~~~~~~~~~

View File

@ -142,6 +142,7 @@ kolla_internal_vip_address: "10.10.10.254"
# mem_reservation:
# memswap_limit:
# kernel_memory:
# ulimits:
#############

View File

@ -0,0 +1,5 @@
---
features:
- |
Add ulimit support for kolla_docker which provides ability
of modify docker ulimits。

View File

@ -23,6 +23,7 @@ try:
except ImportError:
import mock
from docker import errors as docker_error
from docker.types import Ulimit
from oslotest import base
this_dir = os.path.dirname(sys.modules[__name__].__file__)
@ -904,7 +905,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertTrue(self.dw.compare_dimensions(container_info))
@ -915,7 +917,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 10,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertFalse(self.dw.compare_dimensions(container_info))
@ -926,7 +929,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.dw.compare_dimensions(container_info)
self.dw.module.exit_json.assert_called_once_with(
@ -939,7 +943,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '1', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertTrue(self.dw.compare_dimensions(container_info))
@ -954,7 +959,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 10, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 10}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 10,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertTrue(self.dw.compare_dimensions(container_info))
@ -969,7 +975,8 @@ class TestAttrComp(base.BaseTestCase):
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0}
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertFalse(self.dw.compare_dimensions(container_info))
@ -977,3 +984,29 @@ class TestAttrComp(base.BaseTestCase):
container_info = {'State': dict(Status='running')}
self.dw = get_DockerWorker({'state': 'exited'})
self.assertTrue(self.dw.compare_container_state(container_info))
def test_compare_ulimits_pos(self):
self.fake_data['params']['dimensions'] = {
'ulimits': {'nofile': {'soft': 131072, 'hard': 131072}}}
container_info = dict()
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': []}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertTrue(self.dw.compare_dimensions(container_info))
def test_compare_ulimits_neg(self):
self.fake_data['params']['dimensions'] = {
'ulimits': {'nofile': {'soft': 131072, 'hard': 131072}}}
ulimits_nofile = Ulimit(name='nofile',
soft=131072, hard=131072)
container_info = dict()
container_info['HostConfig'] = {
'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0,
'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0,
'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0,
'Ulimits': [ulimits_nofile]}
self.dw = get_DockerWorker(self.fake_data['params'])
self.assertFalse(self.dw.compare_dimensions(container_info))