跳到正文
容器镜像拉不下来时:从代理、同步到离线交付
容器镜像拉不下来时:从代理、同步到离线交付

容器镜像拉不下来时:从代理、同步到离线交付

有网、受限网和完全离线环境需要的不是同一条命令。把镜像获取当成交付链路,才能在代理缓存、私有仓库和 save/load 之间选对方案。

速读#

容器镜像拉不下来,表面是 docker pull 失败,实际常常是镜像怎么跨过网络边界的问题。

  • 能访问外网,只是上游慢:用可信的代理缓存或镜像仓库。
  • 只有中转机能访问外网:用 skopeocrane 或 CI 把镜像同步到内网仓库。
  • 环境完全离线:导出文件,校验后带进去,再导入或推到离线仓库。

临时换一个陌生镜像站也许能救急,但不能成为生产交付链。真正要稳定的是来源、版本、校验、同步和回滚这一整条路径。

先判断是哪一种网络#

我以前遇到拉取失败,第一反应是继续找新的镜像地址。看得多了才发现,这种处理把三类完全不同的环境混在了一起:

环境真实问题合适做法
可联网,但访问上游不稳定路径慢或偶发失败代理缓存、可信镜像仓库
生产网不能直连外网网络边界受控中转同步到内网 Registry
完全离线没有网络路径文件导出、校验、导入

先把环境归类,后面才谈工具。否则今天改 daemon mirror,明天手工传 tar,后天又让每台节点各自找来源,最终谁也说不清线上镜像从哪里来。

路径一:代理缓存,适合“有网但不稳”#

如果机器可以访问外部仓库,只是链路慢,最省事的是让一个受控的 Registry 做 pull-through cache。第一次从上游拉取,之后由缓存直接提供。

它解决的是重复下载和链路抖动,不是供应链信任。配置前仍要回答几个问题:

  • 缓存由谁维护,故障时是否有明确回退路径。
  • 上游标签变化后,缓存多久刷新。
  • 是否保留镜像 digest,能不能确认拿到的是同一个内容。
  • 认证信息放在哪里,是否会写进脚本或日志。

我不会把网上搜到的一串公共镜像地址轮流塞进生产配置。免费节点可能限速、过期或改写内容;今天能拉,不代表下次部署仍能复现。生产需要的是一个自己能解释、能监控的入口。

路径二:同步到内网仓库#

只允许少数中转机访问外网时,应该把“拉镜像”和“部署应用”拆开。中转侧负责从可信来源获取镜像,生产侧只认识内网仓库。

skopeocrane 都能在 Registry 之间复制镜像,不必先启动容器:

Terminal window
skopeo copy --all \
docker://docker.io/library/nginx:1.27 \
docker://harbor.example.com/base/nginx:1.27
crane copy \
docker.io/library/nginx:1.27 \
harbor.example.com/base/nginx:1.27

--all 的意义是连同多架构 manifest 一起复制,避免在 x86 机器上同步成功,到了 ARM 节点才发现缺少对应镜像。

镜像固定、数量少时,手工同步能用;镜像一多,就该让 CI 根据一份清单执行复制,并记录源 digest、目标 digest 和同步时间。CI 的价值不是把命令藏起来,而是让每次交付都有相同入口和可追踪记录。

Harbor、Nexus 和 Artifactory 怎么选#

不用为了“正规”同时搭三套。

工具更适合的情况
Harbor主要管理容器镜像,需要复制、权限和镜像扫描
Nexus已经在统一管理 Maven、npm 等制品,顺手纳管镜像
Artifactory企业已有完整制品平台和配套流程

对一个小团队,最重要的不是功能表,而是所有部署只从一个受控仓库取镜像。已有 Nexus 就继续用;只管容器镜像,Harbor 更直接。为了一个临时交付搭完整平台,维护成本可能比手工文件还高。

路径三:完全离线时用 save/load#

镜像不多、目标环境完全离线时,docker savedocker load 是最短路径:

Terminal window
docker pull nginx:1.27
docker save -o nginx-1.27.tar nginx:1.27
sha256sum nginx-1.27.tar > nginx-1.27.tar.sha256

把两个文件一起送入离线环境,先校验再导入:

Terminal window
sha256sum -c nginx-1.27.tar.sha256
docker load -i nginx-1.27.tar

校验的是传输文件有没有变化,不等于证明镜像来源可信。来源验证应该在联网侧完成,至少固定明确版本并记录 digest;有签名体系时,再验证签名和证明材料。

当镜像从几个增长到几十个,逐个 tar 会开始失控:重复层占空间、版本清单容易漏、每台节点都要重复导入。这时离线环境里也应该放一个 Registry,文件只负责跨边界,进入后统一推到仓库,再由节点正常拉取。

Kubernetes 最怕每个节点各自想办法#

单机执行一次 docker load 可能就结束了,Kubernetes 不一样。Pod 会被调度到任意节点,某个节点没有镜像就会出现 ImagePullBackOff

稳定做法是:

  1. 把镜像同步进所有节点都能访问的内网 Registry。
  2. 部署清单中的 image 明确指向这个仓库。
  3. 配好仓库证书、认证和节点信任。
  4. 用不可变版本或 digest,避免同名标签在节点间漂移。

紧急情况下可以给每个节点预加载镜像,但它很难长期维护。新增节点、重建节点和镜像升级都要重新补一遍;一旦漏了,故障只会在调度到那台节点时出现。

ImagePullBackOff 只是结果#

看到它不要只反复重启 Pod。先看事件:

Terminal window
kubectl describe pod <pod-name> -n <namespace>

常见根因包括仓库地址写错、DNS 不通、证书不受信、认证失败、限流和镜像架构不匹配。它们都会落成“拉取失败”,处理方式却完全不同。

排查顺序可以固定下来:名字和标签是否存在,再看认证与证书,然后测 Registry 网络,最后确认节点架构。比不断更换镜像源更快,也不会把一个配置错误误判成网络问题。

我会保留的最小交付链#

对受限网络,我会把流程压成五步:

可信上游 -> 联网侧拉取与校验 -> 同步/导出 -> 内网仓库 -> 部署

每一步只留下必要记录:镜像全名、源 digest、目标位置、同步时间和执行结果。镜像不是“能 pull 下来就行”的安装包,它是生产制品;既然要进生产,就应该像代码版本一样能回答从哪来、现在是什么、坏了退回哪一版。

临时环境用 save/load 没问题,长期环境用受控 Registry 更稳。工具选哪个是次要的,不要让生产机器在部署时临时去赌外部网络,才是这件事真正的边界。

版权许可

CC BY-NC-SA 4.0 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

相关文章

s1oopX

登录 s1oopX