Docker 拉不动镜像时,先分清是哪一段慢

docker pull 卡住是最容易让人怀疑人生的操作之一:没有任何进度,等十分钟,最后给一句模糊的报错。想解决它,先别急着换镜像源,先确认卡在哪一段。
三段式定位
拉一个镜像,客户端至少要经过三个环节:
- DNS:把
registry-1.docker.io解析成 IP; - TCP:连上 443;
- TLS + 传输:握手、鉴权、下载各层 blob。
用 curl 直接打一下,比看 docker 的报错清楚得多:
# 只看连通性和握手
curl -sv --max-time 10 https://registry-1.docker.io/v2/ 2>&1 | grep -E 'Connected|TLS|HTTP/'
- 卡在解析 — DNS 问题(换个解析器再试);
- 卡在
Connected之前 — 网络层被拦,通常是链路策略或路由问题; - 连上但握手超时 / 下载途中反复重试 — 多半是到 registry 的链路质量差,这就是镜像加速要解决的场景。
registry-mirrors 加速的是什么
/etc/docker/daemon.json 里配的镜像源,作用是让 Docker 从就近的镜像站取 layer,而不是直连官方 registry:
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.m.daocloud.io",
"https://docker.1panel.live"
]
}
改完执行 systemctl reload docker 生效(不是 restart)。
要注意两件事:
- 镜像源不是全量代理。有些源只缓存热门镜像,冷门镜像照样回源,回源失败看起来还是「拉不动」;
- 免费镜像源会限流、会下线。把它当唯一依赖,早晚会在某天早上发现构建全红。
所以更稳的做法是组合拳:常用基础镜像提前拉到本地,或者在能连外网的机器上 docker save 成 tar 再 docker load 过去;正式环境把镜像版本固定住(写死 tag 甚至 digest),构建时不依赖网络实时拉取。
换个思路:让「拉不到」变得不重要
镜像拉取是构建链路里最不可控的一环。少见的一次性方案是换镜像源,多见的长期方案是把镜像变成自己的资产:
- 一次性:镜像源 / 离线导入;
- 长期:自建 registry(或带缓存的内网镜像服务),CI 里统一从这里取。
我自己的做法很土但有效:基础镜像只保留两三个(caddy、python),都提前拉到本机,配置里用固定的镜像前缀,这样即使外面变天,容器也照样起得来。
小结
「拉不动」不是一种故障,而是三种。先用 curl 定位到具体哪一段,再决定是换解析、换源,还是干脆把镜像搬进来。