国内网络环境下的 Docker 镜像迁移与加速实战:用 Skopeo 替代 docker pull + docker tag + docker push

在国内网络环境下做 Docker 开发和运维,很多人都踩过同一个坑:

  • docker pull 拉国外镜像,卡在几十 KB/s
  • CI/CD 构建时偶发超时,流水线一会儿红一会儿绿
  • 生产环境不允许直接访问 Docker Hub,只能走内网仓库
  • 想把一批镜像迁到私有仓库,结果只能一台机器一条条 pull/tag/push
  • 离线环境、内外网隔离环境下,镜像同步又麻烦又慢

很多团队的第一反应是:
配 Docker 镜像加速器手动拉取后再推送到 Harbor写 Shell 循环同步。这些方法不能说错,但都不够优雅,也不够稳。

这篇文章我想讲清楚一个非常实用、但国内很多开发和运维还没真正用起来的工具:Skopeo

它特别适合解决这类问题:

  • 国内网络环境下的 Docker 镜像迁移与加速
  • Docker Hub / Quay / GCR 镜像同步到私有仓库
  • 不落地镜像直接在仓库之间拷贝
  • 离线环境镜像导出、导入、批量同步
  • 无需 Docker Daemon 的镜像检查与复制

一句话概括:

Skopeo 的核心价值,就是让你"跳过本地 Docker 守护进程",直接在镜像源和镜像目标之间做检查、迁移和同步。

这对国内网络环境尤其有用。


一、为什么国内网络环境下,Docker 镜像迁移与加速这么痛?

先说结论:
很多慢,不是你的机器慢,而是你的链路和使用方式本身就低效。

1.1 常见低效链路

很多团队平时迁移镜像是这么干的:

bash 复制代码
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 / 私有仓库]

这条链路的问题很明显:

  1. 必须先完整拉到本地
  2. 依赖 Docker Daemon
  3. 占本地磁盘
  4. 批量同步时非常慢
  5. CI 机器要有 Docker 权限
  6. 多架构镜像、manifest list 处理不直观

对于"只是想把镜像从 A 仓库搬到 B 仓库"的场景,这个流程其实绕远了。


二、Skopeo 是什么?为什么它适合国内 Docker 镜像迁移与加速?

Skopeo 是一个容器镜像操作工具,主要能力包括:

  • 查看远程镜像信息
  • 在不同镜像存储之间直接复制镜像
  • 删除仓库中的镜像
  • 同步整个镜像仓库
  • 支持 Docker Registry、OCI、本地目录、docker-archive 等多种格式

它最关键的两个特点是:

2.1 不依赖 Docker Daemon

也就是说,很多操作不需要:

  • 启动 Docker
  • 有 root 权限
  • 把镜像先拉到本地

2.2 可以直接在仓库之间复制镜像

这就是它最值钱的地方。

比如你可以直接把 Docker Hub 的镜像复制到 Harbor:

bash 复制代码
skopeo copy docker://docker.io/library/nginx:1.27 docker://harbor.company.local/library/nginx:1.27

注意,这里不是:

  • 先 pull
  • 再 tag
  • 再 push

而是直接 copy

这条链路会变成:
flowchart LR A[远程仓库 Docker Hub] --> B[Skopeo] B --> C[目标仓库 Harbor]

中间少了本地 Docker Daemon,也通常少了很多无意义的本地存储开销。


三、Skopeo 能解决哪些典型问题?

围绕"国内网络环境下的 Docker 镜像迁移与加速",Skopeo 最常见的几个落地场景如下。

3.1 场景一:把公网镜像迁移到内网 Harbor

比如你的 Kubernetes 集群只能拉 Harbor,不能直接访问 Docker Hub。

那你要做的不是让每台机器都科学拉镜像,而是:

在一台能访问外网的中转机上,用 Skopeo 把镜像同步到 Harbor。

3.2 场景二:做企业内部镜像缓存仓库

很多公司会有统一基础镜像:

  • nginx
  • redis
  • mysql
  • openjdk
  • alpine
  • busybox
  • node
  • python

可以定期同步到企业仓库,所有开发和集群统一从内网拉,速度稳定得多。

3.3 场景三:离线环境镜像打包与迁移

如果生产环境完全离线,可以这样处理:

  • 外网机器用 skopeo copy 导出为 docker-archive
  • U 盘/介质带到内网
  • 内网再导入目标仓库或 Docker

3.4 场景四:批量同步镜像,替代手写脚本 docker pull/tag/push

如果你有几十上百个镜像版本要同步,Skopeo 的 sync 会比手工脚本更清晰。


四、先安装 Skopeo

官方支持很多发行版,安装很简单。

4.1 Ubuntu / Debian

bash 复制代码
sudo apt-get update
sudo apt-get install -y skopeo

4.2 CentOS / RHEL / Rocky / AlmaLinux

bash 复制代码
sudo yum install -y skopeo

或:

bash 复制代码
sudo dnf install -y skopeo

4.3 macOS

bash 复制代码
brew install skopeo

4.4 验证安装

bash 复制代码
skopeo --version

五、先理解 Skopeo 最重要的几个概念

如果你是第一次接触 Skopeo,这几个前缀一定要先认清。

5.1 docker://

表示远程镜像仓库中的镜像。

例如:

bash 复制代码
docker://docker.io/library/nginx:1.27
docker://harbor.company.local/base/redis:7.2

5.2 docker-archive:

表示 docker save 格式的 tar 文件。

例如:

bash 复制代码
docker-archive:/tmp/nginx.tar

5.3 dir:

表示一个本地目录格式,适合调试和查看层文件。

5.4 oci:

表示 OCI 镜像布局目录。


六、国内网络环境下 Docker 镜像迁移与加速的 4 种实战方式

下面直接上最有用的部分。


6.1 方式一:直接从公网仓库复制到私有仓库

这是最推荐的方式。

6.1.1 登录目标仓库

比如 Harbor:

bash 复制代码
skopeo login harbor.company.local

输入账号密码即可。

6.1.2 复制镜像

bash 复制代码
skopeo copy \
  docker://docker.io/library/nginx:1.27 \
  docker://harbor.company.local/library/nginx:1.27

命令解释

bash 复制代码
skopeo copy \
  docker://docker.io/library/nginx:1.27 \
  docker://harbor.company.local/library/nginx:1.27
  • skopeo copy:复制镜像
  • 第一个地址:源镜像
  • 第二个地址:目标镜像
  • 中间不需要先 docker pull

对比:错误写法 vs 更优写法

传统写法

bash 复制代码
# 传统方式:依赖 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

更优写法

bash 复制代码
# 推荐方式:直接仓库到仓库复制
skopeo copy \
  docker://docker.io/library/nginx:1.27 \
  docker://harbor.company.local/library/nginx:1.27

为什么更优?

  • 少一次本地落盘
  • 不依赖 Docker Daemon
  • 更适合批量同步
  • 更适合 CI/CD 中转机
  • 更适合受限服务器环境

⚠️ 避坑点:目标仓库项目要提前存在

很多 Harbor 配置下,项目不存在时不会自动创建,直接报错。


6.2 方式二:先导出为 tar,再带到离线环境

如果你的生产环境完全无法联网,这种方式最实用。

6.2.1 从公网仓库导出为 Docker Archive

bash 复制代码
skopeo copy \
  docker://docker.io/library/redis:7.2 \
  docker-archive:/tmp/redis_7.2.tar:redis:7.2

解释

这里的目标:

bash 复制代码
docker-archive:/tmp/redis_7.2.tar:redis:7.2

含义是:

  • 导出成 docker save 兼容格式 tar 包
  • 文件路径:/tmp/redis_7.2.tar
  • 镜像名标签:redis:7.2

6.2.2 在离线机器中导入 Docker

bash 复制代码
docker load -i /tmp/redis_7.2.tar

6.2.3 再推送到内网私有仓库

bash 复制代码
docker tag redis:7.2 harbor.company.local/base/redis:7.2
docker push harbor.company.local/base/redis:7.2

对比:错误理解 vs 正确认知

错误理解

Skopeo 只能在联网仓库之间复制,离线没法用。

正确认知

Skopeo 支持:

  • 仓库 -> tar
  • tar -> 仓库
  • 仓库 -> OCI
  • 仓库 -> 本地目录

它并不只是"在线复制工具",它也是一个镜像搬运格式转换工具


6.3 方式三:批量同步镜像到内网仓库

当你不是同步一个镜像,而是一批镜像时,copy 逐条执行会很烦。

这时候可以考虑:

  • Shell 批量循环
  • skopeo sync

6.3.1 用 Shell 批量迁移

先准备镜像列表:

txt 复制代码
docker.io/library/nginx:1.27
docker.io/library/redis:7.2
docker.io/library/alpine:3.20
docker.io/library/busybox:1.36

然后执行脚本:

bash 复制代码
#!/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 ">>> 全部同步完成"

脚本说明

bash 复制代码
#!/usr/bin/env bash
set -euo pipefail
  • -e:任一命令失败立即退出
  • -u:使用未定义变量时报错
  • -o pipefail:管道中任一命令失败都能感知

⚠️ 避坑点:不要写成"失败继续但最终显示成功"

很多人写批量迁移脚本时,最后打印"同步完成",结果中间已经失败好几个镜像了。


6.4 方式四:同步整个仓库或指定仓库内容

skopeo sync 更适合做镜像仓库同步。

例如:

bash 复制代码
skopeo sync --src docker --dest dir registry.example.com/busybox /tmp/busybox-sync

这条命令含义是:

  • 源是 docker registry
  • 目标是本地目录
  • 把指定仓库同步下来

如果你要做更完整的离线同步,可以先同步到目录,再转运,再推入内网仓库。


七、先别急着拉镜像:用 skopeo inspect 先看清楚

这是我非常建议养成的习惯。

很多时候镜像拉不下来、架构不对、标签不对,不是网络问题,而是你拿错镜像了。

7.1 查看远程镜像信息

bash 复制代码
skopeo inspect docker://docker.io/library/nginx:1.27

输出中重点关注这些字段

  • Digest:镜像摘要,判断版本是否一致
  • Architecture:架构,如 amd64 / arm64
  • Os:系统
  • Layers:镜像层
  • Env:环境变量
  • RepoTags:仓库所有标签

7.2 只看镜像摘要

bash 复制代码
skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Digest'

7.3 查看配置层

bash 复制代码
skopeo inspect --config docker://docker.io/library/nginx:1.27 | jq

这在排查以下问题时非常有用:

  • 为什么镜像拉下来跑不起来
  • 为什么同名标签内容变了
  • 为什么 ARM 机器拉 AMD64 镜像失败

⚠️ 避坑点:标签相同,不代表 digest 相同
latest 这种标签最容易坑人。

真正稳定的是 digest,不是 tag。


八、国内网络环境下,镜像加速到底该怎么做?

很多人看到"加速"两个字,第一反应是找"加速地址"。

这不完全错,但如果你从工程实践角度看,真正稳定的加速方案是分层的。


8.1 第一层:优先使用企业私有仓库做统一缓存

最靠谱的做法不是让每台机器都直连外网,而是:

  1. 用一台网络条件较好的中转机
  2. 用 Skopeo 定时同步常用镜像到 Harbor
  3. 所有开发机、测试机、K8s 节点只从 Harbor 拉取

架构如下:
flowchart LR A[Docker Hub / Quay / GCR] --> B[同步机 Skopeo] B --> C[企业 Harbor / 私有仓库] C --> D[开发机] C --> E[CI/CD Runner] C --> F[Kubernetes 节点]

这套方案比"每台机器自己配加速源"稳定太多。

优势

  • 统一治理
  • 可审计
  • 可控版本
  • 减少公网依赖
  • 构建速度更稳定
  • 更适合生产环境

8.2 第二层:构建"白名单基础镜像池"

建议企业维护一份基础镜像清单,比如:

  • nginx
  • redis
  • mysql
  • postgres
  • openjdk
  • eclipse-temurin
  • node
  • python
  • golang
  • alpine
  • busybox

每天或每周定时同步指定版本。这样:

  • 开发不需要到处找镜像
  • 安全团队更容易做漏洞治理
  • 线上版本来源统一

这比大家各自 docker pull 要专业得多。


九、多架构镜像迁移时的注意事项

现在 ARM 机器越来越多,尤其是:

  • Apple Silicon 开发机
  • ARM 服务器
  • 边缘设备
  • 某些国产化环境

所以你迁移镜像时,不能只看 tag,还要看架构。

9.1 查看架构

bash 复制代码
skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Architecture, .Os'

9.2 为什么会出问题?

同一个标签可能对应:

  • 单架构镜像
  • 多架构 manifest list

如果你目标环境是 ARM,但你同步的只是 AMD64 镜像,最后运行就会报:

  • exec format error
  • 镜像拉取正常但容器启动失败

⚠️ 避坑点:镜像能 pull,不代表能 run

很多问题不是出在仓库,也不是出在网络,而是架构不匹配


十、认证、登录与凭据管理

私有仓库几乎都需要认证。

10.1 登录仓库

bash 复制代码
skopeo login harbor.company.local

10.2 直接在命令中传凭据

bash 复制代码
skopeo inspect \
  --creds='username:password' \
  docker://harbor.company.local/base/nginx:1.27

或者 copy 时分别指定源和目标凭据:

bash 复制代码
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 仓库脚本

更好的做法:

  • 用 CI Secret
  • 用环境变量
  • 用独立凭据文件
  • skopeo login 预先登录

十一、实战:做一个"国内 Docker 镜像迁移与加速"同步脚本

下面给一个更接近生产可用的版本。

11.1 镜像清单文件

txt 复制代码
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

11.2 同步脚本

bash 复制代码
#!/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 "全部镜像同步完成"

11.3 使用方式

bash 复制代码
chmod +x sync-images.sh
./sync-images.sh

十二、错误写法和正确写法对比

这一段最适合拿来给团队统一规范。


12.1 错误写法:把中转机当下载器

bash 复制代码
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

问题

  • 流程冗长
  • 占用磁盘
  • 依赖 daemon
  • 批量脚本维护差
  • 容易把本机磁盘打满

12.2 正确写法:直接 copy

bash 复制代码
skopeo copy \
  docker://docker.io/library/nginx:1.27 \
  docker://harbor.company.local/base/nginx:1.27

优势

  • 更直接
  • 更适合自动化
  • 更适合批量迁移
  • 更适合国内网络环境下的 Docker 镜像迁移与加速

12.3 错误写法:只信 tag,不校验 digest

bash 复制代码
# 只看 tag,觉得 latest 一定是对的
skopeo copy docker://docker.io/library/nginx:latest docker://harbor.company.local/base/nginx:latest

风险

  • 上游 latest 已变更
  • 测试和生产拿到的不是同一份内容
  • 追问题时找不到版本基准

更稳妥的思路

  • 先 inspect
  • 记录 digest
  • 优先固定 tag 甚至固定 digest

十三、常见排查思路:镜像同步失败时看什么?

这里我按实战经验给一个排查顺序。

13.1 第一步:先 inspect,确认镜像是否存在

bash 复制代码
skopeo inspect docker://docker.io/library/nginx:1.27

如果 inspect 都失败,那大概率是:

  • 源仓库连不通
  • 标签写错
  • 仓库路径写错
  • 凭据不对

13.2 第二步:检查目标仓库权限

bash 复制代码
skopeo login harbor.company.local

然后再 copy。

常见报错本质上就是:

  • 没有 push 权限
  • 项目不存在
  • 项目禁止匿名访问

13.3 第三步:检查仓库路径是否完整

Docker Hub 官方镜像很多人容易少写 library

容易误写

bash 复制代码
docker://docker.io/nginx:1.27

更稳妥写法

bash 复制代码
docker://docker.io/library/nginx:1.27

⚠️ 避坑点:不同 Registry 的命名规范不完全一样

不要把 Docker CLI 的"省略写法"直接等价到所有工具中。


13.4 第四步:检查是否是架构问题

bash 复制代码
skopeo inspect docker://docker.io/library/nginx:1.27 | jq '.Architecture'

如果目标机器是 ARM,而你同步的是 AMD64,后面容器运行就会出问题。


13.5 第五步:检查出口网络和 DNS

国内环境下,最真实的问题往往不是命令写错,而是:

  • DNS 解析慢
  • TLS 握手超时
  • 出口限流
  • 代理配置不一致
  • 某些仓库域名被拦截或丢包

这时候你应该把"镜像同步"从每个节点收口到统一同步机,而不是让每台机器都自己去碰运气。


十四、Skopeo 和 Docker 到底怎么分工?

这个问题很多人会问。

我的建议很简单:

用 Docker 的场景

  • 本地开发构建镜像
  • 运行容器
  • 日常调试容器
  • docker build / run / exec / logs

用 Skopeo 的场景

  • 远程镜像检查
  • 镜像仓库之间迁移
  • 批量同步
  • 离线打包/导出
  • 做企业镜像缓存
  • 不想依赖 Docker Daemon 的自动化任务

一句话:

Docker 更像"容器运行工具",Skopeo 更像"镜像搬运工具"。


十五、一个非常实用的落地方案

如果你所在团队经常遇到国内拉镜像慢的问题,我建议直接落地这套方案:

方案步骤

  1. 准备一台网络较稳定的同步机
  2. 安装 Skopeo
  3. 登录 Harbor
  4. 维护一份基础镜像白名单
  5. 每天定时同步到 Harbor
  6. 开发机、CI、K8s 统一从 Harbor 拉镜像

定时任务示例

bash 复制代码
crontab -e

加入:

cron 复制代码
0 2 * * * /opt/scripts/sync-images.sh >> /var/log/sync-images.log 2>&1

这表示每天凌晨 2 点同步一次。

这样做的效果

  • 白天构建更稳定
  • 镜像来源统一
  • 版本更可控
  • 公网依赖更少
  • 故障排查更集中

十六、面试里如果被问到"如何解决国内 Docker 拉镜像慢"?

你不要只回答"配镜像加速器"。

更完整、更工程化的回答应该是:

可以这样答

  1. 开发环境 可以配置镜像加速器,提高常用镜像拉取速度
  2. 测试/生产环境 更推荐搭建企业私有仓库,比如 Harbor
  3. Skopeo 定时把 Docker Hub、Quay 等公网镜像同步到内网仓库
  4. 对于离线环境,可以用 skopeo copy 导出 docker-archive 再离线导入
  5. 同步前可以用 skopeo inspect 检查镜像 digest、架构、标签,避免拉错镜像
  6. 对关键基础镜像维护白名单和固定版本,避免 latest 带来的不可控变更

这类回答会明显比"用代理"更像真正做过事的人。


十七、总结:国内网络环境下,Docker 镜像迁移与加速,Skopeo 是非常值得掌握的工具

把这篇文章的核心内容收一下:

你应该记住的 5 个结论

  1. 传统 docker pull/tag/push 方式能用,但效率不高
  2. Skopeo 可以直接在仓库之间复制镜像,不依赖 Docker Daemon
  3. 国内网络环境下,最佳实践不是每台机器自己拉,而是统一同步到私有仓库
  4. skopeo inspect 很适合在拉取前确认 digest、架构和标签
  5. 离线环境、批量迁移、镜像缓存仓库,这些场景 Skopeo 都比 Docker 原生命令更顺手

最推荐的落地方式

用一台网络条件好的同步机 + Skopeo + Harbor,构建企业内部镜像缓存和迁移通道。

这套方案不花哨,但很稳,非常适合国内网络环境下的 Docker 镜像迁移与加速。


十八、拓展学习资料

官方文档


扫码关注不迷路