Optimizing image downloads

In this commit, we obtain a list of images already present in
containerd to avoid unnecessary checks and pulls, reducing CPU
consumption.

TEST PLAN:
PASS: Lock/Unlock controllers
PASS: Successfully swact between controllers
PASS: Successfully recover after power down and up both controllers
PASS: Successfully bootstrap (Simplex and Duplex)
PASS: Successfully recover after active controller goes down
PASS: Successfully application lifecycle

Story: 2010985
Task: 49228

Change-Id: I58dd11c8d590b60ab100f79a03e17c5921e3721b
Signed-off-by: Thiago Miranda <tmarques@windriver.com>
Co-authored-by: Eduardo Juliano Alberti <eduardo.alberti@windriver.com>
This commit is contained in:
Thiago Miranda 2023-12-11 14:06:49 -03:00 committed by Eduardo Alberti
parent d65514be34
commit caf9de1603
2 changed files with 43 additions and 9 deletions

View File

@ -2070,6 +2070,9 @@ APP_VERSION_PLACEHOLDER = 'app-version-placeholder'
APP_MANIFEST_NAME_PLACEHOLDER = 'manifest-placeholder' APP_MANIFEST_NAME_PLACEHOLDER = 'manifest-placeholder'
APP_TARFILE_NAME_PLACEHOLDER = 'tarfile-placeholder' APP_TARFILE_NAME_PLACEHOLDER = 'tarfile-placeholder'
# Application constants
APP_INSTALLATION_TIMEOUT = 3600
# Default node labels # Default node labels
CONTROL_PLANE_LABEL = 'openstack-control-plane=enabled' CONTROL_PLANE_LABEL = 'openstack-control-plane=enabled'
COMPUTE_NODE_LABEL = 'openstack-compute-node=enabled' COMPUTE_NODE_LABEL = 'openstack-compute-node=enabled'

View File

@ -16,6 +16,7 @@ from eventlet.green import subprocess
import glob import glob
import grp import grp
import functools import functools
import json
import io import io
import os import os
import pkg_resources import pkg_resources
@ -65,7 +66,6 @@ APPLY_SEARCH_PATTERN = 'Processing Chart,'
CONTAINER_ABNORMAL_EXIT_CODE = 137 CONTAINER_ABNORMAL_EXIT_CODE = 137
DELETE_SEARCH_PATTERN = 'Deleting release|no release to delete' DELETE_SEARCH_PATTERN = 'Deleting release|no release to delete'
ROLLBACK_SEARCH_PATTERN = 'Helm rollback of release' ROLLBACK_SEARCH_PATTERN = 'Helm rollback of release'
INSTALLATION_TIMEOUT = 3600
MAX_DOWNLOAD_THREAD = 5 MAX_DOWNLOAD_THREAD = 5
MAX_DOWNLOAD_ATTEMPTS = 3 MAX_DOWNLOAD_ATTEMPTS = 3
DOWNLOAD_WAIT_BEFORE_RETRY = 15 DOWNLOAD_WAIT_BEFORE_RETRY = 15
@ -821,6 +821,8 @@ class AppOperator(object):
total_count = len(images_to_download) total_count = len(images_to_download)
threads = min(MAX_DOWNLOAD_THREAD, total_count) threads = min(MAX_DOWNLOAD_THREAD, total_count)
self._docker.set_crictl_image_list([])
start = time.time() start = time.time()
try: try:
registries_info = self._docker.retrieve_specified_registries() registries_info = self._docker.retrieve_specified_registries()
@ -1750,7 +1752,7 @@ class AppOperator(object):
lifecycle_hook_info.lifecycle_type = constants.APP_LIFECYCLE_TYPE_FLUXCD_REQUEST lifecycle_hook_info.lifecycle_type = constants.APP_LIFECYCLE_TYPE_FLUXCD_REQUEST
self.app_lifecycle_actions(None, None, app._kube_app, lifecycle_hook_info) self.app_lifecycle_actions(None, None, app._kube_app, lifecycle_hook_info)
try: try:
with Timeout(INSTALLATION_TIMEOUT, with Timeout(constants.APP_INSTALLATION_TIMEOUT,
exception.KubeAppProgressMonitorTimeout()): exception.KubeAppProgressMonitorTimeout()):
rc = self._fluxcd.make_fluxcd_operation(request, app.sync_fluxcd_manifest) rc = self._fluxcd.make_fluxcd_operation(request, app.sync_fluxcd_manifest)
@ -3327,6 +3329,27 @@ class DockerHelper(object):
def __init__(self, dbapi): def __init__(self, dbapi):
self._dbapi = dbapi self._dbapi = dbapi
self._crictl_image_list = []
def _get_crictl_image_list(self):
cmd = ['crictl', 'images', '--output=json']
try:
output = subprocess.check_output( # pylint: disable=not-callable
cmd, stderr=subprocess.STDOUT)
crictl_output = json.loads(output)
except json.JSONDecodeError as e:
LOG.error('Could not parse json output, error=%s', e)
except subprocess.CalledProcessError as e:
LOG.error('Could not list images, error=%s', e)
else:
self._crictl_image_list = []
for img in crictl_output['images']:
self._crictl_image_list.extend(img['repoTags'])
return self._crictl_image_list
def set_crictl_image_list(self, image_list):
self._crictl_image_list = image_list
def _parse_barbican_secret(self, secret_ref): def _parse_barbican_secret(self, secret_ref):
"""Get the registry credentials from the """Get the registry credentials from the
@ -3451,6 +3474,9 @@ class DockerHelper(object):
rc = True rc = True
if not self._crictl_image_list:
self._get_crictl_image_list()
start = time.time() start = time.time()
if img_tag.startswith(constants.DOCKER_REGISTRY_HOST): if img_tag.startswith(constants.DOCKER_REGISTRY_HOST):
try: try:
@ -3458,12 +3484,15 @@ class DockerHelper(object):
LOG.info("User aborted. Skipping download of image %s " % img_tag) LOG.info("User aborted. Skipping download of image %s " % img_tag)
return img_tag, False return img_tag, False
LOG.info("Image %s download started from local registry" % img_tag) if img_tag not in self._crictl_image_list:
client = docker.APIClient(timeout=INSTALLATION_TIMEOUT) LOG.info("Image %s download started from local registry" % img_tag)
local_registry_auth = cutils.get_local_docker_registry_auth() local_registry_auth = cutils.get_local_docker_registry_auth()
auth = '{0}:{1}'.format(local_registry_auth['username'], auth = '{0}:{1}'.format(local_registry_auth['username'],
local_registry_auth['password']) local_registry_auth['password'])
subprocess.check_call(["crictl", "pull", "--creds", auth, img_tag]) # pylint: disable=not-callable subprocess.check_call( # pylint: disable=not-callable
["crictl", "pull", "--creds", auth, img_tag])
else:
LOG.info("Image %s exists in the local registry" % img_tag)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
try: try:
# Pull the image from the public/private registry # Pull the image from the public/private registry
@ -3477,6 +3506,8 @@ class DockerHelper(object):
target_img_tag, registry_auth = \ target_img_tag, registry_auth = \
self._get_img_tag_with_registry(pub_img_tag, registries_info) self._get_img_tag_with_registry(pub_img_tag, registries_info)
client = docker.APIClient(
timeout=constants.APP_INSTALLATION_TIMEOUT)
client.pull(target_img_tag, auth_config=registry_auth) client.pull(target_img_tag, auth_config=registry_auth)
except Exception as e: except Exception as e:
@ -3515,7 +3546,7 @@ class DockerHelper(object):
else: else:
try: try:
LOG.info("Image %s download started from public/private registry" % img_tag) LOG.info("Image %s download started from public/private registry" % img_tag)
client = docker.APIClient(timeout=INSTALLATION_TIMEOUT) client = docker.APIClient(timeout=constants.APP_INSTALLATION_TIMEOUT)
target_img_tag, registry_auth = \ target_img_tag, registry_auth = \
self._get_img_tag_with_registry(img_tag, registries_info) self._get_img_tag_with_registry(img_tag, registries_info)
client.pull(target_img_tag, auth_config=registry_auth) client.pull(target_img_tag, auth_config=registry_auth)