Support squash docker images layers
Implements: blueprint squash-layers Change-Id: Ic9a144e50440ccb37f7781d8955815a64282e687
This commit is contained in:
parent
628af2f8b0
commit
d98a102162
@ -258,6 +258,10 @@ _CLI_OPTS = [
|
|||||||
help='Attempt to pull a newer version of the base image'),
|
help='Attempt to pull a newer version of the base image'),
|
||||||
cfg.StrOpt('work-dir', help=('Path to be used as working directory.'
|
cfg.StrOpt('work-dir', help=('Path to be used as working directory.'
|
||||||
' By default, a temporary dir is created')),
|
' By default, a temporary dir is created')),
|
||||||
|
cfg.BoolOpt('squash', default=False,
|
||||||
|
help=('Squash the image layers. WARNING: it will consume lots'
|
||||||
|
' of disk IO. "docker-squash" tool is required, install'
|
||||||
|
' it by "pip install docker-squash"')),
|
||||||
]
|
]
|
||||||
|
|
||||||
_BASE_OPTS = [
|
_BASE_OPTS = [
|
||||||
@ -269,7 +273,11 @@ _BASE_OPTS = [
|
|||||||
help=('Comma separated list of .rpm or .repo file(s) '
|
help=('Comma separated list of .rpm or .repo file(s) '
|
||||||
'or URL(s) to install before building containers')),
|
'or URL(s) to install before building containers')),
|
||||||
cfg.StrOpt('apt_sources_list', help=('Path to custom sources.list')),
|
cfg.StrOpt('apt_sources_list', help=('Path to custom sources.list')),
|
||||||
cfg.StrOpt('apt_preferences', help=('Path to custom apt/preferences'))
|
cfg.StrOpt('apt_preferences', help=('Path to custom apt/preferences')),
|
||||||
|
cfg.BoolOpt('squash-cleanup', default=True,
|
||||||
|
help='Remove source image from Docker after squashing'),
|
||||||
|
cfg.StrOpt('squash-tmp-dir',
|
||||||
|
help='Temporary directory to be used during squashing')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
73
kolla/common/utils.py
Normal file
73
kolla/common/utils.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess # nosec
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def make_a_logger(conf=None, image_name=None):
|
||||||
|
if image_name:
|
||||||
|
log = logging.getLogger(".".join([__name__, image_name]))
|
||||||
|
else:
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
if not log.handlers:
|
||||||
|
if conf is None or not conf.logs_dir or not image_name:
|
||||||
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
|
log.propagate = False
|
||||||
|
else:
|
||||||
|
filename = os.path.join(conf.logs_dir, "%s.log" % image_name)
|
||||||
|
handler = logging.FileHandler(filename, delay=True)
|
||||||
|
handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
|
||||||
|
log.addHandler(handler)
|
||||||
|
if conf is not None and conf.debug:
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
return log
|
||||||
|
|
||||||
|
|
||||||
|
LOG = make_a_logger()
|
||||||
|
|
||||||
|
|
||||||
|
def get_docker_squash_version():
|
||||||
|
|
||||||
|
try:
|
||||||
|
stdout = subprocess.check_output( # nosec
|
||||||
|
['docker-squash', '--version'], stderr=subprocess.STDOUT)
|
||||||
|
return stdout.split()[0]
|
||||||
|
except OSError as ex:
|
||||||
|
if ex.errno == 2:
|
||||||
|
LOG.error(('"docker-squash" command is not found.'
|
||||||
|
' try to install it by "pip install docker-squash"'))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def squash(old_image, new_image,
|
||||||
|
from_layer=None,
|
||||||
|
cleanup=False,
|
||||||
|
tmp_dir=None):
|
||||||
|
|
||||||
|
cmds = ['docker-squash', '--tag', new_image, old_image]
|
||||||
|
if cleanup:
|
||||||
|
cmds += ['--cleanup']
|
||||||
|
if from_layer:
|
||||||
|
cmds += ['--from-layer', from_layer]
|
||||||
|
if tmp_dir:
|
||||||
|
cmds += ['--tmp-dir', tmp_dir]
|
||||||
|
try:
|
||||||
|
subprocess.check_output(cmds, stderr=subprocess.STDOUT) # nosec
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
LOG.exception('Get error during squashing image: %s',
|
||||||
|
ex.stdout)
|
||||||
|
raise
|
@ -36,6 +36,7 @@ from oslo_config import cfg
|
|||||||
from requests import exceptions as requests_exc
|
from requests import exceptions as requests_exc
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
# NOTE(SamYaple): Update the search path to prefer PROJECT_ROOT as the source
|
# NOTE(SamYaple): Update the search path to prefer PROJECT_ROOT as the source
|
||||||
# of packages to import if we are using local tools instead of
|
# of packages to import if we are using local tools instead of
|
||||||
# pip installed kolla tools
|
# pip installed kolla tools
|
||||||
@ -46,34 +47,14 @@ if PROJECT_ROOT not in sys.path:
|
|||||||
|
|
||||||
from kolla.common import config as common_config
|
from kolla.common import config as common_config
|
||||||
from kolla.common import task
|
from kolla.common import task
|
||||||
|
from kolla.common import utils
|
||||||
from kolla import exception
|
from kolla import exception
|
||||||
from kolla.template import filters as jinja_filters
|
from kolla.template import filters as jinja_filters
|
||||||
from kolla.template import methods as jinja_methods
|
from kolla.template import methods as jinja_methods
|
||||||
from kolla import version
|
from kolla import version
|
||||||
|
|
||||||
|
|
||||||
def make_a_logger(conf=None, image_name=None):
|
LOG = utils.make_a_logger()
|
||||||
if image_name:
|
|
||||||
log = logging.getLogger(".".join([__name__, image_name]))
|
|
||||||
else:
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
if not log.handlers:
|
|
||||||
if conf is None or not conf.logs_dir or not image_name:
|
|
||||||
handler = logging.StreamHandler(sys.stderr)
|
|
||||||
log.propagate = False
|
|
||||||
else:
|
|
||||||
filename = os.path.join(conf.logs_dir, "%s.log" % image_name)
|
|
||||||
handler = logging.FileHandler(filename, delay=True)
|
|
||||||
handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
|
|
||||||
log.addHandler(handler)
|
|
||||||
if conf is not None and conf.debug:
|
|
||||||
log.setLevel(logging.DEBUG)
|
|
||||||
else:
|
|
||||||
log.setLevel(logging.INFO)
|
|
||||||
return log
|
|
||||||
|
|
||||||
|
|
||||||
LOG = make_a_logger()
|
|
||||||
|
|
||||||
# Image status constants.
|
# Image status constants.
|
||||||
#
|
#
|
||||||
@ -261,7 +242,7 @@ class Image(object):
|
|||||||
self.source = source
|
self.source = source
|
||||||
self.parent_name = parent_name
|
self.parent_name = parent_name
|
||||||
if logger is None:
|
if logger is None:
|
||||||
logger = make_a_logger(image_name=name)
|
logger = utils.make_a_logger(image_name=name)
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.children = []
|
self.children = []
|
||||||
self.plugins = []
|
self.plugins = []
|
||||||
@ -586,6 +567,9 @@ class BuildTask(DockerTask):
|
|||||||
if line:
|
if line:
|
||||||
self.logger.error('%s', line)
|
self.logger.error('%s', line)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if image.status != STATUS_ERROR and self.conf.squash:
|
||||||
|
self.squash()
|
||||||
except docker.errors.DockerException:
|
except docker.errors.DockerException:
|
||||||
image.status = STATUS_ERROR
|
image.status = STATUS_ERROR
|
||||||
self.logger.exception('Unknown docker error when building')
|
self.logger.exception('Unknown docker error when building')
|
||||||
@ -596,6 +580,19 @@ class BuildTask(DockerTask):
|
|||||||
image.status = STATUS_BUILT
|
image.status = STATUS_BUILT
|
||||||
self.logger.info('Built')
|
self.logger.info('Built')
|
||||||
|
|
||||||
|
def squash(self):
|
||||||
|
image_tag = self.image.canonical_name
|
||||||
|
image_id = self.dc.inspect_image(image_tag)['Id']
|
||||||
|
|
||||||
|
parent_history = self.dc.history(self.image.parent_name)
|
||||||
|
parent_last_layer = parent_history[0]['Id']
|
||||||
|
self.logger.info('Parent lastest layer is: %s' % parent_last_layer)
|
||||||
|
|
||||||
|
utils.squash(image_id, image_tag, from_layer=parent_last_layer,
|
||||||
|
cleanup=self.conf.squash_cleanup,
|
||||||
|
tmp_dir=self.conf.squash_tmp_dir)
|
||||||
|
self.logger.info('Image is squashed successfully')
|
||||||
|
|
||||||
|
|
||||||
class WorkerThread(threading.Thread):
|
class WorkerThread(threading.Thread):
|
||||||
"""Thread that executes tasks until the queue provides a tombstone."""
|
"""Thread that executes tasks until the queue provides a tombstone."""
|
||||||
@ -1084,7 +1081,7 @@ class KollaWorker(object):
|
|||||||
del match
|
del match
|
||||||
image = Image(image_name, canonical_name, path,
|
image = Image(image_name, canonical_name, path,
|
||||||
parent_name=parent_name,
|
parent_name=parent_name,
|
||||||
logger=make_a_logger(self.conf, image_name),
|
logger=utils.make_a_logger(self.conf, image_name),
|
||||||
docker_client=self.dc)
|
docker_client=self.dc)
|
||||||
|
|
||||||
if self.install_type == 'source':
|
if self.install_type == 'source':
|
||||||
@ -1226,6 +1223,13 @@ def run_build():
|
|||||||
if conf.debug:
|
if conf.debug:
|
||||||
LOG.setLevel(logging.DEBUG)
|
LOG.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
if conf.squash:
|
||||||
|
squash_version = utils.get_docker_squash_version()
|
||||||
|
LOG.info('Image squash is enabled and "docker-squash" version is %s',
|
||||||
|
squash_version)
|
||||||
|
else:
|
||||||
|
LOG.info('Image squash is disabled')
|
||||||
|
|
||||||
kolla = KollaWorker(conf)
|
kolla = KollaWorker(conf)
|
||||||
kolla.setup_working_dir()
|
kolla.setup_working_dir()
|
||||||
kolla.find_dockerfiles()
|
kolla.find_dockerfiles()
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
support --squash parameter which leverage docker-squash tool to squash
|
||||||
|
newly built layers into a single new layer
|
Loading…
x
Reference in New Issue
Block a user