在开发机、NAS 或 CI 环境里执行 docker pull 时,如果终端反复出现 i/o timeout、context deadline exceeded、net/http: TLS handshake timeout,或者卡在 Pulling fs layer 很久,问题不一定是代理节点速度慢。Docker Hub 超时往往发生在多个环节之间:Docker 守护进程是否经过 Clash、TUN 是否真正接管了容器宿主机的流量、DNS 是否返回了可用地址、规则是否把 Registry 请求误判成 DIRECT,以及认证服务与镜像加速域名是否使用了不同出口。本文以 Clash Meta/Mihomo 内核常见能力为基础,围绕Clash TUN 模式、DNS、规则匹配和 Docker 守护进程四个层面,给出一套可重复的排障流程。
先说明边界:本文讨论的是合法的网络连通性配置,不提供任何绕过组织安全策略或访问受限制资源的方法。你需要准备一台已经安装 Docker 的主机、一个能正常使用的 Clash 配置,以及能够查看连接日志或 Dashboard 的客户端。示例中的端口使用常见的 7890,如果你的 mixed-port、HTTP 代理端口或 TUN 配置不同,请以实际监听端口为准。
Docker Hub 超时的常见现象:先分清卡在哪一层
不同错误信息对应的故障位置并不相同。执行 docker pull 时,如果连镜像仓库的元数据都无法获取,通常会看到 failed to resolve source metadata、TLS handshake timeout 或 Client.Timeout exceeded while awaiting headers。这说明请求可能还没有进入真正的分层下载阶段,优先检查 DNS、Registry 连接和代理路径。若元数据已经成功获取,只有某些 layer 下载中断,则要进一步关注节点带宽、连接复用、并发下载和 Docker 的读取超时。
- 解析阶段失败:主机无法稳定解析
registry-1.docker.io、auth.docker.io或返回了不可达地址。 - TLS 阶段失败:TCP 连接已经建立,但证书握手长时间无响应,常见于请求被错误地直连或链路质量不稳定。
- 认证阶段失败:Registry 可以连接,但获取 Bearer Token 时访问
auth.docker.io超时、403 或连接被重置。 - 分层下载阶段失败:manifest 能打开,某些大文件下载缓慢或中途断开,可能是节点带宽、出口策略或 Docker 并发连接造成。
- 仅容器内失败:宿主机上的
curl正常,但容器或 Docker daemon 失败,说明容器网络、守护进程环境变量或 TUN 路由没有覆盖到实际进程。
排查时不要只在浏览器里打开 hub.docker.com。Docker pull 通常会涉及多个主机名,浏览器能访问网页并不能证明 Docker 的 Registry API、认证端点和内容分发节点都走了同一条链路。建议在 Clash 连接面板中搜索 docker、registry、auth,观察每个请求最终命中的策略。如果日志里完全没有相关连接,问题多半出在 Docker 流量没有进入 Clash;如果能看到连接但策略是 DIRECT,则先修正规则,而不是急着更换订阅。
Docker pull 的真实链路:代理到底要接管谁
Docker 的一个关键特点是:执行 docker pull 的终端只是客户端,真正发起外网请求的通常是后台运行的 Docker daemon。在 Linux 上,它可能是 dockerd 系统服务;在 macOS 和 Windows 上,Docker Desktop 会运行一个 Linux 虚拟机,实际请求可能从虚拟机内部发出。因此,即使你在当前 shell 中设置了 HTTPS_PROXY,也不代表 Docker daemon 会自动继承这些变量。
可以把链路拆成四段理解:第一段是 Docker CLI 将拉取请求交给 daemon;第二段是 daemon 解析 Registry 与认证域名;第三段是 daemon 按自身网络配置访问外网;第四段才是请求进入 Clash 的 HTTP 代理、SOCKS 代理或 TUN 虚拟网卡。任何一段配置不一致,都可能出现「浏览器正常、curl 正常、docker pull 超时」的现象。
| 检查对象 | 需要确认的内容 | 典型误区 |
|---|---|---|
| 当前终端 | HTTPS_PROXY、HTTP_PROXY 是否存在 |
只设置终端变量,却没有设置 daemon |
| Docker daemon | 服务配置是否包含代理环境变量 | 重启 Docker 后变量被清空 |
| Docker Desktop | Resources 或 Proxies 页面是否有独立设置 | 把宿主机代理误认为虚拟机自动继承 |
| Clash | mixed-port、TUN、DNS 与连接日志 | 端口写错,或只开放了 SOCKS 端口 |
| 规则集 | Registry、认证和 CDN 请求的策略 | 只添加一个 Docker 域名,遗漏认证主机 |
如果你使用的是 Linux 主机,并且 Docker daemon 通过 systemd 启动,可以为服务增加代理环境变量;修改后必须执行 daemon-reload 并重启 Docker。示例中的端口和地址仅用于说明配置形态:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/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 daemon 能访问宿主机上的 Clash 监听地址。若 Clash 跑在另一台机器、旁路由或 Docker Desktop 的虚拟机之外,127.0.0.1 就不再代表运行 Clash 的那台主机。此时应改用局域网地址,并在 Clash 的监听设置与防火墙中允许来自 Docker 所在网络的访问。不要为了省事把代理端口暴露到公网;局域网监听也应配合访问控制。
Clash TUN、DNS 与规则:避免 Docker 流量走错出口
TUN 模式的价值在于通过虚拟网卡和路由表接管不理解 HTTP 代理的程序。对于 Docker daemon、某些静态编译的二进制和虚拟机内进程,TUN 通常比单纯打开系统代理更有覆盖力。但 TUN 并不是「开启后所有流量必然代理」:它仍受路由、DNS、绕过网段、进程所在网络命名空间以及 Clash 规则影响。尤其是在 Docker、VPN、ZeroTier、公司安全客户端同时存在时,多个虚拟网卡可能争夺默认路由。
建议先采用规则模式 + 精确域名策略,再按实际需要启用 TUN。Docker Hub 常见的关键域名包括 registry-1.docker.io、auth.docker.io 和 hub.docker.com;镜像层下载还可能跳转到内容分发网络,不能假设所有请求都会停留在一个固定域名下。部分企业 Registry、私有 Harbor 或内网镜像则应明确设为 DIRECT 或指定企业代理,不能把所有 docker.io 相关流量一律送往同一策略。
规则顺序尤其重要。自定义 Docker 规则应放在过宽的 GEOIP、FINAL 或「国内直连」规则之前,否则前面的规则已经作出决定,后面新增的条目不会生效。可以在配置覆写区加入类似以下的域名规则,并将 DOCKER_PROXY 替换为你自己的策略组:
rules:
- DOMAIN,registry-1.docker.io,DOCKER_PROXY
- DOMAIN,auth.docker.io,DOCKER_PROXY
- DOMAIN-SUFFIX,docker.io,DOCKER_PROXY
- DOMAIN-SUFFIX, docker.com,DOCKER_PROXY
- MATCH,DIRECT
上面的示例只用于展示规则关系,实际 YAML 中不要在域名字段后留下多余空格,也不要盲目复制到已有配置底部。更稳妥的方式是先用 Dashboard 查看真实请求主机名,再添加最小范围的 DOMAIN 或 DOMAIN-SUFFIX。如果某个企业内部 Registry 使用 registry.example.internal,应将它放在 Docker 公网规则之前并指定直连或内网策略,避免内网镜像被送到外部节点。
DNS 是另一个高频故障点。Clash 的 fake-ip、redir-host、系统 DNS 和 Docker 内置 DNS 可能形成多层解析链。如果宿主机解析得到的地址可用,但 Docker 容器使用了硬编码的运营商 DNS,容器内请求可能绕过 Clash;反过来,fake-ip 地址被某个组件当作真实公网地址使用,也会导致连接异常。排查时应分别在宿主机、容器内和 Docker daemon 所在环境检查解析结果,不要只执行一次 nslookup 就下结论。
动手排障:从最小测试逐层定位 Docker Hub 超时
下面的顺序适合 Linux、macOS 和 Windows 主机,命令需要按系统替换。核心思想是每次只改变一个变量,并记录结果。不要同时更换节点、切换 TUN、修改 DNS 和重装 Docker,否则即使问题暂时消失,也很难知道真正有效的是哪一步。
第一步:确认 Clash 端口和策略组状态
在 Clash 客户端设置页确认 mixed-port 或 HTTP 端口确实处于监听状态,记下端口号和当前策略组。使用浏览器访问一个你确定需要代理的站点,随后在连接面板里确认请求出现并命中预期节点。若 Clash 本身没有启动、端口被其他程序占用,后续所有 Docker 测试都会失去意义。
第二步:在宿主机用 curl 分别测试直连与代理
先测试宿主机的 DNS 与 HTTPS,再显式指定 Clash HTTP 代理。两个结果可以帮助你判断「主机网络本身不通」还是「代理没有被 Docker 使用」。不要把 HTTP 状态码 401 直接当作网络失败;对于 Registry 认证接口,能快速返回 401 反而说明连接链路已经打通。
curl -I --connect-timeout 10 https://registry-1.docker.io/v2/
curl -I --connect-timeout 10 \
-x http://127.0.0.1:7890 \
https://registry-1.docker.io/v2/
curl -I --connect-timeout 10 \
-x http://127.0.0.1:7890 \
https://auth.docker.io/token
测试期间观察 Clash 连接列表。如果第二条命令可以在面板中看到 registry-1.docker.io,而第一条没有记录或直接失败,说明节点和 Clash 基本可用,问题更可能位于 Docker daemon 的代理配置。如果两条都失败,则应继续检查 DNS、节点质量、规则命中和 TLS 错误,而不是先调整 Docker 并发。
第三步:确认 daemon 看到的代理设置
在 Linux systemd 环境中,可以用 systemctl show 查看服务环境变量;在 Docker Desktop 中,则要查看应用内的代理设置和网络诊断信息。修改配置后重启 daemon,再执行一次拉取测试。特别注意大小写:许多程序同时识别大写和小写变量,但不要假设所有运行时行为完全一致。NO_PROXY 中应保留本机、内网 Registry 和公司域名,避免内部资源被错误代理。
如果 daemon 连接面板完全没有请求,而宿主机 curl 有请求,基本可以确认 Docker 没有使用相同的出口。此时不要只在当前终端反复执行 export HTTPS_PROXY,因为那通常只影响 Docker CLI 或当前 shell,不会改变已经运行的后台服务。
第四步:确认 TUN 是否覆盖实际网络命名空间
如果你不想给 Docker daemon 配置显式 HTTP 代理,可以尝试让 TUN 接管宿主机或 Docker Desktop 虚拟机的流量。但要确认 TUN 的自动路由、严格路由、绕过局域网和 DNS 设置没有互相冲突。开启 TUN 后,先测试宿主机,再测试简单容器,最后才执行大镜像拉取。若宿主机请求有 Clash 日志、容器请求没有,说明容器网络或虚拟机路径没有被 TUN 覆盖。
容器内部可以用临时工具镜像测试 DNS 与 HTTPS;如果基础镜像本身无法拉取,可在已有镜像或宿主机网络命名空间中完成验证。不要把「容器能 ping 某个 IP」作为成功标准,因为 ICMP 通畅不代表 HTTPS、SNI、认证和大文件下载都正常。Docker Hub 的问题应使用相同协议、相同域名和相近的连接方式验证。
第五步:区分节点带宽与规则问题
当元数据和认证都正常,只有大 layer 下载缓慢时,才值得检查节点带宽、晚高峰拥塞以及连接数。可以换一个体积较小的公开镜像进行对照,并在同一时间观察连接面板里的下载速率、重连次数和策略组切换。如果小镜像稳定、大镜像超时,可能是节点对长连接或大文件传输不友好;如果所有请求都在握手阶段超时,则仍应回到规则和 DNS 层。
Docker daemon 的超时时间可以适度调整,但这只能缓解慢而不能修复错误路由。把超时时间无限增大,会让失败请求占用连接和线程更久,最终表现为更多并发拉取任务一起卡住。工程上更合理的做法是先固定可用出口,再根据镜像大小和节点质量设置合理的重试与超时策略。
最容易忽略的配置错误与修复思路
第一种错误是把 127.0.0.1:7890 写入了 Docker Desktop 或远程 Docker 主机的配置。对远程 daemon 来说,这个地址指向远程机器自身,而不是你桌面上的 Clash。第二种错误是 Clash 只监听本机回环地址,Docker 虚拟机或局域网设备无法访问。第三种错误是同时启用多个 VPN、TUN 和透明代理,路由表随连接顺序变化,导致同一域名时好时坏。
第四种错误是只给 registry-1.docker.io 加了规则,却遗漏 auth.docker.io。这种情况下,Docker 看似已经找到镜像仓库,但在获取令牌时失败。第五种错误是把所有 Docker 流量都设为全局代理,导致公司私有 Registry、内网 DNS 和本地 Harbor 也被送出外部节点。规则应体现网络边界:公网 Registry 使用代理,企业域名使用内网策略,本地地址保持直连。
还有一种容易被误判的情况是镜像仓库返回 429、401 或 403。它们属于应用层响应,不等于 Clash 超时。看到这些状态码时,应检查登录状态、镜像拉取限制、凭据助手和仓库权限;只有在连接面板显示 TLS 卡住、连接重置或长时间无响应时,才把重点放在 TUN 和代理链路上。把所有错误都归结为「节点不行」,会让排障方向越来越偏。
常见问题:Docker Hub、TUN 与 Clash 配置
只打开 Clash 系统代理,为什么 docker pull 仍然超时?
系统代理主要影响遵循系统代理设置的应用,而 Docker daemon 通常作为独立后台服务运行,未必读取桌面系统代理。Linux 上需要为 systemd 服务配置环境变量;Docker Desktop 则应检查其应用内代理设置,或者使用能够覆盖虚拟机流量的 TUN。确认方式不是看系统代理图标,而是在 Clash 连接面板中观察 Docker 请求是否真实出现。
开启 TUN 后,是否还需要给 Docker 配置 HTTPS_PROXY?
不一定。若 TUN 已经正确接管 Docker daemon 所在的网络路径,显式 HTTP 代理可以省略;但 Docker Desktop、远程 daemon、虚拟机和复杂旁路由环境未必会被同一 TUN 覆盖。对于服务器上的 Docker,明确配置 daemon 代理通常更容易审计和复现;对于本机上不支持代理变量的工具,TUN 则更有价值。
registry-1.docker.io 能访问,但 auth.docker.io 超时,应该怎么办?
这通常说明规则覆盖不完整,或两个域名命中了不同策略。先在 Clash 日志里确认认证请求的实际主机名,再为 auth.docker.io 添加明确规则,并确认它位于 FINAL、GEOIP 或宽泛直连规则之前。修正规则后重新登录并测试,不要只重复执行 pull。
只有大镜像的 layer 下载超时,是不是一定要换节点?
不一定。先确认元数据、认证和小镜像是否稳定,再观察大文件连接是否频繁重置。如果请求一直命中正确代理,但节点在大流量或长连接下明显降速,换一个带宽和高峰表现更好的节点可能有效;如果策略偶尔跳到 DIRECT,换节点并不能修复规则。还应检查 Docker daemon 的并发下载、磁盘写入和本机防火墙,避免把本地资源瓶颈误认为跨境链路问题。
与只依赖系统代理的通用 VPN 或只在浏览器层生效的扩展相比,Clash 在 Docker 场景中的优势并不是简单地「速度更快」,而是可以把 规则命中、TUN 接管、DNS 解析和连接日志放在同一套可观察链路里:例如把公网 Registry 与认证域名送入代理,把公司 Harbor 保留内网直连,再通过面板确认每个请求的实际策略。这样做比反复切换全局模式更容易复现,也比只修改 Docker 超时时间更接近问题根因。如果你正在寻找一套能覆盖命令行、Docker daemon 与多种网络路径的代理工具,可以先从适合自己平台的 Clash 客户端开始配置。