






















在国内网络环境下做 Docker 开发和运维,很多人都踩过同一个坑:
docker pull 拉国外镜像,卡在几十 KB/spull/tag/push很多团队的第一反应是:
配 Docker 镜像加速器、手动拉取后再推送到 Harbor、写 Shell 循环同步。这些方法不能说错,但都不够优雅,也不够稳。
这篇文章我想讲清楚一个非常实用、但国内很多开发和运维还没真正用起来的工具:Skopeo。
它特别适合解决这类问题:
一句话概括:
Skopeo 的核心价值,就是让你“跳过本地 Docker 守护进程”,直接在镜像源和镜像目标之间做检查、迁移和同步。
这对国内网络环境尤其有用。
先说结论:
很多慢,不是你的机器慢,而是你的链路和使用方式本身就低效。
很多团队平时迁移镜像是这么干的:
docker pull nginx:1.27
docker tag nginx:1.27 harbor.company.local/base/nginx:1.27
docker push harbor.company.local/base/nginx:1.27
看起来很正常,但本质上你走的是这条链路:
flowchart LR A[远程镜像仓库 Docker Hub] --> B[本机 docker daemon] B --> C[本地磁盘解包/缓存] C --> B B --> D[内网 Harbor / 私有仓库]
这条链路的问题很明显:
对于“只是想把镜像从 A 仓库搬到 B 仓库”的场景,这个流程其实绕远了。
Skopeo 是一个容器镜像操作工具,主要能力包括:
它最关键的两个特点是:
也就是说,很多操作不需要:
这就是它最值钱的地方。
比如你可以直接把 Docker Hub 的镜像复制到 Harbor:
skopeo copy docker://docker.io/library/nginx:1.27 docker://harbor.company.local/library/nginx:1.27
注意,这里不是:
而是直接 copy。
这条链路会变成:
flowchart LR A[远程仓库 Docker Hub] --> B[Skopeo] B --> C[目标仓库 Harbor]
中间少了本地 Docker Daemon,也通常少了很多无意义的本地存储开销。
围绕“国内网络环境下的 Docker 镜像迁移与加速”,Skopeo 最常见的几个落地场景如下。
比如你的 Kubernetes 集群只能拉 Harbor,不能直接访问 Docker Hub。
那你要做的不是让每台机器都科学拉镜像,而是:
在一台能访问外网的中转机上,用 Skopeo 把镜像同步到 Harbor。
很多公司会有统一基础镜像:
nginxredismysqlopenjdkalpinebusyboxnodepython可以定期同步到企业仓库,所有开发和集群统一从内网拉,速度稳定得多。
如果生产环境完全离线,可以这样处理:
skopeo copy 导出为 docker-archivedocker pull/tag/push如果你有几十上百个镜像版本要同步,Skopeo 的 sync 会比手工脚本更清晰。
官方支持很多发行版,安装很简单。
sudo apt-get update
sudo apt-get install -y skopeo
sudo yum install -y skopeo
或:
sudo dnf install -y skopeo
brew install skopeo
skopeo --version
如果你是第一次接触 Skopeo,这几个前缀一定要先认清。
docker://表示远程镜像仓库中的镜像。
例如:
docker://docker.io/library/nginx:1.27
docker://harbor.company.local/base/redis:7.2
docker-archive:表示 docker save 格式的 tar 文件。
例如:
docker-archive:/tmp/nginx.tar
dir:表示一个本地目录格式,适合调试和查看层文件。
oci:表示 OCI 镜像布局目录。
下面直接上最有用的部分。
这是最推荐的方式。
比如 Harbor:
skopeo login harbor.company.local
输入账号密码即可。
skopeo copy \
docker://docker.io/library/nginx:1.27 \
docker://harbor.company.local/library/nginx:1.27
skopeo copy \
docker://docker.io/library/nginx:1.27 \
docker://harbor.company.local/library/nginx:1.27
skopeo copy:复制镜像docker pull# 传统方式:依赖 docker daemon,本地要落盘
docker pull nginx:1.27
docker tag nginx:1.27 harbor.company.local/library/nginx:1.27
docker push harbor.company.local/library/nginx:1.27
# 推荐方式:直接仓库到仓库复制
skopeo copy \
docker://docker.io/library/nginx:1.27 \
docker://harbor.company.local/library/nginx:1.27
⚠️ 避坑点:目标仓库项目要提前存在
很多 Harbor 配置下,项目不存在时不会自动创建,直接报错。
如果你的生产环境完全无法联网,这种方式最实用。
skopeo copy \
docker://docker.io/library/redis:7.2 \
docker-archive:/tmp/redis_7.2.tar:redis:7.2
这里的目标:
docker-archive:/tmp/redis_7.2.tar:redis:7.2
含义是:
docker save 兼容格式 tar 包/tmp/redis_7.2.tarredis:7.2docker load -i /tmp/redis_7.2.tar
docker tag redis:7.2 harbor.company.local/base/redis:7.2
docker push harbor.company.local/base/redis:7.2
Skopeo 只能在联网仓库之间复制,离线没法用。
Skopeo 支持:
它并不只是“在线复制工具”,它也是一个镜像搬运格式转换工具。
当你不是同步一个镜像,而是一批镜像时,copy 逐条执行会很烦。
这时候可以考虑:
skopeo sync先准备镜像列表:
docker.io/library/nginx:1.27
docker.io/library/redis:7.2
docker.io/library/alpine:3.20
docker.io/library/busybox:1.36
然后执行脚本:
#!/usr/bin/env bash
set -euo pipefail
# 私有仓库地址
TARGET_REGISTRY="harbor.company.local/base"
# 镜像列表文件
IMAGE_LIST="images.txt"
while read -r image; do
# 跳过空行
[[ -z "$image" ]] && continue
# 取出最后一段作为目标名,例如 nginx:1.27
name_with_tag="${image##*/}"
echo ">>> 正在同步: $image -> $TARGET_REGISTRY/$name_with_tag"
skopeo copy \
"docker://$image" \
"docker://$TARGET_REGISTRY/$name_with_tag"
done < "$IMAGE_LIST"
echo ">>> 全部同步完成"
#!/usr/bin/env bash
set -euo pipefail
-e:任一命令失败立即退出-u:使用未定义变量时报错-o pipefail:管道中任一命令失败都能感知⚠️ 避坑点:不要写成“失败继续但最终显示成功”
很多人写批量迁移脚本时,最后打印“同步完成”,结果中间已经失败好几个镜像了。
skopeo sync 更适合做镜像仓库同步。
例如:
skopeo sync --src docker --dest dir registry.example.com/busybox /tmp/busybox-sync
这条命令含义是:
如果你要做更完整的离线同步,可以先同步到目录,再转运,再推入内网仓库。
skopeo inspect 先看清楚这是我非常建议养成的习惯。
很多时候镜像拉不下来、架构不对、标签不对,不是网络问题,而是你拿错镜像了。
skopeo inspect docker://docker.io/library/nginx:1.27
Digest:镜像摘要,判断版本是否一致Architecture:架构,如 amd64 / arm64Os:系统Layers:镜像层Env:环境变量RepoTags:仓库所有标签skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Digest'
skopeo inspect --config docker://docker.io/library/nginx:1.27 | jq
这在排查以下问题时非常有用:
⚠️ 避坑点:标签相同,不代表 digest 相同
latest 这种标签最容易坑人。
真正稳定的是 digest,不是 tag。
很多人看到“加速”两个字,第一反应是找“加速地址”。
这不完全错,但如果你从工程实践角度看,真正稳定的加速方案是分层的。
最靠谱的做法不是让每台机器都直连外网,而是:
架构如下:
flowchart LR A[Docker Hub / Quay / GCR] --> B[同步机 Skopeo] B --> C[企业 Harbor / 私有仓库] C --> D[开发机] C --> E[CI/CD Runner] C --> F[Kubernetes 节点]
这套方案比“每台机器自己配加速源”稳定太多。
建议企业维护一份基础镜像清单,比如:
nginxredismysqlpostgresopenjdkeclipse-temurinnodepythongolangalpinebusybox每天或每周定时同步指定版本。这样:
这比大家各自 docker pull 要专业得多。
现在 ARM 机器越来越多,尤其是:
所以你迁移镜像时,不能只看 tag,还要看架构。
skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Architecture, .Os'
同一个标签可能对应:
如果你目标环境是 ARM,但你同步的只是 AMD64 镜像,最后运行就会报:
exec format error⚠️ 避坑点:镜像能 pull,不代表能 run
很多问题不是出在仓库,也不是出在网络,而是架构不匹配。
私有仓库几乎都需要认证。
skopeo login harbor.company.local
skopeo inspect \
--creds='username:password' \
docker://harbor.company.local/base/nginx:1.27
或者 copy 时分别指定源和目标凭据:
skopeo copy \
--src-creds='src_user:src_pass' \
--dest-creds='dst_user:dst_pass' \
docker://docker.io/library/nginx:1.27 \
docker://harbor.company.local/base/nginx:1.27
⚠️ 避坑点:不要把明文密码硬编码进 Git 仓库脚本
更好的做法:
skopeo login 预先登录下面给一个更接近生产可用的版本。
docker.io/library/nginx:1.27
docker.io/library/redis:7.2
docker.io/library/alpine:3.20
quay.io/prometheus/prometheus:v2.53.0
#!/usr/bin/env bash
set -euo pipefail
# ============================================
# 国内网络环境下的 Docker 镜像迁移与加速脚本
# 功能:
# 1. 从公网仓库读取镜像
# 2. 使用 skopeo 直接复制到 Harbor
# 3. 支持失败退出,避免“部分成功却误判整体成功”
# ============================================
# Harbor 仓库前缀
TARGET_REGISTRY="harbor.company.local/mirror"
# 镜像清单文件
IMAGE_LIST_FILE="./images.txt"
# 检查 skopeo 是否安装
if ! command -v skopeo >/dev/null 2>&1; then
echo "错误:未安装 skopeo,请先安装"
exit 1
fi
# 检查镜像文件是否存在
if [[ ! -f "$IMAGE_LIST_FILE" ]]; then
echo "错误:镜像清单文件不存在: $IMAGE_LIST_FILE"
exit 1
fi
# 开始循环处理每个镜像
while IFS= read -r image; do
# 去掉空行和注释行
[[ -z "$image" ]] && continue
[[ "$image" =~ ^# ]] && continue
# 取出镜像名+tag,例如 nginx:1.27
image_name_tag="${image##*/}"
# 构造目标镜像路径
target_image="${TARGET_REGISTRY}/${image_name_tag}"
echo "========================================"
echo "开始同步镜像"
echo "源镜像: docker://${image}"
echo "目标镜像: docker://${target_image}"
echo "========================================"
# 执行复制
skopeo copy \
"docker://${image}" \
"docker://${target_image}"
echo "同步成功: ${image} -> ${target_image}"
echo
done < "$IMAGE_LIST_FILE"
echo "全部镜像同步完成"
chmod +x sync-images.sh
./sync-images.sh
这一段最适合拿来给团队统一规范。
docker pull docker.io/library/nginx:1.27
docker tag docker.io/library/nginx:1.27 harbor.company.local/base/nginx:1.27
docker push harbor.company.local/base/nginx:1.27
docker rmi docker.io/library/nginx:1.27
docker rmi harbor.company.local/base/nginx:1.27
skopeo copy \
docker://docker.io/library/nginx:1.27 \
docker://harbor.company.local/base/nginx:1.27
# 只看 tag,觉得 latest 一定是对的
skopeo copy docker://docker.io/library/nginx:latest docker://harbor.company.local/base/nginx:latest
这里我按实战经验给一个排查顺序。
skopeo inspect docker://docker.io/library/nginx:1.27
如果 inspect 都失败,那大概率是:
skopeo login harbor.company.local
然后再 copy。
常见报错本质上就是:
Docker Hub 官方镜像很多人容易少写 library。
docker://docker.io/nginx:1.27
docker://docker.io/library/nginx:1.27
⚠️ 避坑点:不同 Registry 的命名规范不完全一样
不要把 Docker CLI 的“省略写法”直接等价到所有工具中。
skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Architecture'
如果目标机器是 ARM,而你同步的是 AMD64,后面容器运行就会出问题。
国内环境下,最真实的问题往往不是命令写错,而是:
这时候你应该把“镜像同步”从每个节点收口到统一同步机,而不是让每台机器都自己去碰运气。
这个问题很多人会问。
我的建议很简单:
docker build / run / exec / logs一句话:
Docker 更像“容器运行工具”,Skopeo 更像“镜像搬运工具”。
如果你所在团队经常遇到国内拉镜像慢的问题,我建议直接落地这套方案:
crontab -e
加入:
0 2 * * * /opt/scripts/sync-images.sh >> /var/log/sync-images.log 2>&1
这表示每天凌晨 2 点同步一次。
你不要只回答“配镜像加速器”。
更完整、更工程化的回答应该是:
skopeo copy 导出 docker-archive 再离线导入skopeo inspect 检查镜像 digest、架构、标签,避免拉错镜像latest 带来的不可控变更这类回答会明显比“用代理”更像真正做过事的人。
把这篇文章的核心内容收一下:
docker pull/tag/push 方式能用,但效率不高skopeo inspect 很适合在拉取前确认 digest、架构和标签用一台网络条件好的同步机 + Skopeo + Harbor,构建企业内部镜像缓存和迁移通道。
这套方案不花哨,但很稳,非常适合国内网络环境下的 Docker 镜像迁移与加速。
Skopeo GitHub
https://github.com/containers/skopeo
Skopeo 安装文档
https://github.com/containers/skopeo/blob/main/install.md
Skopeo copy 命令说明
https://github.com/containers/skopeo/blob/main/docs/skopeo-copy.1.md
Skopeo inspect 命令说明
https://github.com/containers/skopeo/blob/main/docs/skopeo-inspect.1.md
Skopeo sync 命令说明
https://github.com/containers/skopeo/blob/main/docs/skopeo-sync.1.md
扫码关注不迷路

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。