TL;DR:
自定义下面的配置即可,注意:
- 需要填入./clash-conf,并开启tun
- 注意clash容器的名称不可修改,否则需同步修改下面的脚本
- 修改command属性中的最后一条命令为相应image的CMD
- 应用的image中需要带有ip命令(或许也可以放一个静态编译的iproute2)
- 有可能需要配置
sysctl -w net.ipv4.conf.all.rp_filter=2
version: '3.8' services: clash: image: metacubex/mihomo:v1.18.3 restart: always devices: - /dev/net/tun networks: vpn-net: volumes: - ./clash-conf:/root/.config/mihomo cap_add: - NET_ADMIN s: depends_on: - clash init: true networks: lan-net: vpn-net: cap_add: - NET_ADMIN command: | sh -euxc ' sleep 2 gw=$(getent hosts clash | awk "{ print \$1 }" || true) if [ -z "$$gw" ]; then echo "ERROR: failed to resolve clash" >&2 exit 1 fi echo "Resolved clash to $$gw" ip a ip r ip route del default || true ip route add default via $$gw exec bash -x /root/start.sh' sysctls: - "net.ipv6.conf.all.disable_ipv6=1" # Service custom properties image: XXXXXX restart: always networks: vpn-net: driver: bridge lan-net: driver: bridge
现有方案的缺点:
- 方案1:在宿主机上开tun
- 不共享netns,导致宿主机无法区分流量来源
- 配合fwmark可以实现区分,但是由于docker没法自己自动打fwmark,维护代价太高
- 配合固定ip也可以区分,但是这样需要自主管理docker的ip池分配,维护代价也不低
- 方案2:network-mode: container:XXX
- 使用这个方法共享netns,只能在XXX上设置expose
- 多个容器共享ip地址,无法多次bind同一个端口
- 方案3:在容器内开vpn
- 增加镜像大小和复杂度,难以长期维护
- 方案4:Custom Network Plugin (Network Driver)
- yassine/soxy-driver、vincent-163/docker-transparent-socks5
- 长期未更新(7年)、无大规模应用
- 需要自定义network driver,可能与docker compose兼容不好
- 使用的是redsocks,基于iptables而不是tun,在复杂网络环境下效果可能不如clash/sing-box的tun方案
原理:
- 同时连接到2个network,一个起到compose default network的作用(lan-net),一个专门用于与vpn连接(vpn-net)
- (或许可以只有1个network,但这样不容易debug)
- clash启动tun,应用的service则通过command中的小脚本根据hostname解析clash并自动设置gateway
- 这样无需配置任何固定ip段、固定ip地址,且仍然可以正常使用expose等功能