Docker镜像拉不下来,先别急着改一堆配置

国内云服务器上拉 Docker 镜像,最常见的报错不是 Docker 本身坏了,而是访问 Docker Hub 的链路不稳定。实际使用中发现,同一台机器有时候上午还能 pull,下午就开始 timeout;同一个镜像,centos 拉不动,nginx 偶尔能下来;换个机房或者换条线路,问题又消失。

典型报错大概是这些:

Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection

Get "https://auth.docker.io/token": context deadline exceeded

dial tcp: lookup registry-1.docker.io: i/o timeout

net/http: TLS handshake timeout

这些错误看起来不一样,本质上多半绕不开三类问题:DNS解析慢或被污染、到 Docker Hub 的 TCP/TLS 连接不稳定、镜像层下载中途断流。国内云服务器默认出口线路、运营商、机房策略都会影响结果,不是简单换一个命令就能一直稳。

先确认是 Docker Hub 链路问题,不要一上来就重装 Docker

排查时建议先看三处:DNS、HTTPS连通性、Docker daemon 日志。

DNS可以这样看:

nslookup registry-1.docker.io

nslookup auth.docker.io

如果这里就经常超时,先别折腾 Docker。把 DNS 换成 223.5.5.5、119.29.29.29、114.114.114.114 这类国内公共 DNS,或者云厂商内网 DNS。不同云厂商机房对公网 DNS 的体验差别很明显。

HTTPS连通性可以用 curl 试一下:

curl -I https://registry-1.docker.io/v2/

正常情况下返回 401 Unauthorized 也没问题,说明服务能连上,因为 Docker Hub 的 registry 本来就需要认证流程。怕的是一直卡住、TLS handshake timeout、connection reset。

Docker daemon 日志在 systemd 机器上这样看:

journalctl -u docker -n 100 --no-pager

这里补充一点,很多人只看 docker pull 的终端报错,不看 daemon 日志,容易误判。daemon 日志里能看到是解析失败、认证失败,还是 layer 下载失败。

Docker镜像加速配置放在 daemon.json,不要改错位置

Docker Engine 的镜像加速配置一般写在 /etc/docker/daemon.json。这个文件不是每台机器都有,没有就新建,有就合并配置,别直接覆盖已有的 data-root、log-driver、insecure-registries。

常见写法如下:

{ "registry-mirrors": [ "https://mirror.example.com" ] }

如果文件里已经有其他配置,要注意 JSON 逗号。比如:

{ "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "registry-mirrors": [ "https://mirror.example.com" ] }

配置完重载并重启 Docker:

systemctl daemon-reload

systemctl restart docker

检查是否生效:

docker info

输出里看到 Registry Mirrors,才算 Docker daemon 读到了配置。只是写进 daemon.json 但没重启,pull 行为不会变化。

公共镜像加速地址能用,但不要盲目复制一长串

以前很多国内公共 Docker Hub mirror 很好用,但这两年变化比较大。有的停止服务,有的只给自家云内网用,有的需要登录授权,有的对热门镜像还能缓存,对冷门镜像直接回源失败。

实际使用中发现,daemon.json 里塞十几个 mirror 并不一定更稳。Docker daemon 会按顺序尝试,前面的 mirror 如果响应慢但不立刻失败,反而会拖长 pull 时间。更稳的做法是选一两个确认可用的源,出了问题再换,不要堆配置。

可以用这种方式测一个加速源是否真的可用:

curl -I https://mirror.example.com/v2/

返回 200、401、或者带 Docker-Distribution-Api-Version 相关头信息,都说明它像一个 registry 服务。直接超时、证书错误、返回普通网页,就别放进 Docker 配置。

多说一句,很多所谓“Docker镜像加速地址合集”是过期内容。复制之前看一下最近更新时间,最好在自己的服务器上实际 curl 和 docker pull 测一次。

国内云服务器推荐的配置方式

如果只是单台业务机临时拉镜像,可以直接配置可用 mirror,然后重启 Docker。这个方式成本最低,问题是稳定性取决于第三方 mirror。

如果是多台机器、CI/CD、Kubernetes 集群,就不建议每台机器都依赖公开 mirror。更稳的做法是准备一个自己的 registry cache,或者把常用镜像同步到私有仓库。这样生产节点只访问自己的仓库,外部链路波动不会直接影响发布。

常见场景可以这样拆:

个人测试机:配置 registry-mirrors,够用就行,偶发失败手动重试。

企业业务机:常用镜像提前同步到私有 registry,发布时不直接依赖 Docker Hub。

Kubernetes 集群:节点侧统一配置 containerd 或 Docker mirror,同时在镜像名称上规范化,避免同一个镜像出现 docker.io/library/nginx、nginx、registry-1.docker.io/nginx 这种混用。

跨境业务或海外节点:在海外机房放 registry cache,国内节点按线路质量选择是否走它,不要默认认为海外一定快。

自建 registry cache,适合多台机器和发布频繁的环境

自建缓存仓库的好处是明显的:第一次拉镜像会慢,因为需要回源 Docker Hub;后面再拉同一个镜像层,直接从自己的 cache 出,速度稳定很多。对于 CI 构建、灰度发布、批量扩容,这个差别很大。

Docker 官方 registry 支持 proxy cache 模式,可以这样跑:

docker run -d --restart=always --name registry-cache -p 5000:5000 -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io registry:2

然后在业务机器的 /etc/docker/daemon.json 配置:

{ "registry-mirrors": [ "http://你的缓存服务器IP:5000" ] }

如果走公网,建议上 HTTPS,不建议长期用 HTTP。HTTP 可以在内网临时用,公网暴露 registry 服务要考虑证书、访问控制、防火墙、DDoS 风险。

HTTPS 方式可以用 Nginx 反代 registry,再配置证书。Docker 客户端访问时就写:

{ "registry-mirrors": [ "https://registry.example.com" ] }

这里要注意,registry cache 只缓存被请求过的镜像层,不是把 Docker Hub 全量同步下来。第一次拉一个冷门镜像仍然要看回源链路。生产环境里常用的 nginx、redis、mysql、postgres、openjdk、node、alpine、busybox 这些,建议提前预热。

containerd环境别只改 Docker 配置

现在 Kubernetes 节点很多已经不用 Docker 作为运行时,而是 containerd。这个时候改 /etc/docker/daemon.json 没用,kubelet 拉镜像走的是 containerd。

containerd 的配置一般在:

/etc/containerd/config.toml

如果没有完整配置,可以生成:

containerd config default > /etc/containerd/config.toml

然后找到 registry mirrors 相关位置配置 docker.io mirror。不同 containerd 版本配置格式略有差异,常见结构类似:

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]

endpoint = ["https://mirror.example.com"]

改完重启:

systemctl restart containerd

验证拉取:

crictl pull docker.io/library/nginx:latest

如果 crictl 不通但 docker pull 通,不要混着判断。Docker 和 Kubernetes 节点运行时可能根本不是同一套配置。

DNS、MTU、防火墙这些小坑也会导致拉取失败

镜像加速不是所有问题的答案。实际现场里碰到过不少“看起来像 Docker Hub 访问慢”的问题,最后原因在网络细节。

DNS解析慢

Docker daemon 默认会使用系统 DNS。如果 /etc/resolv.conf 里写了不可用的内网 DNS,或者云服务器从旧镜像克隆过来后 DNS 没更新,docker pull 会在解析阶段卡很久。

可以在 daemon.json 指定 DNS:

{ "dns": [ "223.5.5.5", "119.29.29.29" ], "registry-mirrors": [ "https://mirror.example.com" ] }

不过这里别乱写。如果服务器在某些云厂商 VPC 内,内网域名解析依赖云厂商 DNS,直接改成公网 DNS 可能影响内网服务发现。

MTU不匹配

云服务器挂了隧道、VPN、GRE、VXLAN,或者容器网络经过多层封装时,MTU 不匹配会导致大包传输异常。表现就是小请求能通,大镜像层下载中途失败。

可以看 Docker bridge 的 MTU:

ip link show docker0

必要时在 daemon.json 配置:

{ "mtu": 1450, "registry-mirrors": [ "https://mirror.example.com" ] }

改 MTU 要结合实际网络,不建议看到别人写 1450 就照抄。公有云普通 VPC 常见 1500,叠加隧道后才需要下调。

安全组和出方向策略

有些企业服务器出方向不是全放通,只允许访问固定端口或固定 IP。Docker Hub 涉及 registry-1.docker.io、auth.docker.io、production.cloudflare.docker.com 等域名,背后 IP 还会变化。安全组如果只放了 443 但出口防火墙按域名或 SNI 做策略,也可能拉不下来。

这种环境更适合内部私有 registry。业务节点只访问内网仓库,外部回源由专门的同步机处理,审计和放行都清楚。

选择云服务器时,镜像拉取也要看线路

很多人买云服务器只看 CPU、内存、硬盘,等部署时才发现 Docker 镜像拉不下来。镜像拉取本质是跨网络下载大量小文件和大 layer,线路质量、国际出口、丢包、晚高峰拥塞都会影响体验。

国内业务节点如果经常要访问海外源,优先看网络质量,而不只是标称带宽。BGP、CN2、GIA、三网精品这些词要结合实际测试,不能只看宣传页。带宽 100Mbps 不代表 Docker Hub 稳定满速,跨境链路丢包 2% 时,下载大 layer 可能就很难受。

如果你也在找这种能承载业务部署、镜像缓存、海外访问中转的云服务器,可以看看129云。它的产品线覆盖高性能云服务器、G口大带宽服务器、高防服务器和海外云计算场景,游戏、企业、高防业务都能选到对应规格。需要确认线路、带宽、防御和机房位置时,直接问客服会省很多测试时间,热线是 400-9177118。

不同场景下怎么选机器

如果只是放一个 Docker registry cache,不一定需要很高 CPU,重点看磁盘、带宽、线路稳定性。缓存命中后主要消耗出方向带宽和磁盘 IO。SSD 比机械盘体验好很多,尤其是多节点同时拉镜像时。

如果业务在海外,国内只是少量管理访问,美国洛杉矶这类节点可以用来跑构建、缓存和海外业务服务。129云美国洛杉矶产品有 1核到4核、1G到8G内存、30G到120G硬盘、30Mbps带宽、200G到4TB流量的配置,适合轻量服务和镜像缓存测试。它标注无回国保障,只计费上行流量,所以不要把它当成国内访问强保障线路用。

如果更看重大带宽分发,比如镜像层比较大、构建产物多、海外访问为主,德国大宽带的 10Gbps 峰值比较适合做大流量下载和分发测试。但它是普通线路,不保证大陆网络访问,用之前要明确访问对象在哪里。

如果服务容易被打,或者 registry 同时暴露给公网业务系统,高防节点就有意义。比如美国高防-B型,4C CPU、4G DDR4 ECC、80G SSD、75Mbps 峰值带宽、1个 IPv4、三网精品、霄龙 CPU、200G 防御。DDoS 风险不高的内部缓存没必要上高防,面向公网客户访问的业务入口才需要重点考虑。

镜像名写法不统一,也会让加速失效

Docker 对镜像名有默认补全规则。比如 docker pull nginx,实际会被解析成 docker.io/library/nginx:latest。很多 mirror 配置只针对 docker.io 生效,如果项目里有人写 registry-1.docker.io/library/nginx,有人写 docker.io/library/nginx,还有人写 nginx,排查时会很乱。

建议业务里统一写法,例如:

docker.io/library/nginx:1.26

docker.io/library/redis:7.2

不要在生产里长期使用 latest。latest 会带来两个问题:缓存不可控,发布不可追踪。今天拉到一个 layer,明天同一个 tag 指向新 layer,节点间版本可能不一致。

在 Kubernetes YAML 里也一样,镜像 tag 尽量固定。发布系统可以用明确版本号或者 digest:

nginx@sha256:xxxxxxxx

digest 写法最稳,但维护成本高一些,适合对一致性要求高的环境。

私有仓库同步比临时 pull 更靠谱

生产环境里常见做法是把外部镜像同步到自己的 registry。比如把 docker.io/library/nginx:1.26 同步成 registry.example.com/base/nginx:1.26。业务发布只从 registry.example.com 拉。

可以用 skopeo、crane、docker pull + docker tag + docker push 做同步。简单命令类似:

docker pull nginx:1.26

docker tag nginx:1.26 registry.example.com/base/nginx:1.26

docker push registry.example.com/base/nginx:1.26

这种方式比 registry mirror 更可控。mirror 是请求时缓存,同步仓库是提前准备好。遇到 Docker Hub 抽风、第三方 mirror 失效、跨境链路抖动,发布系统不受影响。

缺点也很直接:需要维护仓库、清理旧镜像、做权限控制、备份元数据。Harbor 在企业里用得比较多,支持项目、用户、镜像扫描、复制策略。轻量场景用 registry:2 也可以,别把公网匿名 push 打开。

Docker Hub限流也要考虑

Docker Hub 对匿名拉取和免费账号有速率限制。很多服务器共用一个出口 IP 时,很容易被限流。NAT 网关、办公出口、CI Runner 集群都可能遇到。

表现通常是:

toomanyrequests: You have reached your unauthenticated pull rate limit

解决方式不是换 DNS,也不是重启 Docker。要么登录 Docker Hub:

docker login

要么使用自己的私有仓库同步镜像,要么使用有授权的镜像服务。CI 环境里尤其要注意,几十个 Runner 并发构建,很快就能把匿名额度打满。

配置后拉取仍然慢,按这个顺序查

先看 docker info,确认 Registry Mirrors 已经生效。没生效就是配置文件路径、JSON格式、daemon 重启问题。

再用 curl 测 mirror 的 /v2/ 接口。mirror 自己都慢,Docker 不可能快。

然后拉一个小镜像测试:

docker pull hello-world

小镜像能拉,大镜像失败,多半是链路稳定性、MTU、磁盘空间或中途断流。

看磁盘空间:

df -h

看 Docker 数据目录:

docker info | grep "Docker Root Dir"

看是否有残留下载占空间:

docker system df

清理时要谨慎:

docker system prune -a

这条命令会删掉未使用镜像,生产机器上别随手执行。尤其是回滚依赖旧镜像的环境,清掉以后重新拉不下来会很尴尬。

一份比较稳的 daemon.json 写法

单机 Docker Engine 可以按这个结构写,mirror 地址换成自己验证可用的:

{ "registry-mirrors": [ "https://mirror.example.com" ], "dns": [ "223.5.5.5", "119.29.29.29" ], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

如果服务器已经有 daemon.json,就合并进去,不要覆盖。

执行:

dockerd --validate --config-file=/etc/docker/daemon.json

如果系统里的 Docker 版本支持这个参数,可以提前检查配置。老版本不支持就跳过。

重启:

systemctl daemon-reload

systemctl restart docker

确认:

docker info

拉取:

docker pull nginx:1.26

别把公网 registry 裸奔放出去

自建 registry cache 如果放公网,至少要做 HTTPS、认证、IP白名单或防火墙限制。开放 5000 端口让所有人访问,轻则被别人蹭流量,重则被刷爆磁盘。

Nginx 反代时可以限制 body size、连接数、来源 IP。云服务器安全组只放自己的业务节点 IP。高频拉取场景还要关注磁盘清理策略,registry cache 时间长了会堆很多 layer。

registry:2 的垃圾回收不是简单删目录,清理前要了解 manifest 和 blob 关系。Harbor 的清理策略更直观,但也要定期执行垃圾回收,不然磁盘迟早满。

国内云服务器拉 Docker 镜像,真正稳的是减少外部依赖

临时测试靠 mirror,生产发布靠私有仓库,集群节点统一 runtime 配置,常用镜像提前同步。网络层面把 DNS、线路、MTU、安全组查清楚,别让 Docker 背所有锅。

需要快速验证当前机器问题时,直接跑这几条:

curl -I https://registry-1.docker.io/v2/

docker info

journalctl -u docker -n 100 --no-pager

docker pull hello-world

docker pull nginx:1.26