Исходный код представленный в этой заметке доступен в моем репозитории GitHub.

Код скрипта для сохранения (save-images.sh):

#!/bin/bash
list="images.txt"
images="images.tar.gz"

usage() {
  echo "USAGE: $0 [--image-list images.txt] [--images images.tar.gz]"
  echo "  [-l|--image-list path] text file with list of images; one image per line."
  echo "  [-i|--images path] tar.gz generated by docker save."
  echo "  [-h|--help] Usage message"
}

POSITIONAL=()
while [[ $# -gt 0 ]]; do
  key="$1"
  case $key in
  -i | --images)
    images="$2"
    shift # past argument
    shift # past value
    ;;
  -l | --image-list)
    list="$2"
    shift # past argument
    shift # past value
    ;;
  -h | --help)
    help="true"
    shift
    ;;
  *)
    usage
    exit 1
    ;;
  esac
done

if [[ $help ]]; then
  usage
  exit 0
fi

pulled=""
while IFS= read -r i; do
  [ -z "${i}" ] && continue
  if docker pull "${i}" >/dev/null 2>&1; then
    echo "Image pull success: ${i}"
    pulled="${pulled} ${i}"
  else
    if docker inspect "${i}" >/dev/null 2>&1; then
      pulled="${pulled} ${i}"
    else
      echo "Image pull failed: ${i}"
    fi
  fi
done <"${list}"

echo "Creating ${images} with $(echo ${pulled} | wc -w | tr -d '[:space:]') images"
docker save $(echo ${pulled}) | gzip --stdout >${images}

Код для загрузки (load-images.sh):

#!/bin/bash
images="images.tar.gz"
list="images.txt"
windows_image_list=""
windows_versions="1809"
usage() {
  echo "USAGE: $0 [--images images.tar.gz] --registry my.registry.com:5000"
  echo "  [-l|--image-list path] text file with list of images; one image per line."
  echo "  [-i|--images path] tar.gz generated by docker save."
  echo "  [-r|--registry registry:port] target private registry:port."
  echo "  [--windows-image-list path] text file with list of images used in Windows. Windows image mirroring is skipped when this is empty"
  echo "  [--windows-versions version] Comma separated Windows versions. e.g., \"1809,2004,20H2\". (Default \"1809\")"
  echo "  [-h|--help] Usage message"
}

push_manifest() {
  export DOCKER_CLI_EXPERIMENTAL=enabled
  manifest_list=()
  for i in "${arch_list[@]}"; do
    manifest_list+=("$1-${i}")
  done

  echo "Preparing manifest $1, list[${arch_list[@]}]"
  docker manifest create "$1" "${manifest_list[@]}" --amend
  docker manifest push "$1" --purge
}

while [[ $# -gt 0 ]]; do
  key="$1"
  case $key in
  -r | --registry)
    reg="$2"
    shift # past argument
    shift # past value
    ;;
  -l | --image-list)
    list="$2"
    shift # past argument
    shift # past value
    ;;
  -i | --images)
    images="$2"
    shift # past argument
    shift # past value
    ;;
  --windows-image-list)
    windows_image_list="$2"
    shift # past argument
    shift # past value
    ;;
  --windows-versions)
    windows_versions="$2"
    shift # past argument
    shift # past value
    ;;
  -h | --help)
    help="true"
    shift
    ;;
  *)
    usage
    exit 1
    ;;
  esac
done
if [[ -z $reg ]]; then
  usage
  exit 1
fi
if [[ $help ]]; then
  usage
  exit 0
fi

docker load --input ${images}

linux_images=()
while IFS= read -r i; do
  [ -z "${i}" ] && continue
  linux_images+=("${i}")
done <"${list}"

arch_list=()
if [[ -n "${windows_image_list}" ]]; then
  IFS=',' read -r -a versions <<<"$windows_versions"
  for version in "${versions[@]}"; do
    arch_list+=("windows-${version}")
  done

  windows_images=()
  while IFS= read -r i; do
    [ -z "${i}" ] && continue
    windows_images+=("${i}")
  done <"${windows_image_list}"

  # use manifest to publish images only used in Windows
  for i in "${windows_images[@]}"; do
    if [[ ! " ${linux_images[@]}" =~ " ${i}" ]]; then
      case $i in
      */*)
        image_name="${reg}/${i}"
        ;;
      *)
        image_name="${reg}/${i}"
        ;;
      esac
      push_manifest "${image_name}"
    fi
  done
fi

arch_list+=("linux-amd64")
for i in "${linux_images[@]}"; do
  [ -z "${i}" ] && continue
  arch_suffix=""
  use_manifest=false
  if [[ (-n "${windows_image_list}") && " ${windows_images[@]}" =~ " ${i}" ]]; then
    # use manifest to publish images when it is used both in Linux and Windows
    use_manifest=true
    arch_suffix="-linux-amd64"
  fi
  case $i in
  */*)
    image_name="${reg}/${i}"
    ;;
  *)
    image_name="${reg}/${i}"
    ;;
  esac

  docker tag "${i}" "${image_name}${arch_suffix}"
  docker push "${image_name}${arch_suffix}"

  if $use_manifest; then
    push_manifest "${image_name}"
  fi
done

Пример списка образов (images.txt):

quay.io/prometheus/prometheus:v2.36.1
quay.io/prometheus/node-exporter:v1.3.1
grafana/grafana:9.0.3

От DevOps

DevOps or not...