From 8fa5ae1865c803b40b249064da82d15ca9b5bac1 Mon Sep 17 00:00:00 2001 From: anders-elastisys <112394389+anders-elastisys@users.noreply.github.com> Date: Sun, 18 Feb 2024 04:34:29 +0100 Subject: [PATCH] bin: improve manage-offline-container-images script (#10857) Fixes bug for retrieving images with tags containing image digests. Script now gets images from jobs and cronjobs as well. New env variable DESTINATION_REGISTRY to push to another registry instead of local registry. New env variable IMAGES_FROM_FILE to pull images listed in a file instead of getting images from a running k8s environment. New env variable REGISTRY_PORT to override port (default is 5000). --- .gitignore | 2 + contrib/offline/README.md | 10 ++- .../manage-offline-container-images.sh | 83 ++++++++++++++----- 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index cf3a4f478..fa68d5606 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ **/vagrant_ansible_inventory *.iml temp +contrib/offline/container-images +contrib/offline/container-images.tar.gz contrib/offline/offline-files contrib/offline/offline-files.tar.gz .idea diff --git a/contrib/offline/README.md b/contrib/offline/README.md index a0e560ec8..a2f3bfe90 100644 --- a/contrib/offline/README.md +++ b/contrib/offline/README.md @@ -5,15 +5,17 @@ Container image collecting script for offline deployment This script has two features: - -(1) Get container images from an environment which is deployed online. - +(1) Get container images from an environment which is deployed online, or set IMAGES_FROM_FILE + environment variable to get images from a file (e.g. temp/images.list after running the + ./generate_list.sh script). (2) Deploy local container registry and register the container images to the registry. Step(1) should be done online site as a preparation, then we bring the gotten images to the target offline environment. if images are from a private registry, you need to set `PRIVATE_REGISTRY` environment variable. -Then we will run step(2) for registering the images to local registry. +Then we will run step(2) for registering the images to local registry, or to an existing +registry set by the `DESTINATION_REGISTRY` environment variable. By default, the local registry +will run on port 5000. This can be changed with the `REGISTRY_PORT` environment variable Step(1) can be operated with: diff --git a/contrib/offline/manage-offline-container-images.sh b/contrib/offline/manage-offline-container-images.sh index 2098eea63..37de5caae 100755 --- a/contrib/offline/manage-offline-container-images.sh +++ b/contrib/offline/manage-offline-container-images.sh @@ -12,11 +12,24 @@ RETRY_COUNT=5 function create_container_image_tar() { set -e - IMAGES=$(kubectl describe pods --all-namespaces | grep " Image:" | awk '{print $2}' | sort | uniq) - # NOTE: etcd and pause cannot be seen as pods. - # The pause image is used for --pod-infra-container-image option of kubelet. - EXT_IMAGES=$(kubectl cluster-info dump | egrep "quay.io/coreos/etcd:|registry.k8s.io/pause:" | sed s@\"@@g) - IMAGES="${IMAGES} ${EXT_IMAGES}" + if [ -z "${IMAGES_FROM_FILE}" ]; then + echo "Getting images from current \"$(kubectl config current-context)\"" + + IMAGES=$(mktemp --suffix=-images) + trap 'rm -f "${IMAGES}"' EXIT + + kubectl describe cronjobs,jobs,pods --all-namespaces | grep " Image:" | awk '{print $2}' | sort | uniq > "${IMAGES}" + # NOTE: etcd and pause cannot be seen as pods. + # The pause image is used for --pod-infra-container-image option of kubelet. + kubectl cluster-info dump | grep -E "quay.io/coreos/etcd:|registry.k8s.io/pause:" | sed s@\"@@g >> "${IMAGES}" + else + echo "Getting images from file \"${IMAGES_FROM_FILE}\"" + if [ ! -f "${IMAGES_FROM_FILE}" ]; then + echo "${IMAGES_FROM_FILE} is not a file" + exit 1 + fi + IMAGES=$(realpath $IMAGES_FROM_FILE) + fi rm -f ${IMAGE_TAR_FILE} rm -rf ${IMAGE_DIR} @@ -26,9 +39,9 @@ function create_container_image_tar() { sudo ${runtime} pull registry:latest sudo ${runtime} save -o registry-latest.tar registry:latest - for image in ${IMAGES} + while read -r image do - FILE_NAME="$(echo ${image} | sed s@"/"@"-"@g | sed s/":"/"-"/g)".tar + FILE_NAME="$(echo ${image} | sed s@"/"@"-"@g | sed s/":"/"-"/g | sed -E 's/\@.*//g')".tar set +e for step in $(seq 1 ${RETRY_COUNT}) do @@ -48,18 +61,20 @@ function create_container_image_tar() { # so that these parts will be replaced with Kubespray. # - kube_image_repo: "registry.k8s.io" # - gcr_image_repo: "gcr.io" + # - ghcr_image_repo: "ghcr.io" # - docker_image_repo: "docker.io" # - quay_image_repo: "quay.io" FIRST_PART=$(echo ${image} | awk -F"/" '{print $1}') if [ "${FIRST_PART}" = "registry.k8s.io" ] || [ "${FIRST_PART}" = "gcr.io" ] || + [ "${FIRST_PART}" = "ghcr.io" ] || [ "${FIRST_PART}" = "docker.io" ] || [ "${FIRST_PART}" = "quay.io" ] || [ "${FIRST_PART}" = "${PRIVATE_REGISTRY}" ]; then - image=$(echo ${image} | sed s@"${FIRST_PART}/"@@) + image=$(echo ${image} | sed s@"${FIRST_PART}/"@@ | sed -E 's/\@.*/\n/g') fi echo "${FILE_NAME} ${image}" >> ${IMAGE_LIST} - done + done < "${IMAGES}" cd .. sudo chown ${USER} ${IMAGE_DIR}/* @@ -72,6 +87,16 @@ function create_container_image_tar() { } function register_container_images() { + create_registry=false + REGISTRY_PORT=${REGISTRY_PORT:-"5000"} + + if [ -z "${DESTINATION_REGISTRY}" ]; then + echo "DESTINATION_REGISTRY not set, will create local registry" + create_registry=true + DESTINATION_REGISTRY="$(hostname):${REGISTRY_PORT}" + fi + echo "Images will be pushed to ${DESTINATION_REGISTRY}" + if [ ! -f ${IMAGE_TAR_FILE} ]; then echo "${IMAGE_TAR_FILE} should exist." exit 1 @@ -81,18 +106,17 @@ function register_container_images() { fi # To avoid "http: server gave http response to https client" error. - LOCALHOST_NAME=$(hostname) if [ -d /etc/docker/ ]; then set -e # Ubuntu18.04, RHEL7/CentOS7 cp ${CURRENT_DIR}/docker-daemon.json ${TEMP_DIR}/docker-daemon.json - sed -i s@"HOSTNAME"@"${LOCALHOST_NAME}"@ ${TEMP_DIR}/docker-daemon.json + sed -i s@"HOSTNAME"@"$(hostname)"@ ${TEMP_DIR}/docker-daemon.json sudo cp ${TEMP_DIR}/docker-daemon.json /etc/docker/daemon.json elif [ -d /etc/containers/ ]; then set -e # RHEL8/CentOS8 cp ${CURRENT_DIR}/registries.conf ${TEMP_DIR}/registries.conf - sed -i s@"HOSTNAME"@"${LOCALHOST_NAME}"@ ${TEMP_DIR}/registries.conf + sed -i s@"HOSTNAME"@"$(hostname)"@ ${TEMP_DIR}/registries.conf sudo cp ${TEMP_DIR}/registries.conf /etc/containers/registries.conf else echo "runtime package(docker-ce, podman, nerctl, etc.) should be installed" @@ -100,19 +124,28 @@ function register_container_images() { fi tar -zxvf ${IMAGE_TAR_FILE} - sudo ${runtime} load -i ${IMAGE_DIR}/registry-latest.tar - set +e - sudo ${runtime} container inspect registry >/dev/null 2>&1 - if [ $? -ne 0 ]; then - sudo ${runtime} run --restart=always -d -p 5000:5000 --name registry registry:latest + + if [ "${create_registry}" ]; then + sudo ${runtime} load -i ${IMAGE_DIR}/registry-latest.tar + set +e + + sudo ${runtime} container inspect registry >/dev/null 2>&1 + if [ $? -ne 0 ]; then + sudo ${runtime} run --restart=always -d -p "${REGISTRY_PORT}":"${REGISTRY_PORT}" --name registry registry:latest + fi + set -e fi - set -e while read -r line; do file_name=$(echo ${line} | awk '{print $1}') raw_image=$(echo ${line} | awk '{print $2}') - new_image="${LOCALHOST_NAME}:5000/${raw_image}" - org_image=$(sudo ${runtime} load -i ${IMAGE_DIR}/${file_name} | head -n1 | awk '{print $3}') + new_image="${DESTINATION_REGISTRY}/${raw_image}" + load_image=$(sudo ${runtime} load -i ${IMAGE_DIR}/${file_name} | head -n1) + org_image=$(echo "${load_image}" | awk '{print $3}') + # special case for tags containing the digest when using docker or podman as the container runtime + if [ "${org_image}" == "ID:" ]; then + org_image=$(echo "${load_image}" | awk '{print $4}') + fi image_id=$(sudo ${runtime} image inspect ${org_image} | grep "\"Id\":" | awk -F: '{print $3}'| sed s/'\",'//) if [ -z "${file_name}" ]; then echo "Failed to get file_name for line ${line}" @@ -136,7 +169,7 @@ function register_container_images() { done <<< "$(cat ${IMAGE_LIST})" echo "Succeeded to register container images to local registry." - echo "Please specify ${LOCALHOST_NAME}:5000 for the following options in your inventry:" + echo "Please specify \"${DESTINATION_REGISTRY}\" for the following options in your inventry:" echo "- kube_image_repo" echo "- gcr_image_repo" echo "- docker_image_repo" @@ -161,13 +194,17 @@ elif [ "${OPTION}" == "register" ]; then register_container_images else echo "This script has two features:" - echo "(1) Get container images from an environment which is deployed online." + echo "(1) Get container images from an environment which is deployed online, or set IMAGES_FROM_FILE" + echo " environment variable to get images from a file (e.g. temp/images.list after running the" + echo " ./generate_list.sh script)." echo "(2) Deploy local container registry and register the container images to the registry." echo "" echo "Step(1) should be done online site as a preparation, then we bring" echo "the gotten images to the target offline environment. if images are from" echo "a private registry, you need to set PRIVATE_REGISTRY environment variable." - echo "Then we will run step(2) for registering the images to local registry." + echo "Then we will run step(2) for registering the images to local registry, or to an existing" + echo "registry set by the DESTINATION_REGISTRY environment variable. By default, the local registry" + echo "will run on port 5000. This can be changed with the REGISTRY_PORT environment variable" echo "" echo "${IMAGE_TAR_FILE} is created to contain your container images." echo "Please keep this file and bring it to your offline environment."