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 页面是否存在可用配置。配置文件如果没有激活,或者订阅已经过期,系统代理开关即使显示为开启,也不代表有可用的出站节点。
- 检查核心状态:确认 Mihomo、Clash Meta 或其他当前使用的核心没有持续重启,日志中没有 YAML 解析失败、端口绑定失败或配置字段不支持等错误。
- 确认节点可用:在代理页面手动选择一个延迟较低、近期能够正常访问外网的节点,不要一开始使用无法判断实际出口的复杂自动选择组。
- 确认混合端口:在设置中找到 HTTP、SOCKS 或 Mixed Port。Docker 最方便使用 HTTP 代理,因此优先记下类似
127.0.0.1:7890的本机 HTTP 混合端口。 - 观察连接日志:先保持日志窗口打开,再执行一次
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 日志和本机防火墙。
小技巧:不要只看「延迟测试」结果。节点测速成功只说明测试 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,还是某个具体镜像仓库。
- 测试代理端口:使用
curl -x请求https://registry-1.docker.io/v2/,确认 HTTP 混合端口能建立 TLS 并得到 401 或其他明确响应。 - 测试 Docker 引擎:执行
docker info,确认客户端连接的是预期的 Docker daemon,而不是远程环境或错误的 Docker Context。 - 测试小镜像:先执行
docker pull hello-world或其他体积较小的公共镜像,避免大文件下载掩盖连接问题。 - 测试指定版本:使用
docker pull nginx:stable这类固定标签,排除 latest 标签变化或镜像清单差异造成的误判。 - 对照连接日志:确认 Registry、认证服务和 CDN 请求是否都经过同一个可用策略组;如果中途切换节点,先固定单个节点复测。
- 最后测试大镜像:小镜像稳定后再拉取 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 链路。