返回博客列表
开发者与 AI

Docker Hub在中国拉取镜像慢、失败怎么办?完整解决指南

PEAK DEV·AI·23CH Docker Hub在中国拉取镜像 慢、失败怎么办?完整解决…

docker pull nginx,终端停在 Pulling fs layer 一动不动;几分钟后抛出 net/http: request canceled while waiting for connection (Client.Timeout exceeded)。换个镜像再试,error pulling image configuration: dial tcp: i/o timeout。这大概是每个在中国大陆做容器开发的工程师都熟悉的画面——本地开发被卡住还只是烦躁,CI 流水线上每次构建都在拉镜像这一步超时重试,才是真正的生产力黑洞。

直答:4 种可行方案速览

方案一句话说明适合谁
① 配置 registry mirror在 daemon.json 里加加速器地址,改动最小有可用加速器地址的用户
② Docker daemon 走代理systemd 注入代理环境变量,直连官方源有 JetStream 或本地代理的开发者
③ 国内镜像仓库中转用阿里云 ACR / 腾讯云 TCR 做”搬运”固定镜像清单的团队 / CI
④ VPN 全局直连加密隧道直达 registry-1.docker.io想要零配置、全生态可用的人

下面先说清楚为什么慢,再逐个展开。


为什么 Docker Hub 在中国这么慢?

1. 跨境链路本身就拥挤。 Docker Hub 的镜像数据托管在海外(registry 服务与对象存储 / CDN 分布在欧美节点),拉取时数据要穿过国际出口。高峰期丢包率上升,TCP 自动降速,一个几百 MB 的镜像可能要拉几十分钟,而且 layer 越大越容易在中途断掉。

2. 拉一次镜像涉及多个域名。 一次 docker pull 背后至少要访问 registry-1.docker.io(镜像清单与 layer 索引)、auth.docker.io(令牌认证)以及背后的 CDN 存储域名。任何一环连接质量差,整个拉取就会卡住或失败——这也是为什么有时报错发生在 Pulling fs layer,有时发生在认证阶段。

3. 公共加速器大面积退场。 2024 年年中以来,国内大量曾经流行的公共 Docker 镜像加速服务陆续停止对外开放或限制使用范围,很多老教程里的加速器域名已经失效。这是当前环境的客观现状:任何写死在博客里的公共加速器地址都有时效性,所以本文不会给出具体地址,而是教你怎么配置、怎么验证手里的地址是否可用。


方案一:配置 registry-mirrors(改动最小)

如果你手里有一个可用的加速器地址——比如阿里云容器镜像服务(ACR)为登录用户提供的专属加速地址(在阿里云控制台”镜像工具 → 镜像加速器”页面获取,每个账号独立),配置方式如下。

编辑(不存在则创建)/etc/docker/daemon.json

{
  "registry-mirrors": [
    "https://<你的加速器地址>"
  ]
}

然后重启 Docker daemon 使配置生效:

sudo systemctl daemon-reload
sudo systemctl restart docker

验证配置是否生效——docker info 的输出末尾应能看到你配置的地址:

docker info | grep -A 3 "Registry Mirrors"

验证加速器本身是否可用——对着 /v2/ 端点发个请求,返回 200401(要求认证)都说明服务活着,超时或 403 则说明该地址已失效:

curl -I --max-time 10 https://<你的加速器地>/v2/

优点: 一次配置全局生效,docker pulldocker build、docker-compose 都自动走加速器。

局限: 前面说过,公共加速器时效性强,需要自己维护”哪个地址还活着”;部分加速器只缓存热门公开镜像,冷门镜像或私有镜像仍会回源失败。

macOS / Windows 上的 Docker Desktop 用户:在 Settings → Docker Engine 的 JSON 编辑框里加入同样的 registry-mirrors 字段,点 Apply & Restart 即可。


方案二:让 Docker daemon 走代理(最常被配错的方案)

很多人在 shell 里 export https_proxy=... 之后发现 docker pull 还是超时——这是因为拉取镜像的是 Docker daemon 这个后台进程,不是你的 shell,shell 环境变量对它完全无效。正确做法是通过 systemd drop-in 给 daemon 注入代理配置。

假设你本机跑着一个代理(例如 JetStream 配合 Clash Verge 等客户端,本地 HTTP 代理端口 7890,具体接入方式见教程 如何在 Clash Verge 中使用 JetStream):

# 1. 创建 drop-in 目录
sudo mkdir -p /etc/systemd/system/docker.service.d

# 2. 写入代理配置
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf <<'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1,.aliyuncs.com,.tencentcloudcr.com"
EOF

# 3. 重载配置并重启 daemon
sudo systemctl daemon-reload
sudo systemctl restart docker

# 4. 确认环境变量已注入
sudo systemctl show --property=Environment docker

第 4 步的输出里能看到 HTTP_PROXY / HTTPS_PROXY 就说明配置生效了,此时 docker pull 会直连 Docker Hub 官方源,速度取决于你的代理线路质量。

几个容易踩的坑:

  • NO_PROXY 里记得排除内网 registry 和国内云厂商仓库域名,否则这些本来就快的地址也会绕道代理。
  • 这份配置只管 daemon 拉镜像docker build 过程中容器内部的网络请求(如 RUN pip install)需要另外通过 --build-arg HTTP_PROXY=... 传入。
  • Docker Desktop 用户不需要碰 systemd:在 Settings → Resources → Proxies 中打开 Manual proxy configuration 填入同样的地址即可。

优点: 直连官方源,镜像永远是最新最全的,私有仓库、docker logindocker push 全部正常工作。

局限: 依赖代理线路的稳定性;大镜像长时间传输对线路质量要求高——这正是下文方案四要解决的问题。


方案三:通过国内镜像仓库中转

如果你的团队镜像清单相对固定(基础镜像 + 几个常用依赖),可以干脆把镜像”搬”到国内仓库:阿里云 ACR 个人版和腾讯云 TCR 个人版都提供免费额度,国内拉取速度可以跑满带宽。

中转思路很简单——在一台海外连接顺畅的机器(海外 VPS、GitHub Actions runner 都可以)上执行:

# 1. 从 Docker Hub 正常拉取
docker pull nginx:1.27

# 2. 打上国内仓库的 tag(以阿里云 ACR 为例)
docker tag nginx:1.27 registry.cn-hangzhou.aliyuncs.com/<你的命名空>/nginx:1.27

# 3. 推送到国内仓库
docker login registry.cn-hangzhou.aliyuncs.com
docker push registry.cn-hangzhou.aliyuncs.com/<你的命名空>/nginx:1.27

之后国内的开发机和 CI 直接从 registry.cn-hangzhou.aliyuncs.com/... 拉取,速度与稳定性都是国内水平。配合 GitHub Actions 定时任务,还能实现常用镜像的自动同步。

优点: 拉取端零特殊配置、速度极快、完全可控,最适合生产环境和 CI。

局限: 只覆盖你主动搬运过的镜像;镜像更新需要重新同步;Dockerfile / compose 文件里的镜像地址要相应改写。


方案四:JetStream 全局连接,直连 Docker Hub

前三个方案各有前提:方案一要有活着的加速器,方案三要提前搬运。当你需要不加任何 Docker 侧配置、随手拉任何镜像都能成的体验时——比如临时调试一个冷门镜像、拉取需要认证的私有仓库、或者跑一条每天要拉几十 GB 镜像的 CI 流水线——用 JetStream VPN 建立全局连接是最省事的路径。

原理上,JetStream 建立加密隧道后,registry-1.docker.ioauth.docker.io 及其背后 CDN 的所有流量都从隧道内直达目标服务器,绕开拥挤的公共出口。对 Docker 拉镜像这种长时间大流量的场景,有几点实际体验上的差异:

  • 智能分流:国内流量(如阿里云 ACR、公司内网 registry)自动走直连,只有出境流量进隧道,不需要手工维护 NO_PROXY 之类的排除列表。
  • 低延迟的香港 / 日本 / 新加坡节点:物理距离近、国际出口质量好,对多 layer 并行下载的吞吐提升明显。
  • 长连接稳定性:为 CI / 开发机长时间大流量拉取设计,几个 GB 的镜像层中途不断流,不用反复 retry。

连接后不需要改 daemon.json、不需要配代理,docker pull 开箱即用:

# JetStream 全局连接状态下,直接拉取即可
docker pull pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime

JetStream 支持 Windows / macOS / Linux / Android 全平台(下载地址,更多信息见官网)。如果你同时被 GitHub clone 慢困扰,可以参考我们的另一篇实测:GitHub 在中国访问慢?完整加速指南


四种方案对比

维度① registry mirror② daemon 走代理③ 国内仓库中转④ VPN 全局直连
配置成本低(改 daemon.json)中(systemd drop-in)高(需搬运流程)极低(连上即用)
镜像覆盖加速器缓存范围全部(含私有)仅已搬运镜像全部(含私有)
速度稳定性取决于加速器取决于代理线路国内带宽级专线节点级
长期维护需盯加速器存活基本免维护需同步更新免维护
最适场景个人开发机已有代理的开发者生产 / CI 固定镜像全场景兜底

推荐组合:生产环境和 CI 用方案三保证确定性;开发机日常用方案四(或方案二)保证”任何镜像随手可拉”。两者完全可以共存。


常见问题(FAQ)

为什么我在终端 export 了代理,docker pull 还是超时?

因为执行拉取的是 Docker daemon(root 权限的后台进程),它不继承你 shell 的环境变量。必须按方案二用 systemd drop-in 配置,或在 Docker Desktop 的 Proxies 设置里填写。

配置了 registry mirror 之后怎么确认真的在走加速器?

两步:docker info | grep -A 3 "Registry Mirrors" 确认配置已加载;然后拉一个镜像,观察速度变化。如果配置了 mirror 但速度依旧,大概率是该加速器已失效,用 curl -I https://<地址>/v2/ 验证(超时即失效)。

docker pull 报 429 Too Many Requests 是怎么回事?

这是 Docker Hub 的拉取频率限制,匿名用户按 IP 计数、配额较低。走公共加速器时大量用户共享出口 IP,更容易触发。解决办法:docker login 使用个人账号提升配额,或改用方案三 / 方案四避开共享 IP。

Kubernetes 节点(containerd)怎么配置?

containerd 不读取 daemon.json。代理方式与方案二相同,但服务名换成 containerd.service(drop-in 放在 /etc/systemd/system/containerd.service.d/);镜像加速则需要在 /etc/containerd/certs.d/docker.io/hosts.toml 中配置 mirror 地址,改完 systemctl restart containerd

docker build 里的 apt / pip 下载慢,跟这篇的方案有关系吗?

方案一、二只加速镜像层的拉取,构建过程中容器内部的网络请求是另一回事——需要给构建传入代理(--build-arg HTTPS_PROXY=...)或换用国内软件源。如果你用方案四的全局连接并开启智能分流,构建内的出境请求也会一并走隧道,这是它额外的省心之处。类似的依赖下载问题我们在 Hugging Face 模型下载加速指南 里有更完整的讨论。


结语

Docker Hub 拉取慢的根源是跨境链路,而公共加速器退场让”抄一个镜像地址”的老办法不再可靠。更稳妥的思路是:用可验证的方法管理加速器(方案一),用正确的姿势配置代理(方案二),关键镜像做国内中转(方案三),再用 JetStream 全局连接兜底(方案四)。四种方案互不冲突,按场景组合,Pulling fs layer 卡住的日子就可以翻篇了。

准备好开始了吗?

立即体验安全、快速的 VPN 服务

查看价格和免费使用

分享这篇文章