使用registry存储特性同步
将镜像从 registry 中同步到本地目录,使用 registry 存储的特性,将本地目录中的镜像转换成 registry 存储的格式, 这样的好处就是可以去除一些 skopeo dir 中重复的 layers,减少镜像的总大小
此种方式是有些复杂对于大镜像的复制是推荐的, 而对于一些小镜像且显得多余
给定一个镜像列表 images-list.txt,其格式如下:
kubesphere/kube-apiserver:v1.20.6
kubesphere/kube-scheduler:v1.20.6
kubesphere/kube-proxy:v1.20.6
kubesphere/kube-controller-manager:v1.20.6
kubesphere/kube-apiserver:v1.19.8
- convert-images.sh
#!/bin/bash
set -eo pipefail
GREEN_COL="\\033[32;1m"
RED_COL="\\033[1;31m"
NORMAL_COL="\\033[0;39m"
BASE_PATH=$(cd "$(dirname $0)" && pwd)
SOURCE_REGISTRY=$1
IMAGES_DIR=$2
: "${IMAGES_DIR:="images"}"
: "${IMAGES_LIST_FILE:="${BASE_PATH}/images-list.txt"}"
: "${SOURCE_REGISTRY:="docker.io"}"
# 仓库服务器中的目录
BLOBS_PATH="docker/registry/v2/blobs/sha256"
REPO_PATH="docker/registry/v2/repositories"
# 记录当前数和总镜像数
CURRENT_NUM=0
ALL_IMAGES=$(sed -n '/#/d;s/:/:/p' "${IMAGES_LIST_FILE}" | sort -u)
TOTAL_NUMS=$(echo "${ALL_IMAGES}" | wc -l)
# 从远程仓库同步指定镜像到本地目录中
skopeo_sync() {
if skopeo sync \
--insecure-policy --src-tls-verify=false --dest-tls-verify=false \
--override-arch amd64 --override-os linux \
--src docker --dest dir \
"$1" "$2" > /dev/null; then
echo -e "$GREEN_COL Progress: ${CURRENT_NUM}/${TOTAL_NUMS} sync $1 to $2 successful $NORMAL_COL"
else
echo -e "$RED_COL Progress: ${CURRENT_NUM}/${TOTAL_NUMS} sync $1 to $2 failed $NORMAL_COL"
exit 2
fi
}
convert_images() {
rm -rf ${IMAGES_DIR}; mkdir -p ${IMAGES_DIR}
for image in ${ALL_IMAGES}; do
(( CURRENT_NUM="${CURRENT_NUM}"+1 ))
# 取 images-list.txt 文本中的每一行,并分隔存储。
image_name=${image%%:*}
image_tag=${image##*:}
image_repo=${image%%/*}
# 函数调用 从仓库同步镜像到本地 images 目录
skopeo_sync" ${SOURCE_REGISTRY}/${image}" "${IMAGES_DIR}/${image_repo}"
# 在本地 images 目录中,取得 get image manifest sha256sum 信息
manifest="${IMAGES_DIR}/${image}/manifest.json"
# 62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
manifest_sha256=$(sha256sum "${manifest}" | awk '{print $1}')
# docker/registry/v2/blobs/sha256/62/62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732dee
mkdir -p "${BLOBS_PATH}/${manifest_sha256:0:2}/${manifest_sha256}"
# 该 data 文件实际上是镜像的 manifest.json 文件
ln -f "${manifest}" "${BLOBS_PATH}/${manifest_sha256:0:2}/${manifest_sha256}/data"
# make image repositories dir
mkdir -p "${REPO_PATH}/${image_name}/"{_uploads,_layers,_manifests}
mkdir -p "${REPO_PATH}/${image_name}/_manifests/revisions/sha256/${manifest_sha256}"
mkdir -p "${REPO_PATH}/${image_name}/_manifests/tags/${image_tag}/"{current,index/sha256}
mkdir -p "${REPO_PATH}/${image_name}/_manifests/tags/${image_tag}/index/sha256/${manifest_sha256}"
# create image tag manifest link file
# sha256:62ffc2ed7554e4c6d360bce40bbcf196573dd27c4ce080641a2c59867e732deer
echo -n "sha256:${manifest_sha256}" > "${REPO_PATH}/${image_name}/_manifests/revisions/sha256/${manifest_sha256}/link"
echo -n "sha256:${manifest_sha256}" > "${REPO_PATH}/${image_name}/_manifests/tags/${image_tag}/current/link"
echo -n "sha256:${manifest_sha256}" > "${REPO_PATH}/${image_name}/_manifests/tags/${image_tag}/index/sha256/${manifest_sha256}/link"
# link image layers file to registry blobs dir
# 匹配 manifest.json 中"digest"两个不带sha256的值
sed '/v1Compatibility/d' ${manifest} | grep -Eo "\b[a-f0-9]{64}\b" | while IFS= read -r layer ; do
# 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa 、 beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
mkdir -p "${BLOBS_PATH}/${layer:0:2}/${layer}"
# 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa 、 beae173ccac6ad749f76713cf4440fe3d21d1043fe616dfbe30775815d1d0f6a
mkdir -p "${REPO_PATH}/${image_name}/_layers/sha256/${layer}"
# sha256:5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
echo -n "sha256:${layer}" > "${REPO_PATH}/${image_name}/_layers/sha256/${layer}/link"
# 复制images目录中 "application/vnd.docker.container.image.v1+json" 容器配置 config 与 多个 "application/vnd.docker.image.rootfs.diff.tar.gzip" layer
ln -f "${IMAGES_DIR}/${image}/${layer}" "${BLOBS_PATH}/${layer:0:2}/${layer}/data"
done
done
}
convert_images
运行一个 registry 来测试
docker run -itd -p 6000:5000 --name registry-tmp2 -v ${PWD}:/var/lib/registry registry:2
docker pull 192.168.31.100:6000/kubesphere/kube-apiserver:v1.20.6
使用脚本将 registry 存储中的镜像转换成 skopeo dir 的方式,然后再将镜像同步到 registry 中
- install-images.sh
#!/bin/bash
REGISTRY_DOMAIN="192.168.31.100:5000"
REGISTRY_PATH="/var/lib/registry"
REGISTRY_PATH="/code/srliao/mytest/skopeo/test"
# 定义 registry 存储的 blob 目录 和 repositories 目录,方便后面使用
BLOB_DIR="docker/registry/v2/blobs/sha256"
REPO_DIR="docker/registry/v2/repositories"
# 定义生成 skopeo 目录
SKOPEO_DIR="docker/skopeo"
gen_skopeo_dir() {
# 切换到 registry 存储主目录下
[[ -d "${SKOPEO_DIR}" ]] && rm -rf "${SKOPEO_DIR}" && mkdir -p "${SKOPEO_DIR}"
# 通过 find 出 current 文件夹可以得到所有带 tag 的镜像,因为一个 tag 对应一个 current 目录
while IFS= read -r -d '' image; do
# 根据镜像的 tag 提取镜像的名字
name=$(echo "${image}" | awk -F '/' '{print $5"/"$6":"$9}')
link=$(sed 's/sha256://' "${image}/link")
mfs="${BLOB_DIR}/${link:0:2}/${link}/data"
# 创建镜像的硬链接需要的目录
mkdir -p "${SKOPEO_DIR}/${name}"
# 硬链接镜像的 manifests 文件到目录的 manifest 文件
ln "${mfs}" "${SKOPEO_DIR}/${name}/manifest.json"
# 使用正则匹配出所有的 sha256 值,然后排序去重
layers=$(grep -Eo "\b[a-f0-9]{64}\b" "${mfs}" | sort -n | uniq)
for layer in ${layers}; do
# 硬链接 registry 存储目录里的镜像 layer 和 images config 到镜像的 dir 目录
ln "${BLOB_DIR}/${layer:0:2}/${layer}/data" "${SKOPEO_DIR}/${name}/${layer}"
done
done < <(find ${REPO_DIR} -type d -name "current" -print0)
}
sync_image() {
# 使用 skopeo sync 将 dir 格式的镜像同步到 regsitry 存储中
pushd ${SKOPEO_DIR} > /dev/null || exit 1
while IFS= read -r -d '' project; do
skopeo sync \
--insecure-policy --src-tls-verify=false --dest-tls-verify=false \
--src dir --dest docker \
"${project}" "${REGISTRY_DOMAIN}/${project}"
done < <(find ${SKOPEO_DIR} -mindepth 1 -maxdepth 1 -type d -print0)
popd > /dev/null || exit 1
}
pushd ${REGISTRY_PATH} > /dev/null || exit 1
gen_skopeo_dir
sync_image
popd > /dev/null || exit 1
从 registry 存储中 select 出镜像进行同步
先将镜像同步到一个 registry 中,再将镜像从 registry 存储中拿出来,该 registry 可以当作一个镜像存储的池子,使用 Linux 中硬链接的特性将镜像"复制" 一份出来,然后再打一个 tar 包,这样做的好处就是每次打包镜像的时候都能复用历史的镜像数据,而且性能极快
- image-select.sh
#!/bin/bash
set -eo pipefail
# 命令行变量
IMAGES_LIST="$1"
REGISTRY_PATH="$2"
OUTPUT_DIR="$3"
# Registry 仓库数据目录
BLOB_DIR="docker/registry/v2/blobs/sha256"
REPO_DIR="docker/registry/v2/repositories"
# 判断输出目录是否存在如不存在则移除。
[ -d "${OUTPUT_DIR}" ] && rm -rf "${OUTPUT_DIR}" && mkdir -p "${OUTPUT_DIR}"
find "${IMAGES_LIST}" -type f -name "*.list" -exec grep -Ev '^#|^/' {} \; | grep ':' | while IFS= read -r image; do
# 镜像名称和Tag
image_name=${image%%:*}
image_tag=${image##*:}
# link 路径获取
tag_link="${REGISTRY_PATH}/${REPO_DIR}/${image_name}/_manifests/tags/${image_tag}/current/link"
manifest_sha256=$(sed 's/sha256://' "${tag_link}")
manifest="${REGISTRY_PATH}/${BLOB_DIR}/${manifest_sha256:0:2}/${manifest_sha256}/data"
mkdir -p "${OUTPUT_DIR}/${BLOB_DIR}/${manifest_sha256:0:2}/${manifest_sha256}"
# 强制硬链接到指定目录
ln -f "${manifest}" "${OUTPUT_DIR}/${BLOB_DIR}/${manifest_sha256:0:2}/${manifest_sha256}/data"
# make image repositories dir
mkdir -p "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/"{_uploads,_layers,_manifests}
mkdir -p "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/revisions/sha256/${manifest_sha256}"
mkdir -p "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/tags/${image_tag}/{current,index/sha256}"
mkdir -p "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/tags/${image_tag}/index/sha256/${manifest_sha256}"
# create image tag manifest link file
echo -n "sha256:${manifest_sha256}" > "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/tags/${image_tag}/current/link"
echo -n "sha256:${manifest_sha256}" > "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/revisions/sha256/${manifest_sha256}/link"
echo -n "sha256:${manifest_sha256}" > "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_manifests/tags/${image_tag}/index/sha256/${manifest_sha256}/link"
# 强制创建 /docker/registry/v2/blobs/ 各 layer data 文件到指定目录之中
sed '/v1Compatibility/d' "${manifest}" | grep -Eo '\b[a-f0-9]{64}\b' | sort -u | while IFS= read -r layer; do
mkdir -p "${OUTPUT_DIR}/${BLOB_DIR}/${layer:0:2}/${layer}"
mkdir -p "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_layers/sha256/${layer}"
ln -f "${BLOB_DIR}/${layer:0:2}/${layer}/data" "${OUTPUT_DIR}/${BLOB_DIR}/${layer:0:2}/${layer}/data"
echo -n "sha256:${layer}" > "${OUTPUT_DIR}/${REPO_DIR}/${image_name}/_layers/sha256/${layer}/link"
done
done