在 Linux 开发机、家用服务器或企业内网中执行 docker pull 时,最常见的失败并不是 Dockerfile 写错,而是 Docker Engine 到 Docker Hub 的出站请求没有稳定经过代理。典型表现包括镜像清单获取超时、context deadline exceeded、TLS 握手卡住、registry-1.docker.io 无法连接,或者浏览器可以访问 Docker Hub,但终端里的 Docker 服务始终失败。原因在于 Docker Engine 通常由 systemd 以独立服务运行,它不会自动继承桌面端 Clash 的系统代理,也不会因为宿主机浏览器已经能访问外网,就自然获得相同的路由。
本文以 Mihomo 内核为基础,围绕 Clash TUN 模式代理 Docker Hub 这一场景,说明透明代理、DNS 劫持、规则分流和 Linux 路由之间的关系。你将看到一套适合排查和改造的 YAML 思路,以及 Docker Engine、容器网络和企业网关环境中容易被忽略的细节。文中的端口、网段和策略组名称都可以替换为你的实际配置;请先确认你有权在当前设备和网络中使用代理,并遵守所在地区、单位或云平台的网络政策。
Docker pull 为什么不能简单等同于浏览器代理
浏览器代理正常,并不代表 Docker 已经走代理。浏览器通常读取系统代理设置,或者使用应用内部的代理配置;Docker CLI 则只是向本机 Docker Engine 发出请求,真正访问 Docker Hub 的进程往往是后台运行的 dockerd。如果 Docker Engine 位于 Linux 主机上,它可能使用自己的 systemd 环境、默认网关和 DNS 设置,完全绕过了 Clash Verge 或其他桌面客户端的用户态代理端口。
Docker Hub 的一次拉取操作也不只有一个域名。客户端可能先访问认证服务,再访问镜像仓库、对象存储或 CDN。常见目标包括 registry-1.docker.io、auth.docker.io、index.docker.io 以及镜像层下载过程中出现的第三方存储域名。只给一个域名添加规则,往往只能解决登录或清单请求,镜像层下载仍然会在后续阶段超时。
因此排障时不要只测试网页首页。应当把问题拆成三层:第一层是 Docker Engine 能否向外建立 TCP 和 TLS 连接;第二层是 DNS 是否返回了可用结果,并且没有被错误的本地解析污染;第三层是 Mihomo 连接面板中实际出现的主机名是否命中了预期代理策略。只有这三层都对上,才算真正完成了 Docker Hub 代理。
docker pull 的同时打开 Mihomo 或 Clash 客户端的连接列表,搜索 docker.io、auth.docker.io 和 registry。如果完全没有记录,优先检查 TUN 是否接管了 Docker 流量;如果记录显示 DIRECT,则应先调整规则顺序。
TUN 模式如何接管 Docker 的透明流量
TUN 模式可以理解为在操作系统中创建一张虚拟网卡。应用仍然按照原来的方式连接目标地址,不需要知道 HTTP 代理端口或 SOCKS5 端口;Mihomo 通过路由规则把这些数据包导入 TUN,再根据域名、目标 IP、进程或规则集决定直连还是代理。对于不读取 HTTP_PROXY、不支持 SOCKS5,或者由 systemd、容器运行时启动的程序,这种方式比单独配置环境变量更容易覆盖。
但 TUN 并不是「打开后所有流量自动变好」。它至少依赖四个条件:虚拟网卡成功创建,系统路由确实把目标流量送入该网卡,DNS 请求没有绕过 Mihomo,规则最终将 Docker Hub 相关连接分配给正确的代理组。任何一项不成立,都会出现表面上 TUN 已开启、实际 Docker 仍然直连的情况。
在 Linux 上尤其要注意 Docker 的网络模型。默认情况下,容器通过 Docker bridge 出站,宿主机负责 NAT;dockerd 自身的请求则直接由宿主机网络栈发起。若 TUN 只接管了普通用户进程,而没有正确处理 Docker bridge、转发和回环流量,宿主机上的 curl 可能成功,docker pull 却依旧失败。此时不要盲目扩大代理范围,应先确认实际失败的是 dockerd、容器内命令,还是镜像层下载。
Mihomo TUN 基础字段与安全边界
下面是一段用于理解字段关系的基础示例。它不是可以直接覆盖所有订阅配置的完整文件,实际使用时需要与现有的代理组、DNS 和规则集合并。auto-route 负责尝试写入路由,auto-detect-interface 用于识别当前主要出口,stack 则决定 TUN 使用的网络栈。
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
mixed 通常适合需要兼顾 TCP、UDP 和不同应用行为的桌面或服务器环境,但并非所有内核版本都以相同方式处理复杂网络。若开启后出现局域网访问异常、VPN 冲突或 Docker 网桥不通,可以先退回更保守的模式,确认基础路由后再调整。对于生产服务器,建议在维护窗口修改 TUN,并准备好通过 SSH 或控制台关闭配置的办法,避免路由环路导致远程失联。
Mihomo YAML:为 Docker Hub 建立清晰的规则分流
规则的核心不是把所有 Docker 流量都送入代理,而是准确覆盖认证、仓库和镜像层下载所需的目标。通常可以先将明确属于 Docker Hub 的域名放入代理策略组,再根据连接日志补充实际出现的域名。规则要放在过宽的国内直连、GEOIP 或默认规则之前,否则即使写了 DOMAIN-SUFFIX,docker.io,也可能已经在前面的规则中被处理。
rules:
- DOMAIN-SUFFIX,docker.io,PROXY
- DOMAIN-SUFFIX,docker.com,PROXY
- DOMAIN,auth.docker.io,PROXY
- DOMAIN,registry-1.docker.io,PROXY
- MATCH,DIRECT
这里的 PROXY 只是示例策略组名称,必须替换成配置中真实存在的组。若订阅文件已经包含 RULE-SET、GEOSITE 或脚本规则,不建议直接把大量规则复制到文件末尾,因为末尾通常已经接近 MATCH。更可靠的方式是在 Mihomo 的覆写区、规则提供者前置规则或客户端支持的自定义规则位置添加条目,并在连接面板确认命中结果。
镜像层的实际下载地址可能来自对象存储或 CDN。若清单请求能成功而下载层失败,说明 Docker Hub 主域规则并没有覆盖整个链路。这时应查看 Docker Engine 的错误信息和 Mihomo 连接记录,找出失败时出现的真实域名,再决定是为特定域名增加规则,还是使用更宽但仍可控的策略。不要看到一个陌生 CDN 域名就把整个云厂商域名后缀全部代理,否则可能让大量不相关的国内业务改变出口。
DOMAIN 验证认证和仓库连接,再用 DOMAIN-SUFFIX 补充同一服务的子域,最后才考虑规则集。每次只增加一组规则并重新执行拉取,便于判断到底是哪一项解决了问题。
DNS、路由与 Docker bridge:最容易忽略的三条链路
透明代理中,DNS 不是附属功能,而是决定规则能否按域名工作的重要环节。若系统先把 registry-1.docker.io 解析成一个不可用的地址,Mihomo 后续即使拥有正确的代理规则,也可能无法还原出预期的目标。启用 TUN 时,通常应让 DNS 请求进入 Mihomo 的 DNS 模块,通过 fake-ip 或 redir-host 模式统一处理,而不是让 Docker、宿主机和公司 DNS 各自解析一份结果。
fake-ip 模式便于根据域名建立规则映射,但部分内网服务、硬编码 IP 的程序和特殊证书校验可能不适配。redir-host 对兼容性更友好,却更依赖真实解析结果和嗅探能力。没有绝对适合所有环境的模式,关键是保持 DNS 路径一致。可以先检查宿主机的解析结果,再检查 Docker 容器内部的解析结果;如果两者不同,应先解决 DNS 分裂,而不是马上更换节点。
Docker bridge 还涉及转发和 NAT。执行以下检查时,重点不是记住某一条命令,而是确认每一步对应的网络层:
- 用
ip addr查看 TUN 虚拟网卡和 Docker bridge 是否存在,并确认它们没有使用重复地址段。 - 用
ip route检查默认路由和 Docker 网段路由,确认没有因为 VPN 或多网卡出现优先级冲突。 - 用
cat /etc/resolv.conf查看宿主机 DNS,但不要仅凭文件内容判断 Docker 实际使用的 DNS。 - 用
docker run --rm alpine:latest nslookup registry-1.docker.io测试容器解析;如果镜像本身尚未拉取,可使用本地已有的诊断镜像替代。 - 检查防火墙是否允许宿主机转发 Docker bridge 流量,以及 TUN、Docker 和 VPN 规则之间是否存在相互丢弃。
企业网络中还要考虑出口防火墙、TLS 检查和统一 DNS 策略。某些公司网络允许浏览器通过认证代理访问外部网站,却不允许服务器直接向任意地址建立连接;这时单纯开启 TUN 可能违反网络架构预期。应先确认企业是否提供标准 HTTP 代理、镜像仓库或白名单机制。如果组织已经提供内部 Docker Registry,优先使用内部镜像缓存通常比把生产主机全部流量交给透明代理更容易审计。
动手配置:从最小测试到 Docker Engine 验证
建议按照由小到大的顺序操作,不要一开始同时修改 TUN、DNS、Docker daemon 和防火墙。第一步,保存当前 Mihomo 配置并确认代理策略组本身可用;用宿主机上的 curl 访问 Docker Hub 的公开 HTTPS 地址,观察连接列表中是否出现预期域名。此时只验证 Clash 的出站能力,不急着判断 Docker。
第二步,开启 TUN 和 DNS 接管,重新执行宿主机测试。若宿主机 curl 从 DIRECT 变成 PROXY,说明虚拟网卡和规则至少开始工作。若完全没有连接记录,应查看系统权限、路由写入状态以及客户端日志;若出现连接但仍是 DIRECT,则检查规则顺序和策略组名称。不要把「能解析域名」当成「能建立 HTTPS」,两者需要分别验证。
第三步,验证 Docker Engine 的运行环境。使用 systemd 管理的 Docker 通常可以通过 drop-in 文件设置显式代理,但这与 TUN 是两种不同思路。显式代理适合只希望让 Docker Engine 访问仓库的服务器,TUN 适合覆盖更多不支持代理变量的程序。两者可以同时存在,但必须明确优先级,避免一个路径把请求送入 HTTP 代理,另一个路径又尝试透明接管。
sudo systemctl show --property=Environment docker
sudo journalctl -u docker --since "10 minutes ago"
docker info
docker pull hello-world
第四步,使用一个体积较小、来源明确的公开镜像进行测试,观察连接面板和 Docker 日志是否一致。成功拉取只能证明当前镜像链路可用,不能证明所有私有仓库、企业镜像或大体积分层都没问题。接着再测试需要认证的仓库,并确认登录凭据没有被写入公开脚本、Shell 历史或日志。
如果 Docker CLI 报错但 Mihomo 没有任何连接记录,优先检查 dockerd 是否运行在另一台虚拟机、WSL 实例或远程 Docker context 中。执行 docker context ls 可以确认当前 CLI 指向的 Engine。很多人实际连的是远程服务器,却只在本地电脑开启了 TUN,因此本地连接面板自然看不到远程 Docker 的请求。
Docker Engine 代理与容器内代理不要混为一谈
docker pull 由 Docker Engine 负责访问镜像仓库,而容器启动后执行的 apt、npm、pip 或应用 API 请求,则由容器自身发起。给 Docker Engine 配好 TUN 或 HTTP 代理,并不意味着容器内的业务进程也会自动使用同一条代理路径。相反,为了让容器内流量透明代理,通常还需要让宿主机转发、Docker bridge 路由和 TUN 规则形成完整链路。
如果只是解决拉取镜像问题,建议先不要给所有容器设置全局代理环境变量。全局注入 HTTP_PROXY、HTTPS_PROXY 和 ALL_PROXY 可能导致数据库、内部 API、云元数据地址甚至本地服务也被送入代理。更稳妥的做法是根据服务需求设置 NO_PROXY,加入 localhost、127.0.0.1、Docker 网关、企业内网域名和私有 Registry 域名,并在应用文档允许时逐服务配置。
对于生产环境,内部镜像建议使用 Harbor、云厂商容器镜像服务或企业 Registry 做缓存。这样可以把外部 Docker Hub 的不稳定因素集中在镜像同步任务上,而不是让每一台部署主机都实时访问公网。Clash TUN 更适合作为开发环境、临时构建机或确实没有内部镜像服务时的网络补充,不应替代企业级镜像治理、访问控制和审计。
常见故障与定位方法
只显示认证成功但拉取层失败:通常说明 auth.docker.io 已经命中代理,但镜像层所用的 CDN 或对象存储域名没有覆盖。请根据连接日志补充规则,不要只重复添加 docker.io。
宿主机 curl 成功,docker pull 超时:重点检查 Docker context、dockerd 所在位置、systemd 环境和 Docker bridge 路由。若当前 CLI 连接的是远程 Engine,本机 TUN 对远程主机没有作用。
开启 TUN 后内网服务无法访问:检查 fake-ip 排除列表、内网域名规则、局域网网段和默认路由。把公司域名、私有 Registry、网关地址和常用内网段明确设为直连或指定出口,避免依赖模糊的 GEOIP 判断。
DNS 查询正常但 HTTPS 仍然超时:解析成功只说明 DNS 有响应,不代表 TCP、TLS 和后续 HTTP 都正常。分别检查代理节点质量、系统时间、证书链、MTU、企业 TLS 检查以及防火墙策略。
同一配置在桌面端正常、服务器端异常:确认两台设备使用的内核版本、规则集、DNS 模式和出口节点是否一致。桌面端可能拥有系统代理权限,而服务器端没有;也可能一个运行在 IPv4,另一个优先使用 IPv6,导致最终路径完全不同。
常见问题
Docker Hub 只需要 HTTP_PROXY,还需要开启 TUN 吗?
不一定。若目标只是让 Docker Engine 访问 Docker Hub,并且 Docker 官方支持在当前版本中读取代理变量,那么给 systemd 服务设置 HTTP 或 HTTPS 代理通常更简单、更容易审计。TUN 的优势在于覆盖不读取代理变量的程序、多个子进程和更复杂的透明转发场景。建议先选择一种主路径,确认它稳定后再考虑叠加另一种方案。
Docker 容器应该使用什么 DNS?
没有适用于所有网络的固定地址。关键是让容器的 DNS 请求经过你设计好的解析链路,不要让宿主机、Docker daemon、容器运行时和企业 DNS 各自走不同路径。先查看实际的 /etc/resolv.conf 和容器运行参数,再结合 Mihomo DNS 日志判断是否发生了绕过。
只写 DOMAIN-SUFFIX,docker.io 就够了吗?
它可能覆盖主要仓库域名,但不保证认证、CDN 和镜像层下载都使用相同后缀。正确做法是先用明确域名规则验证基础链路,再根据连接日志补充真实出现的主机。规则越宽,误代理和排障成本越高,因此应以实际请求为依据逐步扩展。
可以在生产服务器上长期使用 Clash TUN 代理 Docker 吗?
技术上可以,但应评估稳定性、权限、故障回滚、日志审计和组织网络政策。生产环境更推荐内部 Registry、镜像缓存或企业批准的出口代理。若必须使用 TUN,应固定版本、备份配置、限制代理范围,并准备不依赖当前网络的远程恢复方式。
与只配置浏览器插件、临时修改单个终端环境变量,或依赖不透明的全局 VPN 相比,Clash TUN 能把 Docker Hub 的 DNS、路由、规则命中和代理策略放到同一套可观察链路中;即使遇到 CDN 域名变化,也能通过连接日志逐步补齐规则,而不是反复猜测。对于需要在 Linux、Docker Engine 和企业网络之间保持一致行为的开发者,这种可控的透明代理方式更适合长期排障与配置复用。