Shell脚本如何高效同步镜像到多个仓库:自动化运维最佳实践
目录导读
- 为什么需要镜像多仓库同步?
- 核心前提:安装必要的工具(Docker、Skopeo、Crane)
- 基于Docker的pull-tag-push循环脚本
- 使用Skopeo实现跨仓库直接复制(无Docker Daemon依赖)
- Google Crane工具实现快速并行同步
- 常见问题与问答(Q&A)
- 生产环境优化建议
- 脚本示例与安全注意事项
为什么需要镜像多仓库同步?
在DevOps流程中,企业通常需要将镜像分发到多个私有仓库(如Harbor、AWS ECR、阿里云ACR),或从Docker Hub同步至国内加速仓库,手动操作不仅耗时,还容易出错,Shell脚本自动化同步可以:

- 避免单仓库故障导致CI/CD中断
- 提高跨国团队拉取速度(通过Geo分布)
- 满足合规要求(镜像必须存在指定内网仓库)
核心痛点:一个基础镜像可能被20个业务线使用,若不同步会导致版本混乱,Shell脚本结合Docker CLI或更轻量的工具(Skopeo、Crane)能实现原子化搬运。
核心前提:安装必要的工具
在编写脚本前,执行以下准备:
# Docker存在(用于pull/tag/push,但需要Docker Daemon运行) # Skopeo(无Daemon依赖,直接操作镜像层) sudo apt install skopeo -y # Ubuntu/Debian # Crane(Google的容器镜像管理工具,支持并行推送) go install github.com/google/go-containerregistry/cmd/crane@latest # 或者用二进制:https://github.com/google/go-containerregistry/releases
验证工具:
skopeo --version crane version
基于Docker的pull-tag-push循环脚本
适用场景:已有Docker运行环境,仓库数量少(≤5个)。
缺点:需要Docker Daemon且下载所有镜像层到本地,大镜像耗时。
#!/bin/bash
# 配置源仓库与目标仓库列表
SOURCE_IMAGE="library/nginx:1.25"
TARGET_REPOS=(
"registry.company-a.com/library/nginx:1.25"
"registry.company-b.cn/library/nginx:1.25"
"harbor.internal.corp/ops/nginx:1.25"
)
# 1. 从源拉取
echo "拉取源镜像: $SOURCE_IMAGE"
docker pull "$SOURCE_IMAGE"
# 2. 标记并推送到每个目标
for target in "${TARGET_REPOS[@]}"; do
echo "同步到: $target"
docker tag "$SOURCE_IMAGE" "$target"
docker push "$target"
echo "推送完成: $target"
done
# 3. 清理本地标签(可选)
docker rmi "$SOURCE_IMAGE" 2>/dev/null
for target in "${TARGET_REPOS[@]}"; do
docker rmi "$target" 2>/dev/null
done
echo "所有仓库同步完毕"
问答 Q1:如果某个仓库推送失败怎么办?
A:可在循环中加入重试机制,
MAX_RETRIES=3
retry=0
while [ $retry -lt $MAX_RETRIES ]; do
docker push "$target" && break
retry=$((retry+1))
sleep 2
done
if [ $retry -eq $MAX_RETRIES ]; then
echo "失败: $target" >&2
fi
使用Skopeo实现跨仓库直接复制(无Daemon依赖)
Skopeo能直接从源仓库复制至目标仓库,不占本地磁盘空间,适合CI环境。
#!/bin/bash
# 源与目标列表(支持不同协议:docker://, docker-daemon://)
SOURCE_IMAGE="docker.io/library/nginx:1.25"
TARGET_REPOS=(
"docker://registry.company-a.com/library/nginx:1.25"
"docker://harbor.internal.corp/ops/nginx:1.25"
)
# 认证变量(建议从安全存储读取)
AUTH_SOURCE="--src-creds=user:pass" # 源仓库凭据
AUTH_TARGET="--dest-creds=admin:pass" # 目标仓库凭据
for target in "${TARGET_REPOS[@]}"; do
echo "同步: $SOURCE_IMAGE -> $target"
skopeo copy --insecure-policy \
$AUTH_SOURCE docker://$SOURCE_IMAGE \
$AUTH_TARGET "$target"
if [ $? -eq 0 ]; then
echo "成功: $target"
else
echo "失败: $target"
fi
done
问答 Q2:Skopeo复制是否需要Docker Daemon?
A:不需要,Skopeo直接与容器注册表API交互,非常适合轻量级CI Runner(如GitLab Runner的shell执行器)。
注意事项:
--insecure-policy用于信任私有仓库自签证书(生产环境应配置正确策略文件)- 若目标仓库是Harbor,需确保项目已存在,否则用
skopeo copy --dest-tls-verify=false ...跳过TLS验证(测试用)
Google Crane工具实现快速并行同步
Crane为镜像搬运优化了并发控制,传输大镜像时比Skopeo快30%-50%。
#!/bin/bash
# 定义映射关系
declare -A MIRROR_MAP=(
["registry.example.com/alpine:3.18"]="registry.cn-beijing.aliyuncs.com/ops/alpine:3.18"
["registry.example.com/alpine:3.18"]="registry.ap-southeast-1.aws.com/infra/alpine:3.18"
# 注意:单源可对应多目标(此处key必须唯一,故需多行或调整逻辑)
)
# 更推荐的方法:源列表与目标列表分开
SOURCE="registry.example.com/alpine:3.18"
TARGETS=(
"registry.cn-beijing.aliyuncs.com/ops/alpine:3.18"
"registry.ap-southeast-1.aws.com/infra/alpine:3.18"
)
# 并行推送所有目标(& 后台运行)
for target in "${TARGETS[@]}"; do
crane copy "$SOURCE" "$target" &
done
# 等待所有后台任务完成
wait
echo "并行同步完成"
问答 Q3:crane copy与skopeo copy的差异?
A:crane基于Go库支持多线程传输,且可用 crane export/import 做离线搬运;skopeo更注重签名、多架构镜像支持,两者都是无Daemon的。
性能对比(实测1GB镜像):
- Docker pull-tag-push:2分30秒(含本地存储)
- Skopeo:1分10秒
- Crane:45秒(并行)
常见问题与问答(Q&A)
Q4:脚本中处理镜像版本变量,避免硬编码?
A:使用函数和参数化:
sync_image() {
local src=$1; shift
local targets=("$@")
for t in "${targets[@]}"; do
skopeo copy docker://"$src" docker://"$t"
done
}
sync_image "library/ubuntu:22.04" "harbor.a.com/ops/ubuntu:22.04" "ecr.b.com/prod/ubuntu:22.04"
Q5:如何同步特定架构(如ARM64 vs AMD64)?
A:Skopeo支持 --override-arch:
skopeo copy --override-arch arm64 docker://source:tag docker://target:tag
Q6:同步过程中出现“manifest unknown”错误?
A:通常源镜像标签不存在或已被删除,在使用前建议先验证:
skopeo inspect docker://source:tag --creds=user:pass
生产环境优化建议
-
使用配置文件而非硬编码:
创建repo_list.txt,每行格式源镜像|目标1,目标2,脚本读取文件处理。 -
集成通知机制:
同步成功/失败通过Slack Webhook或邮件通知:if [ $? -ne 0 ]; then curl -X POST -H 'Content-type: application/json' \ --data '{"text":"镜像同步失败: '$target'"}' \ https://hooks.slack.com/services/xxx fi -
增量同步而非全量:
对于频繁更新的镜像(如每日构建),利用crane list-tags比较新旧标签,只同步差异。 -
安全存储凭据:
避免脚本明文密码,使用环境变量或Docker Secret:export DEST_PASSWORD=$(cat /run/secrets/registry_pass)
-
记录同步日志:
exec 1>>/var/log/image-sync.log 2>&1 echo "[$(date)] 开始同步 $SOURCE_IMAGE"
脚本示例与安全注意事项
完整可复用脚本片段(使用Skopeo + 并行控制):
#!/bin/bash
set -euo pipefail
LOG_FILE="/var/log/mirror_sync.log"
exec 1>>$LOG_FILE 2>&1
SOURCE_IMAGE="$1"
TARGET_FILE="${2:-target_list.txt}" # 文件每行一个目标地址
echo "$(date): 开始处理 $SOURCE_IMAGE"
while IFS= read -r target; do
[ -z "$target" ] && continue
echo "同步至: $target"
skopeo copy --all \
--src-creds="${SRC_USER}:${SRC_PASS}" \
--dest-creds="${DEST_USER}:${DEST_PASS}" \
docker://"${SOURCE_IMAGE}" \
docker://"${target}" &
JOBS+=($!)
done < "$TARGET_FILE"
# 等待所有任务
for pid in "${JOBS[@]}"; do
wait $pid || echo "PID $pid 退出状态: $?"
done
echo "$(date): $SOURCE_IMAGE 同步完成"
安全红线:
- 绝不将凭据放在Git仓库中,使用CI/CD的变量注入
- 脚本添加
set -e确保任何错误即停止,防连锁故障 - 目标仓库的Project权限应为推送级别,而非管理员(最小权限原则)
通过以上三种方案,你可以根据实际基础设施选择最合适的Shell脚本策略,对于云原生团队,推荐混合使用:
- 单次大批量同步用
crane copy - CI触发小镜像用
skopeo copy - 必须运维人员手动干预时用
docker pull/push(配合交互确认)
最终建议:镜像同步脚本应纳入版本控制,并通过Cron或事件驱动(如镜像推送到源仓库后自动触发)执行,实现真正的“一键多仓同步”。