在服务器或家庭 NAS 上运行 Docker 服务时,容器经常会遇到这样的网络问题:宿主机浏览器可以打开 GitHub,容器里的 git clone 却持续超时;npm install 卡在下载依赖;Docker Hub 拉取镜像失败;某些 AI API 请求始终无法建立连接。根本原因通常不是应用本身,而是容器流量没有完整经过 Clash、DNS 解析没有与代理路径保持一致,或者 iptables 转发规则没有覆盖 Docker 网桥。
本文以 Clash 或 Mihomo 运行在 Docker 中的场景为例,介绍一套可维护的透明代理方案。内容涵盖容器网络模式、DNS 接管、iptables 转发、代理环境变量和故障排查,并提供可以按需拆分的 YAML 与 Compose 配置。你可以把它用于 GitHub、npm、Docker Hub、PyPI、Google APIs 以及常见 AI 服务的稳定访问。
先理解 Docker 透明代理的流量路径
Docker 默认使用 bridge 网络。容器通过虚拟网卡连接到宿主机上的 docker0 网桥,出站数据通常经过宿主机的 NAT,再从物理网卡发往互联网。Clash 如果只监听宿主机的 HTTP 或 SOCKS 端口,并不会自动接管这些连接,因为容器应用大多直接发起 TCP 连接,并不知道代理端口的存在。
因此,透明代理需要完成三个动作:
- 让 Clash 监听容器能够到达的地址:不能只绑定
127.0.0.1,否则 bridge 网络中的容器无法连接宿主机代理端口。 - 把容器的出站流量重定向到 Clash:通过
REDIRECT或TPROXY修改流量路径,使应用无需设置代理变量也能被接管。 - 保证 DNS 查询路径一致:容器不能绕过 Clash,直接向宿主机或公网 DNS 查询,否则可能出现域名污染、规则匹配异常和 DNS 泄露。
实际部署时通常有两种拓扑。第一种是让 Mihomo 使用 network_mode: host,它与宿主机共享网络命名空间,配置简单,适合 Linux 服务器和家庭网关。第二种是让 Mihomo 保持独立的 bridge 网络,再通过宿主机端口映射和 iptables 转发容器流量,隔离性更好,但路由和权限配置更加复杂。
| 方案 | 优点 | 注意事项 | 适用场景 |
|---|---|---|---|
| host 网络 | 端口和路由最简单,透明代理规则容易统一 | 网络隔离较弱,仅适用于 Linux | 服务器、NAS、软路由 |
| bridge 网络 | 容器隔离清晰,便于独立管理 | 需要处理端口映射、网桥和转发链 | 多容器生产环境 |
| 仅代理变量 | 不需要修改 iptables,风险较低 | 只有支持代理的应用才会生效 | Git、npm、Python、curl 等命令行工具 |
如果你的目标是让所有容器流量都自动代理,推荐从 host 网络模式开始验证。等 DNS、路由和规则确认稳定后,再根据隔离需求迁移到 bridge 方案。不要一开始同时启用 TPROXY、REDIRECT、多个 DNS 服务和复杂的 Docker 自定义网络,否则出现超时时很难判断是哪一层造成的。
准备 Mihomo Docker 与模块化 YAML
下面的示例采用 Mihomo,因为它对 TUN、TPROXY、fake-ip 和规则集的支持通常比旧版 Clash 更完整。配置中的节点内容需要替换为你自己的订阅或节点,示例只展示网络相关部分。建议将配置拆成 config.yaml、rules/ 和 logs/,升级容器时不会丢失运行数据。
services: mihomo: image: metacubex/mihomo:latest container_name: mihomo restart: unless-stopped network_mode: host cap_add: - NET_ADMIN - NET_RAW volumes: - ./mihomo:/root/.config/mihomo environment: - TZ=Asia/Shanghai
NET_ADMIN 用于创建 TUN 接口、修改路由和处理透明代理规则,NET_RAW 则方便部分底层网络操作。若只使用 HTTP/SOCKS 代理变量,不启用 TUN 或 TPROXY,可以不授予全部权限;但透明代理场景下缺少权限通常会导致容器启动成功、代理端口却无法真正接管流量。
mixed-port: 7890 redir-port: 7892 tproxy-port: 7893 allow-lan: true bind-address: '*' mode: rule log-level: info dns: enable: true listen: 0.0.0.0:1053 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 nameserver: - 223.5.5.5 - 119.29.29.29 fallback: - tls://1.1.1.1:853 - tls://8.8.8.8:853 fallback-filter: geoip: true geoip-code: CN ipcidr: - 240.0.0.0/4
这里的 mixed-port 同时提供 HTTP 和 SOCKS5 代理,适合给 Git、npm 或其他显式支持代理的程序使用。redir-port 用于 Linux 的 REDIRECT 模式,tproxy-port 用于保留目标地址和 UDP 的 TPROXY 模式。二者不需要同时使用;初次部署建议先使用 REDIRECT 处理 TCP,确认 GitHub 和 npm 正常后,再考虑 TPROXY。
allow-lan 当作安全控制:它只决定 Clash 是否接受局域网连接。若监听在 0.0.0.0,请通过防火墙限制 7890、7892、7893 和 DNS 端口的来源,并为 External Controller 设置强密码,避免代理端口被公网扫描和滥用。动手配置:用 iptables 接管 Docker 容器流量
以下步骤假设宿主机为 Linux,Clash 使用 host 网络,并先采用 TCP REDIRECT。假设 Docker 网桥网段是 172.17.0.0/16,Clash 的透明端口是 7892。如果你的 Docker 自定义网络使用其他网段,应先执行 docker network inspect 查看实际的 subnet,再替换下面的地址。
- 确认 Clash 已经监听透明端口,并检查容器内或宿主机上是否能访问代理 API。若配置文件有语法错误,先通过日志修复,不要直接添加转发规则。
- 打开 Linux 的 IPv4 转发功能,让数据包能够在 Docker 网桥和物理网卡之间转发。
- 创建独立的自定义链,将 Docker 网桥来的 TCP 流量重定向到 Clash,同时排除私有网段、广播地址和 Clash 自身端口。
- 重新创建一个测试容器,使用
curl、getent hosts和git ls-remote分别验证 DNS、TCP 代理和 HTTPS 请求。
# 临时启用 IPv4 转发,重启后需写入 sysctl 配置 sudo sysctl -w net.ipv4.ip_forward=1 # 创建独立链,重复执行前先删除旧链或检查是否已存在 sudo iptables -t nat -N CLASH_DOCKER 2>/dev/null || true # 排除局域网、Docker 网段和本机保留地址,避免内部服务被代理 sudo iptables -t nat -A CLASH_DOCKER -d 10.0.0.0/8 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -d 172.16.0.0/12 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -d 192.168.0.0/16 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -d 127.0.0.0/8 -j RETURN # 排除 Clash 透明端口,防止流量重复重定向 sudo iptables -t nat -A CLASH_DOCKER -p tcp --dport 7892 -j RETURN # 其余 TCP 流量交给 Clash REDIRECT sudo iptables -t nat -A CLASH_DOCKER -p tcp -j REDIRECT --to-ports 7892 # 将 Docker 网桥进入的流量导入自定义链 sudo iptables -t nat -A PREROUTING -i docker0 -p tcp -j CLASH_DOCKER
上述规则只处理 TCP。GitHub、npm、Docker Hub 的主要请求通常是 HTTPS over TCP,因此可以先用这个方案验证。需要代理 UDP、QUIC 或部分实时应用时,再切换到 TPROXY 或 Mihomo TUN。直接把所有 UDP 强行转换成 TCP 并不可靠,可能造成 DNS、HTTP/3、语音和视频连接异常。
若宿主机使用 nftables 或 Docker 已经接管了 iptables 兼容层,命令显示成功并不代表数据包一定经过预期链。可以用 sudo iptables -t nat -vnL CLASH_DOCKER 查看计数器是否增长;如果计数始终为零,应检查实际网卡名称、Docker 网桥名称以及系统的防火墙后端。生产环境还应使用发行版的持久化工具保存规则,避免服务器重启后透明代理失效。
# /etc/sysctl.d/99-clash-docker.conf net.ipv4.ip_forward = 1 # 应用配置 sudo sysctl --system
DNS 与代理变量:解决 GitHub、npm 和 AI 请求超时
透明代理只负责接管连接,不一定能修复 DNS。Docker 默认会把宿主机解析器地址写入容器的 /etc/resolv.conf,在某些系统上可能指向本地 stub 地址,例如 127.0.0.53。容器内部的这个地址并不总是可达,或者它会直接向运营商 DNS 查询境外域名,最终表现为「代理节点正常,但域名解析失败」。
如果使用 fake-ip,建议让容器 DNS 查询明确指向 Mihomo 的 DNS 监听端口。host 网络模式下,宿主机可以使用 127.0.0.1:1053;bridge 网络模式下则应使用 Mihomo 容器在 Docker 网络中的固定地址。不要把 127.0.0.1 写入普通 bridge 容器的 dns 配置,因为它代表容器自身,而不是宿主机。
services: worker: image: debian:bookworm-slim dns: - 172.17.0.1 environment: HTTP_PROXY: http://172.17.0.1:7890 HTTPS_PROXY: http://172.17.0.1:7890 ALL_PROXY: socks5://172.17.0.1:7890 NO_PROXY: localhost,127.0.0.1,.local,172.16.0.0/12
显式代理变量与透明代理可以并存,但同一个进程最好只选择一种方式。Git、curl、npm、pip 和很多 SDK 会读取 HTTP_PROXY、HTTPS_PROXY 或 ALL_PROXY;Docker Engine 拉取镜像则需要在 Docker 服务的 systemd drop-in 中单独设置代理,不能只给某个容器设置环境变量。
# /etc/systemd/system/docker.service.d/http-proxy.conf [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,.local" # 重新加载并重启 Docker 服务 sudo systemctl daemon-reload sudo systemctl restart docker
验证时不要只执行一次 ping github.com。ping 使用 ICMP,可能没有经过你配置的 TCP 代理,结果不能代表 HTTPS 是否正常。更有价值的检查顺序是:先用 getent hosts github.com 检查解析,再用 curl -I https://github.com 检查 TLS,最后执行 git ls-remote https://github.com/... 或 npm view express version 验证真实应用协议。
curl -I 成功而 git clone 失败,重点检查 Git 是否读取了代理变量以及远程地址是否使用 HTTPS;如果域名解析成功但连接超时,查看 Clash Dashboard 的 Connections 页面,确认请求是否出现、命中了哪条规则以及实际使用的策略组。稳定性优化与常见故障定位
容器透明代理最常见的问题不是节点本身,而是规则链、DNS 和容器生命周期之间没有形成一致的配置。建议将 GitHub、GitLab、Docker Hub、npm、PyPI、OpenAI 以及你实际使用的 API 域名放入明确的代理规则,避免只依赖 IP 规则。很多云服务使用 CDN,IP 地址频繁变化,单纯维护 IP-CIDR 很快就会失效。
- 容器能解析域名但 HTTPS 超时:检查 Clash Connections 是否能看到请求。如果看不到,说明流量没有进入透明端口;如果看得到但规则为 DIRECT,检查域名规则顺序和策略组。
- 宿主机正常,容器不正常:检查
docker0或自定义网桥是否真的进入PREROUTING,再查看CLASH_DOCKER规则计数是否增加。 - DNS 查询超时:确认 Clash 的 DNS 监听地址、容器的
resolv.conf和防火墙放行情况。fake-ip 地址段也不能被错误地加入内网直连排除规则。 - Docker Hub 仍然无法拉取:确认配置的是 Docker Engine 代理,而不是业务容器的环境变量;修改 systemd 配置后必须重新加载并重启 Docker。
- 部分服务连接后马上断开:检查 MTU、HTTP/3 和 UDP 支持。某些网络环境下可以暂时禁用 QUIC,优先使用 TCP/TLS 验证是否为 UDP 路径问题。
- 重启后规则消失:将 sysctl 和 iptables 规则纳入系统启动流程,或者使用发行版提供的持久化服务,并记录 Docker 网络网段变化。
安全方面,建议把代理监听端口限制在局域网或 Docker 私有网段,不要为了省事直接对公网开放。External Controller 应绑定到本机地址并设置 secret;如果必须远程管理,应通过 SSH 隧道或受限防火墙访问。节点订阅、代理密码和 API 密钥不要写入公开镜像,也不要提交到 Git 仓库,可以通过环境变量、独立挂载文件或私有 Secret 管理。
完成部署后,可以建立一个最小化的健康检查容器,周期性访问 GitHub、Docker Hub 和实际使用的 AI API,并把失败日志与 Clash 的连接日志对照。这样能区分「节点不可用」「DNS 失败」「规则走错」「Docker 服务没有读取代理」等不同故障,而不是反复更换节点。对于长期运行的服务器,稳定的关键是减少隐式行为:固定网络网段、明确 DNS、明确代理规则,并为每个关键端口保留可观测的日志。