Добрый день. Возможно, кому-то будет полезна данная информация. Мне в рамках некоторых рабочих задач понадобилось сохранить порядка 50 образов Docker и затем загрузить их в Docker на другом сервере.
За исходным кодом прошу под кат.
Код скрипта для сохранения образов (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
Инструкция по использованию:
Делаем файл save-images.sh исполняемым
chmod +x ./save-images.sh
И запускаем его
./save-images.sh --image-list ./images.txt
На выходе получаем архив images.tar.gz содержащий образы из списка. Для загрузки образов выполняем похожие операции
chmod +x load-images.sh
И загружаем образы из архива
./load-images.sh --image-list ./images.txt --registry <АДРЕС_ЛОКАЛЬНОГО_REGISTRY:ПОРТ>
Весь код доступен на GitHub: https://github.com/ZeroBot-Dot/docker-save-load-multiple-images
Комментарии (7)
Zhbert
11.10.2022 21:12+2Идея классная, спасибо, но было бы полезнее, если бы ты расписал подробнее, что твой скрипт делает пошагово, и как ты добился результата.
ZeroBot-Dot Автор
11.10.2022 21:33Могу попробовать завтра расписать в README репозитория. Напишу как сделаю.
funca
12.10.2022 01:01+1Этот способ поддерживает дедупликацию данных при сохранении в архив?
AlexGluck
12.10.2022 11:23+1Нет, дедупликацию не поддерживает. Для таких случаев наверное надо реджистри брать и внутри него будет дедупликация слоёв. Запустил свой реджистри, хранение указал в определенную папку. Скачал образ реджистри отдельно, в тар поместил скрипт, папку со слоями и образ реджистри. Принёс куда надо архивчик, распаковал и скриптом поднял реджистри из которого уже образы перекидываешь куда надо.
ebogdanov
Интересно, спасибо.
У меня задача была более широкая и я как ленивый решил использовать skopeo https://github.com/containers/skopeo :)
ZeroBot-Dot Автор
Спасибо. Обязательно ознакомлюсь с вашим вариантом.
AlexGluck
У меня задача была как у автора, но я тоже решил не страдать и воспользоваться скопео.