写点什么

“程”风破浪的开发者|镜像仓库迁移的方法

作者:琦彦
  • 2022-10-21
    中国香港
  • 本文字数:8287 字

    阅读完需:约 1 分钟

“程”风破浪的开发者|镜像仓库迁移的方法

Habor 数据迁移

迁移流程

  • 两个不同的 Harbor 实例迁移数据(含镜像数据和数据库数据)

迁移方式

Skopeo 使用

什么是 Skopeo?

skopeo 使用 API V2 Registry,例如 Docker Registry、Atomic Registry、私有 Registry、本地目录和本地 OCI 镜像目录。skopeo 不需要运行守护进程,它可以执行的操作包括:


  • 通过各种存储机制复制镜像,例如,可以在不需要特权的情况下将镜像从一个 Registry 复制到另一个 Registry

  • 检测远程镜像并查看其属性,包括其镜像层,无需将镜像拉到本地

  • 从镜像库中删除镜像

  • 当存储库需要时,skopeo 可以传递适当的凭据和证书进行身份验证

镜像存储特点

根据 Robin 大佬在 《镜像仓库中镜像存储的原理解析》文章里得出的结论:


  • 通过 Registry API 获得的两个镜像仓库中相同镜像的 manifest 信息完全相同。

  • 两个镜像仓库中相同镜像的 manifest 信息的存储路径和内容完全相同。

  • 两个镜像仓库中相同镜像的 blob 信息的存储路径和内容完全相同

skopeoo 安装

源码编译(静态)

描述: 要构建 skopeo 二进制文件您至少需要 Go 1.12 版本以上, 其次构建 skopeo 有两种方法,即在容器中或者在本地环境中构建(安装环境较为复杂), 此处为了方便演示将采用容器方式进行编译构建。


# 1.拉取skopeo源码到本地$ git clone --depth=1 https://github.com/containers/skopeo.git  # https://github.com/containers/skopeo.git$ cd skopeo$ sed -i 's#proxy.golang.org#https://goproxy.cn#g' skopeo/Makefile
# 2.下载镜像构建依赖$ sudo apt-get install go-md2man # 构建手册依赖于 go-md2man。$ whereis go-md2man # 获得本机中go-md2man路径。
# 3.构建静态二进制文件$ BUILD_IMAGE="golang:latest"$ docker run --name skopeo-build -v $PWD:/src -v /usr/bin/go-md2man:/go/bin/go-md2man -w /src -e CGO_ENABLED=0 -e GOPROXY=https://goproxy.cn,direct ${BUILD_IMAGE} \sh -c 'make BUILDTAGS=containers_image_openpgp GO_DYN_FLAGS=' # CGO_CFLAGS="" CGO_LDFLAGS="" GO111MODULE=on go build -mod=vendor -ldflags '-X main.gitCommit=df4d82b960572c19e9333381a203c0ac475766d7 ' -gcflags "" -tags "containers_image_openpgp" -o bin/skopeo ./cmd/skopeo
# 4.运行编译生成的skopeo可执行文件$ cd ./bin # /opt/software/skopeo/bin$ ./skopeo --help # Various operations with container images and container image registries # ....... # Use "skopeo [command] --help" for more information about a command.
复制代码


构建关键参数解析:


  • CGO_ENABLED=0 : 设置该环境变量, 禁用 CGO 会导致 Go 在可能的情况下更喜欢静态连接库,而不是动态链接到系统库 (解决可以在 Ubuntu 或者其它 linux 发行版中执行编译后二进制文件)。

  • GOPROXY=https://goproxy.cn,direct : Golong 依赖下载镜像站,加快 go get 依赖拉拉取。

  • BUILDTAGS=containers_image_openpgp : 设置该 make 参数消除了对 libgpgme 及其配套库的依赖, Skopeo 的一些特性依赖于非 Go 库,例如 libgpgme 和 libdevmapper。

  • GO_DYN_FLAGS= : 清空该 make 参数 (否则会强制创建动态可执行文件)

2.分发包安装

描述: skopeo 可能已经打包在您的发行版中,此处以 ubuntu 20.04 为例进行安装。

在线方式

# Ubuntu sudo apt-get -y updatesudo apt-get -y install skopeo
# centos 7sudo yum makecachesudo yum -y install skopeo# 或者sudo dnf makecachesudo dnf -y install skopeo
复制代码


CentOS 7 默认下载的 skopeo 是 0.1.40,如果需要使用最新的版本,需要自己通过源码构建

安装 dnf文档

离线方式


# Kubic提供了软件包# Ubuntu 20.04. /etc/os-releaseecho "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -
sudo apt-get updatesudo apt-get -y upgradesudo apt-get -y install skopeo
# centos7 #我们不再为 RHEL 7 发布最新的Skopeo 版本。一些较旧的工件可能会保留下来,例如# https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_7/x86_64/
复制代码

Skopeo centos7 RPM 存档位置

https://centos.pkgs.org/7/centos-extras-x86_64/skopeo-0.1.40-11.el7_8.x86_64.rpm.html

3.容器安装运行

Skopeo 容器镜像可在 quay.io/skopeo/stable:latest 获得, 例如我们采用 podman 命令进行如下操作:


podman run docker://quay.io/skopeo/stable:latest copy --help
复制代码


我们可以使用yumdnf安装skopeo在 CentOS 7 上。在本教程中,我们讨论了这两种方法,但你只需要选择一种方法来安装 skopeo。

skopeoo 命令

描述: skopeoo 是操作各种容器镜像和容器镜像仓库的工具,其使用方法及其可用命令如下:


./skopeo --help    # 子命令可采用如下命令 skopeo [command] --help 命令Usage:  skopeo [flags]  skopeo [command]Available Commands:   copy          # 复制一个镜像从 A 到 B,这里的 A 和 B 可以为本地 docker 镜像或者 registry 上的镜像;  delete        # 删除一个镜像 tag,可以是本地 docker 镜像或者 registry 上的镜像;  help          # 帮助查看  inspect       # 查看一个镜像的 manifest 或者 image config 详细信息;  list-tags     # 列出存储库名称指定的镜像的tag  login           # 登陆某个镜像仓库,类似于 docker login 命令  logout          # 退出某个已认证的镜像仓库, 类似于 docker logout 命令  manifest-digest # 计算文件的清单摘要是一个sha256sum 值  standalone-sign   # 使用本地文件创建签名  standalone-verify # 验证本地文件的签名  sync              # 将一个或多个图像从一个位置同步到另一个位置 (该功能非常Nice)Flags:    --command-timeout duration   # 命令超时时间(单位秒)    --debug                      # 启用debug模式    --insecure-policy            # 在不进行任何策略检查的情况下运行该工具(如果没有配置 policy 的话需要加上该参数)    --override-arch ARCH         # 处理镜像时覆盖客户端 CPU 体系架构,如在 amd64 的机器上用 skopeo 处理 arm64 的镜像    --override-os OS             # 处理镜像时覆盖客户端 OS    --override-variant VARIANT   # 处理镜像时使用VARIANT而不是运行架构变量    --policy string              # 信任策略文件的路径 (为镜像配置安全策略情况下使用)    --registries.d DIR           # 在目录中使用Registry配置文件(例如,用于容器签名存储)    --tmpdir string              # 用于存储临时文件的目录-h, --help                       help for skopeo -v, --version                    Version for Skopeo
复制代码

skopeo 初体验

描述: 在使用体验 skopeo 之前,我们需要了解一哈 Skopeo 可以在哪些镜像和存储库类型上执行镜像操作(官网文档走一波):



温馨提示: 同一个镜像存在的方式有可能不同,不同类型方式存储对镜像的 layer 处理的方式也不一样。

skopeo 批量同步 Harbor 镜像

1. 生成待同步的镜像列表文件

#!/bin/bash#镜像清单文件,将获取到的镜像信息存到该文件中
#check paramif [[ -z $1 || -z $2 ]]; then echo "SOURCE_REGISTRY OR TARGET_REGISTRY is missing" echo "please execute the script in the following format,case: sh list_harbor_image.sh HARBOR_ADDR HARBOR_ADMIN_PASSWORD" echo "Example: sh list_harbor_image.sh 10.0.35.26:5000 Sugon@Harbor123 " exitfi
# Harbor连接地址HARBOR_ADDR=$1# Harbor 管理员用户密码HARBOR_AUTH=admin:$2 #镜像清单文件IMAGES_FILE=harbor-images-list.txt# 显示脚本执行过程,并显示脚本对变量的处理结果。# set -x# 获取所有镜像清单Project_List=$(curl -s -u ${HARBOR_AUTH} -H "Content-Type: application/json" -X GET http://${HARBOR_ADDR}/api/v2.0/projects?page_size=100 | python -m json.tool | grep name | awk '/"name": /' | awk -F '"' '{print $4}')echo -e "【Harbor<${HARBOR_ADDR}>项目列表】: \n ${Project_List}"
# 遍历Harbor项目for Project in $Project_List;do # 某个项目内的镜像列表-页码 PAGE_NUM=1 while true; do Image_Names=$(curl -s -u ${HARBOR_AUTH} -X GET "http://${HARBOR_ADDR}/api/v2.0/projects/$Project/repositories?page_size=100&page=${PAGE_NUM}" | python -m json.tool | grep name | awk '/"name": /' | awk -F '"' '{print $4}') Image_Size=`echo "${Image_Names}" | wc -l` echo -e "【Harbor<${HARBOR_ADDR}>, 项目<${Project}>镜像列表大小<${Image_Size}>, 详情】: \n ${Image_Names}" for Image in $Image_Names;do Image_Tags=$(curl -s -u ${HARBOR_AUTH} -H "Content-Type: application/json" -X GET http://${HARBOR_ADDR}/v2/$Image/tags/list | awk -F '"' '{print $8,$10,$12}')
for Tag in $Image_Tags;do if [ ! -z "${Tag}" ] || [[ ! ${Tag} =~ 400 ]] ||[[ ! ${Tag} =~ 404 ]];then echo "$Image:$Tag" >> ${IMAGES_FILE} fi done done # 当前页码的列表数量<100 if [ "${Image_Size}" -lt "100" ]; then break else let PAGE_NUM=${PAGE_NUM}+1 fi
done
done
复制代码

2. 基于 skopeo copy 机制同步

#!/bin/bashGREEN_COL="\\033[32;1m"RED_COL="\\033[1;31m"NORMAL_COL="\\033[0;39m"
#check paramif [[ -z $1 || -z $2 ]]; then echo " SOURCE_REGISTRY OR TARGET_REGISTRY OR HARBOR_ADMIN_PASSWORD is missing" echo "please execute the script in the following format,case: sh skopeo_transfer_image.sh SOURCE_REGISTRY TARGET_REGISTRY HARBOR_ADMIN_PASSWORD" echo "Example: sh skopeo_transfer_image.sh 10.0.35.26:5000 10.6.6.215:5000 Sugon@Harbor123 " exitfi
# 源Harbor地址SOURCE_REGISTRY=$1# 目标Harbor地址TARGET_REGISTRY=$2# Harbor 管理员用户密码HARBOR_AUTH=admin:$3# 待同步的镜像列表IMAGES_LIST_FILE="harbor-images-list.txt"
set -eo pipefail
CURRENT_NUM=0ALL_IMAGES="$(sed -n '/#/d;s/:/:/p' ${IMAGES_LIST_FILE} | sort -u)"TOTAL_NUMS=$(echo "${ALL_IMAGES}" | wc -l)echo -e "$GREEN_COL Starting 【Image Trasfer】: sync $1 to $2 ,\n 【Image Total Nums】: ${TOTAL_NUMS}, \n 【Image List】:\n ${ALL_IMAGES} "
# 镜像复制方法skopeo_copy() {
# skopeo version 1.4.1 写法 # if skopeo copy --insecure-policy --src-tls-verify=false --dest-tls-verify=false \ # --override-arch amd64 --override-os linux -q docker://$1 docker://$2; 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 # skopeo version 0.1.40 写法 if skopeo copy docker://$1 docker://$2 \ --src-tls-verify=false --dest-tls-verify=false --dest-creds ${HARBOR_AUTH}; 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}# 执行复制for image in ${ALL_IMAGES}; do let CURRENT_NUM=${CURRENT_NUM}+1 skopeo_copy ${SOURCE_REGISTRY}/${image} ${TARGET_REGISTRY}/${image}done
复制代码

3. 一键执行

#!/bin/bash
# 后台执行: nohup sh harbor_transfer.sh 10.0.35.26:5000 Sugon@Harbor123 10.6.6.215:5000 2>&1 &#check paramif [[ -z $1 || -z $2 || -z $3 ]]; then echo " SOURCE_REGISTRY OR TARGET_REGISTRY is missing" echo "please execute the script in the following format,case: sh harbor_transfer.sh SOURCE_REGISTRY HARBOR_ADMIN_PASSWORD TARGET_REGISTRY" echo "Example: sh harbor_transfer.sh 10.0.35.26:5000 Sugon@Harbor123 10.6.6.215:5000 " exitfi
# 源Harbor地址SOURCE_REGISTRY=$1# 目标Harbor管理员用户密码HARBOR_ADMIN_PASSWORD=$2# 目标Harbor地址TARGET_REGISTRY=$3# 1. 导出镜像列表echo -e "【 1/2 Harbor迁移-导出镜像列表】:存放 harbor-images-list.txt"sh -v list_harbor_image.sh ${SOURCE_REGISTRY} ${HARBOR_ADMIN_PASSWORD}
# 2. 迁移镜像echo -e "【 2/2 Harbor迁移-迁移镜像】:遍历 harbor-images-list.txt"sh -v skopeo_transfer_image.sh ${SOURCE_REGISTRY} ${TARGET_REGISTRY} ${HARBOR_ADMIN_PASSWORD}
复制代码

执行效果

一键执行

阿里 image-syncer 使用

下载和安装

releases页面可下载源码以及二进制文件

手动编译

go get github.com/AliyunContainerService/image-syncercd $GOPATH/github.com/AliyunContainerService/image-syncer
# This will create a binary file named image-syncermake
复制代码

使用用例

# 获得帮助信息./image-syncer -h
# 设置配置文件为config.json,默认registry为registry.cn-beijing.aliyuncs.com# 默认namespace为ruohe,并发数为6./image-syncer --proc=6 --auth=./auth.json --images=./images.json --namespace=ruohe \--registry=registry.cn-beijing.aliyuncs.com --retries=3
复制代码

配置文件

在 v1.2.0 版本之后,image-syncer 的配置文件支持 JSON 和 YAML 两种格式,并且支持将原 config 文件替换为一个认证信息文件和一个镜像同步文件。详细的配置文件示例可在目录 example 下找到,旧版本的配置文件格式(auth 和 images 字段放在一起的版本,通过 --config 参数指定)也是兼容的,目录下 config.json 为示例。

认证信息

auth.json 包含了所有仓库的认证信息


{      // 认证字段,其中每个对象为一个registry的一个账号和    // 密码;通常,同步源需要具有pull以及访问tags权限,    // 同步目标需要拥有push以及创建仓库权限,如果没有提供,则默认匿名访问        "quay.io": {    // 支持 "registry" 和 "registry/namespace"(v1.0.3之后的版本) 的形式,需要跟下面images中的registry(registry/namespace)对应                    // images中被匹配到的的url会使用对应账号密码进行镜像同步, 优先匹配 "registry/namespace" 的形式        "username": "xxx",               // 用户名,可选,(v1.3.1 之后支持)valuse 使用 "${env}" 或者 "$env" 类型的字符串可以引用环境变量        "password": "xxxxxxxxx",         // 密码,可选,(v1.3.1 之后支持)valuse 使用 "${env}" 或者 "$env" 类型的字符串可以引用环境变量        "insecure": true                 // registry是否是http服务,如果是,insecure 字段需要为true,默认是false,可选,支持这个选项需要image-syncer版本 > v1.0.1    },    "registry.cn-beijing.aliyuncs.com": {        "username": "xxx",        "password": "xxxxxxxxx"    },    "registry.hub.docker.com": {        "username": "xxx",        "password": "xxxxxxxxxx"    },    "quay.io/coreos": {                               "username": "abc",                      "password": "xxxxxxxxx",        "insecure": true      }}
复制代码

镜像同步文件

{    // 同步镜像规则字段,其中条规则包括一个源仓库(键)和一个目标仓库(值)        // 同步的最大单位是仓库(repo),不支持通过一条规则同步整个namespace以及registry        // 源仓库和目标仓库的格式与docker pull/push命令使用的镜像url类似(registry/namespace/repository:tag)    // 源仓库和目标仓库(如果目标仓库不为空字符串)都至少包含registry/namespace/repository    // 源仓库字段不能为空,如果需要将一个源仓库同步到多个目标仓库需要配置多条规则    // 目标仓库名可以和源仓库名不同(tag也可以不同),此时同步功能类似于:docker pull + docker tag + docker push
"quay.io/coreos/kube-rbac-proxy": "quay.io/ruohe/kube-rbac-proxy", "xxxx":"xxxxx", "xxx/xxx/xx:tag1,tag2,tag3":"xxx/xxx/xx"
// 当源仓库字段中不包含tag时,表示将该仓库所有tag同步到目标仓库,此时目标仓库不能包含tag // 当源仓库字段中包含tag时,表示只同步源仓库中的一个tag到目标仓库,如果目标仓库中不包含tag,则默认使用源tag // 源仓库字段中的tag可以同时包含多个(比如"a/b/c:1,2,3"),tag之间通过","隔开,此时目标仓库不能包含tag,并且默认使用原来的tag // 当目标仓库为空字符串时,会将源镜像同步到默认registry的默认namespace下,并且repo以及tag与源仓库相同,默认registry和默认namespace可以通过命令行参数以及环境变量配置,参考下面的描述}
复制代码

更多参数

image-syncer 的使用比较简单,但同时也支持多个命令行参数的指定:


-h  --help       使用说明,会打印出一些启动参数的当前默认值       --config     设置用户提供的配置文件路径,使用之前需要创建此文件,默认为当前工作目录下的config.json文件。这个参数与 --auth和--images 的                 作用相同,分解成两个参数可以更好地区分认证信息与镜像仓库同步规则。建议使用 --auth 和 --images.
--auth 设置用户提供的认证文件所在路径,使用之前需要创建此认证文件,默认为当前工作目录下的auth.json文件
--images 设置用户提供的镜像同步规则文件所在路径,使用之前需要创建此文件,默认为当前工作目录下的images.json文件
--log 打印出来的log文件路径,默认打印到标准错误输出,如果将日志打印到文件将不会有命令行输出,此时需要通过cat对应的日志文件查看
--namespace 设置默认的目标namespace,当配置文件内一条images规则的目标仓库为空,并且默认registry也不为空时有效,可以通过环境变量DEFAULT_NAMESPACE设置,同时传入命令行参数会优先使用命令行参数值
--registry 设置默认的目标registry,当配置文件内一条images规则的目标仓库为空,并且默认namespace也不为空时有效,可以通过环境变量DEFAULT_REGISTRY设置,同时传入命令行参数会优先使用命令行参数值
--proc 并发数,进行镜像同步的并发goroutine数量,默认为5
--records 指定传输过程中保存已传输完成镜像信息(blob)的文件输出/读取路径,默认输出到当前工作目录,一个records记录了对应目标仓库的已迁移信息,可以用来进行连续的多次迁移(会节约大量时间,但不要把之前自己没执行过的records文件拿来用),如果有unknown blob之类的错误,可以删除该文件重新尝试,image-syncer 在 >= v1.1.0 版本中移除了对于records文件的依赖
--retries 失败同步任务的重试次数,默认为2,重试会在所有任务都被执行一遍之后开始,并且也会重新尝试对应次数生成失败任务的生成。一些偶尔出现的网络错误比如io timeout、TLS handshake timeout,都可以通过设置重试次数来减少失败的任务数量
--os 用来过滤源 tag 的 os 列表,为空则没有任何过滤要求,只对非 docker v2 schema1 media 类型的镜像格式有效
--arch 用来过滤源 tag 的 architecture 列表,为空则没有任何过滤要求
复制代码

参考链接:

https://github.com/containers/skopeo


https://github.com/containers/skopeo/blob/main/docs/skopeo-sync.1.md


https://goharbor.io/docs/2.2.0/administration/configuring-replication/create-replication-endpoints/


https://github.com/AliyunContainerService/image-syncer/blob/master/README-zh_CN.md


发布于: 刚刚阅读数: 5
用户头像

琦彦

关注

孤独的技术没有价值 2019-08-24 加入

还未添加个人简介

评论

发布
暂无评论
“程”风破浪的开发者|镜像仓库迁移的方法_学习方法_琦彦_InfoQ写作社区