Support health check for Docker containers

The docker has supported healthcheck. It is useful for application
container.
Zun may integration these options.

  --health-cmd Command to run to check health
  --health-interval Time between running the check
  --health-retries Consecutive failures needed to report unhealthy
  --health-timeout Maximum time to allow one check to run
  In zun, the four parameter are cmd, interval, retries, timeout.

Partial-Implements: blueprint support-healthycheck

Change-Id: I1f4f6a5536a33ecb9ac9c032b119a9349e3616ee
This commit is contained in:
weikeyou 2018-07-31 16:30:21 +08:00
parent 99816f5d23
commit 68df96cb2f
16 changed files with 114 additions and 7 deletions

View File

@ -355,6 +355,12 @@ class ContainersController(base.Controller):
pci_req = self._create_pci_requests_for_sriov_ports(context,
requested_networks)
healthcheck = container_dict.pop('healthcheck', {})
if healthcheck:
api_utils.version_check('healthcheck', '1.22')
healthcheck['test'] = healthcheck.pop('cmd', '')
container_dict['healthcheck'] = healthcheck
mounts = container_dict.pop('mounts', [])
if mounts:
api_utils.version_check('mounts', '1.11')

View File

@ -38,6 +38,7 @@ _legacy_container_properties = {
'availability_zone': parameter_types.availability_zone,
'auto_heal': parameter_types.boolean,
'privileged': parameter_types.boolean,
'healthcheck': parameter_types.healthcheck,
}
legacy_container_create = {

View File

@ -172,6 +172,30 @@ availability_zone = {
'maxLength': 255,
}
healthcheck = {
'type': ['object', 'null'],
'items': {
'type': 'object',
'properties': {
'cmd': {
'type': ['string'],
'minLength': 1,
'maxLength': 255
},
'interval': {
'type': ['integer', 'null']
},
'retries': {
'type': ['integer', 'null']
},
'timeout': {
'type': ['integer', 'null']
}
},
'additionalProperties': False
}
}
mounts = {
'type': ['array', 'null'],
'items': {

View File

@ -48,6 +48,7 @@ _basic_keys = (
'disk',
'auto_heal',
'privileged',
'healthcheck',
)

View File

@ -51,13 +51,14 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 1.16 - Modify restart_policy to capsule spec content
* 1.17 - Add support for detaching ports
* 1.18 - Modify the response of network list
* 1.19 - Intoduce container resize API
* 1.19 - Introduce container resize API
* 1.20 - Convert type of 'command' from string to list
* 1.21 - Add support privileged
* 1.22 - Add healthcheck to container create
"""
BASE_VER = '1.1'
CURRENT_MAX_VER = '1.21'
CURRENT_MAX_VER = '1.22'
class Version(object):

View File

@ -177,3 +177,9 @@ user documentation.
----
Support privileged container
1.22
----
Add healthcheck to container create

View File

@ -301,6 +301,18 @@ class DockerDriver(driver.ContainerDriver):
if container.disk:
disk_size = str(container.disk) + 'G'
host_config['storage_opt'] = {'size': disk_size}
# The time unit in docker of heath checking is us, and the unit
# of interval and timeout is seconds.
if container.healthcheck:
healthcheck = {}
healthcheck['test'] = container.healthcheck.get('test', '')
interval = container.healthcheck.get('interval', 0)
healthcheck['interval'] = interval * 10 ** 9
healthcheck['retries'] = int(container.healthcheck.
get('retries', 0))
timeout = container.healthcheck.get('timeout', 0)
healthcheck['timeout'] = timeout * 10 ** 9
kwargs['healthcheck'] = healthcheck
kwargs['host_config'] = docker.create_host_config(**host_config)
if image['tag']:

View File

@ -0,0 +1,36 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""add healthcheck to containers
Revision ID: 2fb377a5a519
Revises: 105626c4f972
Create Date: 2018-05-03 11:27:00.722445
"""
# revision identifiers, used by Alembic.
revision = '2fb377a5a519'
down_revision = '105626c4f972'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
import zun
def upgrade():
with op.batch_alter_table('container', schema=None) as batch_op:
batch_op.add_column(sa.Column(
'healthcheck', zun.db.sqlalchemy.models.JSONEncodedDict(),
nullable=True))

View File

@ -171,6 +171,7 @@ class Container(Base):
ForeignKey('capsule.id', ondelete='CASCADE'))
started_at = Column(DateTime)
privileged = Column(Boolean, default=False)
healthcheck = Column(JSONEncodedDict)
class VolumeMapping(Base):

View File

@ -64,7 +64,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
# Version 1.32: Add 'exec_instances' attribute
# Version 1.33: Change 'command' to List type
# Version 1.34: Add privileged to container
VERSION = '1.34'
# Version 1.35: Add 'healthcheck' attribute
VERSION = '1.35'
fields = {
'id': fields.IntegerField(),
@ -107,6 +108,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
'exec_instances': fields.ListOfObjectsField('ExecInstance',
nullable=True),
'privileged': fields.BooleanField(nullable=True),
'healthcheck': z_fields.JsonField(nullable=True),
}
@staticmethod

View File

@ -26,7 +26,7 @@ from zun.tests.unit.db import base
PATH_PREFIX = '/v1'
CURRENT_VERSION = "container 1.21"
CURRENT_VERSION = "container 1.22"
class FunctionalTest(base.DbTestCase):

View File

@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest):
'default_version':
{'id': 'v1',
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
'max_version': '1.21',
'max_version': '1.22',
'min_version': '1.1',
'status': 'CURRENT'},
'description': 'Zun is an OpenStack project which '
@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest):
'versions': [{'id': 'v1',
'links': [{'href': 'http://localhost/v1/',
'rel': 'self'}],
'max_version': '1.21',
'max_version': '1.22',
'min_version': '1.1',
'status': 'CURRENT'}]}

View File

@ -121,6 +121,16 @@ class TestContainerController(api_base.FunctionalTest):
params=params, content_type='application/json',
headers=headers)
def test_run_container_healthcheck_wrong_value(self):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"healthcheck": "test"}')
with self.assertRaisesRegex(AppError,
"Invalid input for field"):
self.post('/v1/containers?run=true',
params=params, content_type='application/json')
@patch('zun.network.neutron.NeutronAPI.get_available_network')
@patch('zun.compute.api.API.container_create')
@patch('zun.compute.api.API.image_search')

View File

@ -117,6 +117,7 @@ class TestDockerDriver(base.DriverTestCase):
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
mock_container = self.mock_default_container
mock_container.status = 'Creating'
mock_container.healthcheck = {}
networks = [{'network': 'fake-network'}]
volumes = []
fake_port = {'mac_address': 'fake_mac'}
@ -183,6 +184,7 @@ class TestDockerDriver(base.DriverTestCase):
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
mock_container = self.mock_default_container
mock_container.status = 'Creating'
mock_container.healthcheck = {}
networks = [{'network': 'fake-network'}]
volumes = []
fake_port = {'mac_address': 'fake_mac'}
@ -251,6 +253,7 @@ class TestDockerDriver(base.DriverTestCase):
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
mock_container = self.mock_default_container
mock_container.status = 'Creating'
mock_container.healthcheck = {}
mock_container.runtime = None
networks = [{'network': 'fake-network'}]
volumes = []

View File

@ -103,6 +103,10 @@ def get_test_container(**kwargs):
'capsule_id': kwargs.get('capsule_id', 42),
'started_at': kwargs.get('started_at'),
'privileged': kwargs.get('privileged', False),
'healthcheck': kwargs.get('healthcheck',
{"retries": "2", "timeout": 3,
"test": "stat /etc/passwd || exit 1",
"interval": 3}),
}

View File

@ -344,7 +344,7 @@ class TestObject(test_base.TestCase, _TestObject):
# For more information on object version testing, read
# https://docs.openstack.org/zun/latest/
object_data = {
'Container': '1.34-22c46c6ae571b83295c3dac74fe8772f',
'Container': '1.35-7cadf071bb6865a6da6b7be581ce76f6',
'VolumeMapping': '1.1-50df6202f7846a136a91444c38eba841',
'Image': '1.1-330e6205c80b99b59717e1cfc6a79935',
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',