Docker Hub 拉不动镜像的五条出路:加速器、前缀替换、守护进程代理与自建缓存

· 约 4 分钟 🪞 国内镜像源

docker pull 卡在 pulling fs layer 十分钟不动、net/http: TLS handshake timeoutunexpected EOF——Docker Hub 直连的不稳定,叠加 2024 年年中公共加速器的批量关停,让「拉个镜像」成了国内开发环境的日常障碍。

出路不止一条,按见效速度从快到慢排成五条,各有适用面。开始之前先记住两个高频误区,后面反复用到:

  1. registry-mirrors 只对 Docker Hub(docker.io)生效——ghcr.io、quay.io、registry.k8s.io 的失败它救不了。
  2. 拉镜像的是 dockerd 守护进程——终端里 export http_proxy 对它完全无效。

出路一:镜像加速器(registry-mirrors)

最经典的路径:/etc/docker/daemon.json 写入 registry-mirrors 数组,systemctl restart docker

排错顺序固定:docker info 看底部有没有列出 Registry Mirrors——大量「配了没用」其实是没生效:daemon.json 写错路径(Docker Desktop 要在 Settings → Docker Engine 里改,改宿主机文件无效)、JSON 语法错误导致整个文件被忽略、改完没重启服务。

生效了还拉不动,就是加速器本身死了。公共加速器的存活状态变化很快,别恋战,换下一个——可用地址以国内镜像源速查维护的清单为准,这类信息写死在博客里过几个月就是错的。

出路二:前缀替换(免配置,救所有仓库)

加速器是透明代理;前缀替换则是显式改名,把镜像服务的域名直接写进镜像名:

docker pull docker.m.daocloud.io/library/nginx:alpine
# 拉完改回原名,compose/K8s 清单不用动
docker tag docker.m.daocloud.io/library/nginx:alpine nginx:alpine

注意官方镜像要补 library/ 命名空间。这条路的独特价值有两个:无需 root 改配置(临时救急、无权限环境);能救 registry-mirrors 覆盖不了的仓库——同类服务通常成套提供 ghcr.m.daocloud.iok8s.m.daocloud.io 等前缀。

代价是依赖具体的第三方服务。纪律是:前缀只出现在拉取命令里,Dockerfile 和 compose 落盘的仍写原始镜像名,用 tag 改名兜底——否则该服务一停服,全线构建跟着挂。

出路三:给守护进程配代理

有可用代理时的正解,但必须配到 daemon 上:

# /etc/systemd/system/docker.service.d/proxy.conf
[Service]
Environment="HTTPS_PROXY=http://<代理地址>:<端口>"
Environment="NO_PROXY=localhost,127.0.0.1,<内网registry域名>"

systemctl daemon-reload && systemctl restart dockerdocker info 出现 HTTP Proxy 字段即生效。Docker Desktop 在 Settings → Resources → Proxies 里填。

两个次生坑:NO_PROXY 漏掉公司内网 registry,会把内网推拉流量也送进代理,全挂;daemon 代理只管 pull/push,docker build 过程中容器内的 apt/pip 请求不继承它——构建内的下载用 build-arg 传代理,或直接换国内镜像源

出路四:云上生产环境——自建中转仓库

服务器在阿里云、腾讯云上时,别再依赖任何公共加速器:把需要的公共镜像同步进自己在该云的镜像仓库(ACR/TCR 都有免费个人版),生产机器同地域内网拉取。速度是内网级的,且彻底摆脱公网波动。

同步的三种姿势:本地拉了 tag 成仓库地址 push 上去;用镜像服务自带的海外构建/代理拉取能力;CI 里加一个 skopeo copy 的同步 job 批量搬运。

这条路的真正收益超出网络本身:镜像版本被自己的仓库固化,可审计、可回滚,部署不再依赖第三方存活状态——把「网络问题」升级成「供应链管理」,生产环境本来就该走到这一步

出路五:K8s 集群——containerd 是另一套配置

从 Docker 迁到 K8s 的团队最常见的失配:现代 K8s 节点的运行时是 containerd,不读 /etc/docker/daemon.json,按 Docker 的方法配自然没用。

containerd(1.5+)的推荐姿势:/etc/containerd/config.toml 里启用 config_path(通常指向 /etc/containerd/certs.d),然后按仓库建目录放 hosts.toml(如 certs.d/docker.io/hosts.toml 写镜像端点),重启 containerd。验证用 crictl pull 或看 kubelet 事件——节点上可能根本没有 docker 命令。

节点多的集群更推荐结构性方案:统一走出路四的中转仓库;或集群内自建 pull-through 缓存(Harbor 代理项目、registry:2 的 proxy 模式),全集群只有缓存节点需要出网,公网流量也省了。换仓库地址时记得同步 imagePullSecrets

选择建议

场景首选
个人机器临时拉一个前缀替换
个人/团队日常开发加速器 + 前缀替换兜底
有稳定代理daemon 代理
云上生产自建中转仓库
K8s 集群containerd 配置 + 中转仓库/缓存

配完发现速度还是不理想,瓶颈可能根本不在 registry——代理与镜像的关系、DNS 层的干扰,见换了国内镜像还是慢公共 DNS 的选择与污染诊断。拉下来的镜像要从 docker run 沉淀成可维护的编排文件,见docker run 与 compose 的参数对照

❓ 常见问题

配了镜像加速器还是拉不动,是哪里没配对?

先跑 docker info 看底部有没有 Registry Mirrors 一栏——大量「配了没用」其实是没生效逐项核对:(1) daemon.json 路径对不对——Linux 是 /etc/docker/daemon.json,Docker Desktop 要在 Settings → Docker Engine 的 JSON 里改,改宿主机文件无效;(2) JSON 语法——多个尾逗号、少个引号都会导致整个文件被忽略甚至 docker 起不来;(3) 改完必须 systemctl restart docker(daemon-reload 不够);(4) docker info 确认加速器已列出。都生效了还失败,则大概率是加速器本身失效——公共加速器可用性变化很快(2024 年年中多家高校与公共加速器停止服务后尤其如此),换下一个地址再试,可用清单以镜像源速查工具页维护的为准。最后确认失败面:只有 Docker Hub 拉不动,还是 ghcr.io 等也拉不动——registry-mirrors 只对 docker.io 生效,其他仓库失败属于另一个问题,走前缀替换或代理。

docker.m.daocloud.io 这类前缀替换是什么原理?和加速器有什么区别?

加速器是「透明代理」:配置一次,镜像名不变;前缀替换是「显式改名」:不用配置,把仓库域名直接写进镜像名用法:docker pull docker.m.daocloud.io/library/nginx:alpine——把 docker.io 换成镜像服务的对应前缀(官方镜像要补上 library/ 命名空间);同一家通常还提供 ghcr.m.daocloud.io、k8s.m.daocloud.io 等对应其他仓库的前缀,这是 registry-mirrors 覆盖不了 ghcr/quay/k8s 仓库时最省事的补法拉完改回原名,让 compose/K8s 清单不用改:docker tag docker.m.daocloud.io/library/nginx:alpine nginx:alpine。取舍:(1) 优点——无需 root 改配置、按次使用、能救所有仓库;(2) 缺点——镜像名写死在 Dockerfile/compose 里会造成对某个第三方服务的硬依赖,它一停服全线构建失败;建议只在拉取动作里用前缀,落盘的配置文件仍写原始镜像名 + tag 改名兜底

有代理了,为什么 docker pull 还是超时?

因为拉镜像的是 dockerd 守护进程,不是你的终端——shell 里的 http_proxy 环境变量对它完全无效,这是 Docker 代理问题的第一误区。Linux 正确配法:建 /etc/systemd/system/docker.service.d/proxy.conf,写入 [Service] 段的 Environment="HTTPS_PROXY=http://<代理地址>:<端口>" 和 NO_PROXY(把内网 registry、localhost 排除掉),然后 systemctl daemon-reload && systemctl restart docker,docker info 里能看到 HTTP Proxy 字段即生效。Docker Desktop:Settings → Resources → Proxies 里填,别改系统文件。两个次生坑:(1) NO_PROXY 漏配内网仓库,导致公司私有 registry 的流量也被推向代理,推拉全挂;(2) 代理只解决 pull,docker build 容器内的网络请求(apt/pip)不继承 daemon 代理,要用 build-arg 传入 HTTP_PROXY 或改用镜像源。:docker login 认证请求同样走 daemon,终端代理一样管不到。

服务器在阿里云/腾讯云上,拉镜像的最优解是什么?

用云厂商自家的容器镜像服务做「中转仓库」,内网拉取,快且稳定,这是生产环境的正解思路:Docker Hub 的公共镜像不直接拉,而是先同步到自己在该云的镜像仓库(阿里云 ACR、腾讯云 TCR 都有个人免费版),生产机器从同地域仓库内网拉取——速度是内网带宽级别的,且不受公网波动影响。同步的三种姿势:(1) 手动中转:本地或海外机器 pull 后 tag 成 ACR 地址 push 上去;(2) 用镜像服务自带的「海外源构建/镜像加速拉取」功能(部分云的 ACR 支持直接从 Docker Hub 构建或代理);(3) CI 里加一个同步 job,用 skopeo copy 批量搬运,保持 digest 不变。顺带的收益:生产部署不再依赖任何公共加速器的存活状态,镜像版本也被自己的仓库固化,可审计可回滚——把「网络问题」升级成了「供应链管理」,这本来就是生产环境该做的

K8s 节点拉镜像失败,按 Docker 的方法配了没用?

因为现代 K8s 节点的运行时是 containerd,不读 /etc/docker/daemon.json——这是从 Docker 迁移过来的集群最常见的失配。containerd 的配置:新版本(1.5+)推荐 config_path 方式——在 /etc/containerd/config.toml 里指定 registry 配置目录(通常 /etc/containerd/certs.d),然后为每个仓库建目录放 hosts.toml,例如 certs.d/docker.io/hosts.toml 里写 server 与镜像端点;改完 systemctl restart containerd。验证用 crictl pull 或直接看 kubelet 事件,而不是 docker pull——节点上可能根本没有 docker 命令。更省事的集群级方案:(1) 所有节点统一走上一条的云厂商中转仓库,部署清单里直接写自己仓库的地址;(2) 集群内自建 pull-through 缓存(Harbor 的代理项目或 registry:2 的 proxy 模式),全集群只有缓存节点需要出网——节点多的时候还能大幅省公网流量。imagePullSecrets 记得同步:换了仓库地址,凭据也要指向新仓库。

🪞 打开 国内镜像源 pip/npm/Ubuntu/Docker 等 17 个工具·清华/阿里/华为/腾讯 8 大源·一键复制配置命令

📖 同一工具的其他教程

🔗 相关阅读

全部教程 →