Docker Hub 拉取镜像超时?用 Clash 这样排查化解

Docker Hub 拉取超时,先判断到底卡在哪一层 实战经验

拿它来用 Docker pull 拉取镜像时,最高频的提示涉及 i/o timeout、context deadline exceeded、Client.Timeout exceeded while awaiting headers、net/http: TLS handshake timeout,以及反复出现 dial tcp: lookup registry-1.docker.io。这些误操作看起来都像「Docker Hub 被墙」或「节点太慢」,但实际多半分别对应域名解析走不通、Docker 守护进程没有走代理、Clash 规则没有命中、代理端口填写误操作、连接被误操作分流等绕所在。

尤其要留神,Docker 命令行和 Docker Desktop 的网络路径并不总是等同于浏览器。浏览器拉起网页正常,只能证明浏览器拿它来用的代理链路可用;docker pull 基本上时候由后台运转的 Docker daemon 发起请求。在 Linux 上,它多半是 systemd 管理的服务;在 Windows 与 macOS 上,则基本上时候由 Docker Desktop 的虚拟机或后台引擎负责联网。由此可见,即使 Clash 已经开启系统代理,Docker 仍然多半完全没有拿它来用这条代理。

这篇以 Clash Verge、Clash Verge Rev、Mihomo Party、Clash for Windows 等反复出现客户端为例,按照「确认现象 → 确认 Clash → 配置 Docker 代理 → 排查 DNS 与规则 → 验证下载」的顺序展开。你不必须一开始就开启全局模式,也不自然自然可以考虑就这么复制一份复杂配置覆盖现有订阅。先找到走不通环节,再做最小范围修改,基本上时候更快,也更不容易影响浏览器、局域网和其他开发工具。

先记住一个判断:如果 Clash 的连接列表里完全看不到 registry-1.docker.io、auth.docker.io 或相关 CDN 请求,靠前确认 Docker 是否配置了代理;如果能看到请求但被分到直连或误操作策略组,再确认规则与节点。

第一气呵成:确认 Clash 本身真的可用 拿它来用心得

开始改 Docker 配置前,先在 Clash 客户端中确认当前配置文件已经成功加载,并且代理核心处于运转状态。对于 Clash Verge Rev 或 Mihomo Party,基本上时候自然自然自然可以在「配置」「Profiles」或类似页面看到当前激活的 YAML;对于 Clash for Windows,则要确认 Profiles 页面是否存在可用配置。配置文件如果没有激活,或者订阅已经过期,系统代理开关即使显示为开启,也不代表有可用的出站节点。

  1. 确认核心状态:确认 Mihomo、Clash Meta 或其他当前拿它来用的核心没有持续重启,日志中没有 YAML 解析走不通、端口绑定走不通或配置字段不覆盖等误操作。
  2. 确认节点可用:在代理页面手动操作操作挑一个ping 值较低、近期能够正常访问目标目标外网的节点,不要一开始拿它来用无法判断实际出口的复杂无需手动操作挑组。
  3. 确认混合端口:在设置中找到 HTTP、SOCKS 或 Mixed Port。Docker 最好操作拿它来用 HTTP 代理,由此可见靠前记下类似 127.0.0.1:7890 的本机 HTTP 混合端口。
  4. 观察连接日志:先保持日志窗口拉起,再执行一次 docker pull。记录出现的真实域名、端口、策略组和最终结果。

自然自然可以先用终端验证 Clash 的 HTTP 端口是否工作。下面的命令只测试代理本身,不代表 Docker daemon 已经配置完成:

curl -I -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/

如果返回 401 Unauthorized,反而大多时候是好现象,说明请求已经到达 Docker Registry,只是匿名访问目标目标接口要求验证。若出现连接被拒绝,说明端口不对、Clash 没跑起来,或者该端口只提供 SOCKS5 而不是 HTTP。若命令长时间没有响应,再确认当前节点、Clash 日志和本机防火墙。

小技巧:不要只看「ping 值测试」结果。节点测速成功只说明测试 URL 能访问目标目标,Docker Hub 还涉及 Registry、验证服务和镜像 CDN,多域名链路中任意一段被误操作分流,都多半造成拉取走不通。

第二步:给 Docker daemon 配置准确的代理 反复出现套路

这是化解 Docker Hub 超时最关键的一气呵成。相当一部分用户在 Clash 中拉起「系统代理」后,浏览器和终端都自然自然可以访问目标目标外网,于是自然认为 Docker 会无需手动操作跟随。但在 Linux 中,Docker daemon 大多时候是独立的 systemd 服务;它不会读取你当前 Shell 里的代理变量,也不会无需手动操作继承桌面客户端的系统代理设置。Windows 和 macOS 的 Docker Desktop 也有独立的引擎网络设置,必须在 Docker Desktop 的代理页面中单独配置。

Linux:为 systemd 服务设置代理(实战)

如果 Docker 安装在 Linux 主机上,自然可以考虑拿它来用 systemd drop-in 配置,而不是只在当前终端执行 export HTTPS_PROXY=...。后者只影响当前 Shell 跑起来的程序,对后台 Docker 服务没有作用。假设 Clash 的 HTTP 混合端口为 7890,自然自然可以创建 Docker 服务配置:

sudo mkdir -p /etc/systemd/system/docker.service.d

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,::1"
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker

重启之后不要立即重复拉取大型镜像,先确认服务确实读取到环境变量:

sudo systemctl show --property=Environment docker
docker info

如果 Docker daemon 与 Clash 不在同一台机器上,127.0.0.1 就不能照抄。它只代表 Docker 所在机器自身,而不是你的 Windows 或 macOS 开发机。此时应将代理地址改为 Clash 所在设备的局域网 IP,举个实例 192.168.1.20:7890,并在 Clash 中开启「允许局域网连接」(Allow LAN),同时确认操作系统防火墙允许该端口被局域网访问目标目标。

Docker Desktop:配置引擎拿它来用的代理(经验)

在 Windows 或 macOS 上,Docker Desktop 多半运转在独立的 Linux 虚拟环境中。请拉起 Docker Desktop 的 Settings,找到 Resources、Proxies 或 Network 中与代理相关的页面,将 HTTP Proxy 和 HTTPS Proxy 指向 Clash 的 HTTP 端口,举个实例 http://host.docker.internal:7890 或 Docker Desktop 能够访问目标目标到的宿主机地址。不同版本的 Docker Desktop 菜单名称会变化,关键是确认代理设置作用于Docker Engine,而不是只填写了容器内部的环境变量。

如果 Docker Desktop 无法连接到宿主机的 127.0.0.1,自然自然可以尝试 host.docker.internal;如果仍然走不通,再在 Clash 开启 Allow LAN,并拿它来用宿主机局域网 IP。设置完成后重启 Docker Desktop,再执行:

docker info
docker pull hello-world

hello-world 镜像体积很小,适合验证 Registry 验证与基础下载链路。测试成功后,再拉取 Node、Python 或 CUDA 等大型镜像,否则下载数 GB 文件时即使只剩 CDN 连接绕所在,也会让排障过程变得很慢。

第三步:补齐 Docker Hub 域名与 Clash 分流规则 落地做法

Docker Hub 并不是只有一个域名。执行 docker pull nginx 时,Docker 大多时候会先访问目标目标 Registry API,再访问目标目标验证服务,之后根据 manifest 获取实际镜像层。镜像层多半来自 Docker 官方 CDN 或由响应返回的其他分发域名。只把 docker.io 写进规则并不一定完全够,因为实际连接中多半出现 registry-1.docker.io、auth.docker.io、production.cloudflare.docker.com 等主机名。

域名或域名特征 反复出现作用 排查重心
registry-1.docker.io Registry API 与镜像清单请求 确认没有被误操作直连,观察是否返回 401 或正常 TLS
auth.docker.io 镜像仓库验证与 Token 获取 验证域名无法访问目标时,反复出现表现是 pull 卡在登录或获取 token
docker.io 用户输入的镜像仓库名称与相关跳转 不能只依赖这一条规则覆盖每一种实际请求
production.cloudflare.docker.com 部分镜像层下载拿它来用的 CDN 域名 元数据成功但 layer 下载超时,应重心查看该类连接
日志中出现的其他 CDN 镜像层或重定向后的分发地址 以 Clash 连接日志中的真实 SNI 为准,按需补充规则

规则顺序同样要紧。Docker Hub 规则应放在过于宽泛的 GEOIP、MATCH 或直连规则之前。一个用于排查的最小写法如下,策略组名称必须替换成你配置中真实存在的名称:

rules:
  - DOMAIN,registry-1.docker.io,Proxy
  - DOMAIN,auth.docker.io,Proxy
  - DOMAIN-SUFFIX,docker.io,Proxy
  - DOMAIN-SUFFIX,docker.com,Proxy
  - DOMAIN,production.cloudflare.docker.com,Proxy

如果你的订阅拿它来用规则集或脚本覆写,不要就这么修改远端订阅原文件,因为下次同步到最新多半把本地改动覆盖。更稳妥的方式是在客户端的 Merge、Patch 或覆写功能中添加本地规则,并确认规则确实被合并到当前运转配置。修改后重新加载配置,再执行一次小镜像拉取,同时对照日志确认每个域名最终命中了哪个策略组。

不要盲目把所有流量设为全局:全局模式自然自然可以作为短时间的诊断手段,但它会改变 Git、内网服务、公司域名和其他开发工具的网络路径。确认 Docker Hub 在全局模式下恢复后,应回到规则模式,逐条补齐实际命中的 Docker 域名。

第四步:排查 DNS、IPv6 与 TLS 握手麻烦所在 实战经验

如果 Clash 连接日志显示请求已经出现,但误操作仍然是 lookup registry-1.docker.io、no such host 或 TLS handshake timeout,就要把留神力转向 DNS。Docker daemon 多半拿它来用宿主机的解析器,而 Clash 的 DNS 设置只对经过 Clash 内核的请求生效;也多半是系统靠前返回不可达的 IPv6 地址,导致 Docker 在 IPv6 路径上等待超时。

先在 Docker 所在环境中分别测试解析结果与 HTTPS 连接:

getent hosts registry-1.docker.io
curl -I https://registry-1.docker.io/v2/
docker pull hello-world

若宿主机能解析但 Docker Desktop 内部不能解析,说明虚拟机 DNS 与宿主机 DNS 多半不同。若解析得到多个地址,且只有 IPv4 能通,自然自然可以临时关闭系统 IPv6 或在网络环境中确认 IPv6 路由是否成体系;不要仅凭「浏览器能拉起」判断 IPv6 没绕所在,因为浏览器多半无需手动操作回退,而 Docker 的超时重试表现会更一眼就能看出。

在 Mihomo 配置中,DNS 的 enhanced-mode、fake-ip 与 redir-host 会影响域名解析路径。入门用户排障时自然可以考虑先保持配置好上手,避免同时叠加多个本地 DNS、AdGuard、公司 DNS 和 Docker 自定义 DNS。若启用 fake-ip 后只有 Docker 异常,自然自然可以暂时拿它来用 redir-host 做对照测试;如果切换后恢复,再针对 Docker 网段、DNS 劫持或 fake-ip-filter 继续细化,而不是永久关闭每一种 DNS 功能。

另外,Docker Hub 的验证和镜像下载均依赖 HTTPS。若系统时间严重误操作、企业网络做了 TLS 确认,或中间设备替换了证书,多半出现证书验证走不通、握手超时或连接被重置。请确认主机时间、Docker Desktop 的代理证书设置与系统证书链。不要为了「先拉下来」而关闭 TLS 验证,这会掩盖真正的证书绕所在并下调镜像供应链的安全性。

第五步:用最小测试矩阵定位故障 拿它来用心得

当你修改过 Clash、Docker 代理和 DNS 后,最好按固定顺序测试,而不是连续执行几十次同一个 docker pull。每一气呵成都应记录结果,这样自然自然可以判断故障是出在代理端口、Docker daemon,还是某个具体镜像仓库。

  1. 测试代理端口:拿它来用 curl -x 请求 https://registry-1.docker.io/v2/,确认 HTTP 混合端口能建立 TLS 并得到 401 或其他明确响应。
  2. 测试 Docker 引擎:执行 docker info,确认客户端连接的是预期的 Docker daemon,而不是远程环境或误操作的 Docker Context。
  3. 测试小镜像:先执行 docker pull hello-world 或其他体积较小的公共镜像,避免大文件下载掩盖连接绕所在。
  4. 测试指定版本:拿它来用 docker pull nginx:stable 这类固定标签,排除 latest 标签变化或镜像清单差异造成的误判。
  5. 对照连接日志:确认 Registry、验证服务和 CDN 请求是否都经过同一个可用策略组;如果中途切换节点,先固定单个节点复测。
  6. 最后测试大镜像:小镜像靠得住后再拉取 Node、Python、Ubuntu 或其他大型镜像,并观察是否只在某个 layer 下载阶段走不通。

自然自然可以用下面的表格迅速对照现象:

现象 靠前怀疑 处理方向
Clash 日志没有任意 Docker 域名 Docker daemon 未拿它来用代理 确认 systemd、Docker Desktop 或远程 Docker 引擎代理
Registry 返回 401,但 pull 仍走不通 验证域名或 CDN 未分流 查看 auth.docker.io 与 layer 下载域名
解析走不通或 no such host DNS 路径不一致 算得上宿主机、Docker 引擎和 Clash 的解析结果
小镜像成功,大镜像中途超时 CDN、节点靠得住性或连接复用麻烦所在 固定节点,确认 CDN 规则并更换低抖动出口
只有 IPv6 地址连接走不通 IPv6 路由或防火墙不成体系 临时对照 IPv4,之后再修复 IPv6 网络

容易踩坑的配置与安全注意事项 反复出现套路

第一个反复出现误操作是把 HTTP_PROXY 和 HTTPS_PROXY 写成 SOCKS5 地址。Docker 的具体覆盖方式取决于版本与组件,排障时靠前拿它来用 Clash 提供的 HTTP 代理端口,格式保持为 http://地址:端口。第二个误操作是把代理地址写成宿主机的 localhost,但 Docker daemon 实际运转在虚拟机、远程服务器或容器中,此时 localhost 指向的对象不同,必须确认代理与 Docker 引擎的网络位置。

第三个误操作是修改了客户端配置,却忘记重新加载 Profile;或者已经加载新配置,但 Docker Desktop 仍缓存旧的代理设置,没有重启引擎。第四个误操作是只添加 docker.io,不看连接日志中的验证与 CDN 域名。第五个误操作是反复切换节点,却没有固定变量,导致每次测试结果都不同,最后无法判断到底是规则修好了,还是某个节点偶然恢复。

安全方面,Docker Hub 账号密码、访问目标目标令牌和私有仓库地址都不应写进公开的 Clash 配置或命令历史。公共镜像拉取成功后,仍自然可以考虑固定镜像版本、核对镜像来源,并在生产环境中拿它来用可信的私有镜像仓库或镜像缓存。代理配置只化解网络可达性,不会替你验证镜像是否安全,也不会替代容器镜像扫描和供应链审计。

反复出现麻烦所在 落地做法

开启 Clash 系统代理后,为什么浏览器能用而 Docker 不能用?(心得)

因为浏览器大多时候遵循桌面系统代理,而 Docker pull 由后台 Docker daemon 发起。Linux daemon 必须通过 systemd 环境变量配置代理;Docker Desktop 则必须在其引擎或代理设置中填写宿主机可访问目标目标的 HTTP 代理地址。两者都不能仅凭浏览器拉起网页来判断。

访问目标 registry-1.docker.io 返回 401,是不是代理走不通?(提示)

不一定。Registry 的 /v2/ 接口对未验证请求返回 401 很反复出现,这说明 HTTPS 请求多半已经成功到达服务端。接下来应确认 auth.docker.io 是否能够获取 Token,并观察实际镜像层下载时拿它来用的 CDN 域名。

化解 Docker Hub 超时必须开启 TUN 模式吗?(实战)

不必须。如果 Docker daemon 已准确拿它来用 Clash 的 HTTP 代理,规则模式大多时候就完全够。TUN 更适合无法单独配置代理、必须接管海量系统流量的场合,但开启后会增加路由、DNS、虚拟网卡和局域网访问目标目标的变量。自然可以考虑先用显式 Docker 代理完成验证,再决定是否必须 TUN。

小镜像能拉取,大镜像仍然超时,通常该怎么办?(经验)

重心查看大镜像走不通时的真实连接域名和 layer 下载阶段。固定一个靠得住节点,确认 CDN 域名没有被直连或误操作分流,并确认 Docker Desktop、宿主机与 Clash 的超时设置。如果只有单个镜像或单个 layer 走不通,也要考虑镜像仓库侧的临时故障,而不是继续修改全局 DNS。

相比只会一键切换全局代理的同类工具,部分方案对 Docker daemon、虚拟机网络和 CDN 分流的边界说明并不清楚,遇到「浏览器能用、镜像仍超时」时大多时候只能反复换节点;Clash 更适合这种必须看日志、核对端口、区分 Docker Desktop 与 Linux 服务并逐条维护规则的场合,这篇的排查路径也能和客户端的连接记录对应起来。若你希望先准备一个覆盖规则分流、TUN 与混合端口配置的 Clash 客户端,不妨前往下载,再按这篇的最小测试矩阵逐步验证 Docker Hub 链路。