在服务器或家庭 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:通过 REDIRECTTPROXY 修改流量路径,使应用无需设置代理变量也能被接管。
  • 保证 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.yamlrules/logs/,升级容器时不会丢失运行数据。

docker-compose.yml
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,可以不授予全部权限;但透明代理场景下缺少权限通常会导致容器启动成功、代理端口却无法真正接管流量。

config.yaml 核心配置
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,再替换下面的地址。

  1. 确认 Clash 已经监听透明端口,并检查容器内或宿主机上是否能访问代理 API。若配置文件有语法错误,先通过日志修复,不要直接添加转发规则。
  2. 打开 Linux 的 IPv4 转发功能,让数据包能够在 Docker 网桥和物理网卡之间转发。
  3. 创建独立的自定义链,将 Docker 网桥来的 TCP 流量重定向到 Clash,同时排除私有网段、广播地址和 Clash 自身端口。
  4. 重新创建一个测试容器,使用 curlgetent hostsgit ls-remote 分别验证 DNS、TCP 代理和 HTTPS 请求。
启用转发并添加 REDIRECT 规则
# 临时启用 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 网桥名称以及系统的防火墙后端。生产环境还应使用发行版的持久化工具保存规则,避免服务器重启后透明代理失效。

保存 IPv4 转发参数
# /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 配置,因为它代表容器自身,而不是宿主机。

为测试容器指定 Clash 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_PROXYHTTPS_PROXYALL_PROXY;Docker Engine 拉取镜像则需要在 Docker 服务的 systemd drop-in 中单独设置代理,不能只给某个容器设置环境变量。

Docker Engine 代理配置示例
# /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.comping 使用 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、明确代理规则,并为每个关键端口保留可观测的日志。

立即开始

用 Clash 掌控你的流量

支持 Windows、macOS、Linux、Android 与 iOS,灵活规则,开箱即用。

免费下载 查看设置指南 →