多服务共用VPN容器时,如何在各自docker-compose.yaml中配置端口?
问题背景
我想搭建一个VPN容器供多个服务共用,同时希望每个使用VPN的服务能在自己的docker-compose.yaml里独立配置需要发布的端口,而不是在共享VPN容器的配置文件里硬编码这些端口。目前找到的方案都需要在VPN容器的docker-compose里配置所有服务的端口,有没有办法实现让VPN容器完全不用知晓下游服务的信息?
当前现状
目前的可行方案是在VPN容器(以gluetun为例)的配置里声明所有服务需要的端口:
gluetun/docker-compose.yaml
version: "3" services: gluetun: image: qmcgaw/gluetun container_name: gluetun cap_add: - NET_ADMIN environment: - VPN_SERVICE_PROVIDER=vyprvpn - OPENVPN_USER=fred@flintstone.com - OPENVPN_PASSWORD=yadayadayada - SERVER_REGIONS=Germany - HTTPPROXY=on - FIREWALL_VPN_INPUT_PORTS=51413 ports: # VPN自身的HTTP代理端口 - 8123:8888 # Transmission需要的端口(希望移到Transmission的配置里) - 9091:9091 - 51413:51413 - 51413:51413/udp
transmission/docker-compose.yaml
services: transmission: image: lscr.io/linuxserver/transmission:latest container_name: transmission network_mode: container:gluetun environment: - PUID=1248 - PGID=1248 - TZ=Etc/UTC volumes: - /tmp/transmission/data:/config - /tmp/transmission/downloads:/downloads - /tmp/transmission/watch/folder:/watch # 这里配置端口会报错: # ports: # - 9091:9091 # - 51413:51413 # - 51413:51413/udp restart: unless-stopped
如果尝试在Transmission的配置里加ports,会触发错误:ERROR: for transmission Cannot create container for service transmission: conflicting options: port publishing and the container type network mode
解决方案:使用自定义Docker网络+路由配置
核心思路是放弃network_mode: container:gluetun,改用自定义桥接网络让VPN容器和服务容器互通,再通过路由配置强制服务流量走VPN接口,同时允许每个服务在自己的docker-compose里独立配置端口映射。
1. 创建自定义Docker网络
先手动创建专用网络(也可在gluetun的docker-compose中定义):
docker network create vpn-shared-net
2. 修改gluetun的docker-compose.yaml
移除所有服务相关端口,仅保留VPN自身需要的端口,同时接入自定义网络:
version: "3" services: gluetun: image: qmcgaw/gluetun container_name: gluetun cap_add: - NET_ADMIN environment: - VPN_SERVICE_PROVIDER=vyprvpn - OPENVPN_USER=fred@flintstone.com - OPENVPN_PASSWORD=yadayadayada - SERVER_REGIONS=Germany - HTTPPROXY=on # 无需再配置服务端口,gluetun会自动处理防火墙规则 ports: # 仅保留VPN自身的HTTP代理端口 - 8123:8888 networks: - vpn-shared-net # 可选:固定gluetun的IP,简化后续路由配置 ipam: config: - subnet: 172.20.0.0/16 gateway: 172.20.0.1 ip_range: 172.20.0.0/24 networks: vpn-shared-net: external: true # 手动创建的网络需设为external;若由compose自动创建则移除该行
3. 修改Transmission的docker-compose.yaml
接入自定义网络,独立配置端口映射,同时添加路由规则让流量走gluetun:
services: transmission: image: lscr.io/linuxserver/transmission:latest container_name: transmission environment: - PUID=1248 - PGID=1248 - TZ=Etc/UTC volumes: - /tmp/transmission/data:/config - /tmp/transmission/downloads:/downloads - /tmp/transmission/watch/folder:/watch ports: # 现在可在此独立配置端口 - 9091:9091 - 51413:51413 - 51413:51413/udp networks: - vpn-shared-net # 配置权限和路由规则 cap_add: - NET_ADMIN sysctls: - net.ipv4.conf.all.src_valid_mark=1 command: > sh -c " GLUETUN_IP=$(getent hosts gluetun | awk '{ print $1 }') && ip route del default && ip route add default via $GLUETUN_IP && /init " restart: unless-stopped networks: vpn-shared-net: external: true
关键说明
- 自定义网络:让所有服务与VPN容器处于同一网络,可通过容器名或IP互相通信,Docker自定义网络自带DNS解析。
- 路由配置:通过
ip route命令删除默认路由并添加指向gluetun的默认路由,强制服务所有流量走VPN接口。 - 独立端口映射:服务容器不再共享VPN的网络命名空间,因此可在自身
docker-compose中自由配置ports,无冲突问题。 - 防火墙自动维护:gluetun会自动管理防火墙规则,确保仅VPN流量被允许,无需手动配置
FIREWALL_VPN_INPUT_PORTS(若需开放外部端口仍可配置该变量)。
内容的提问来源于stack exchange,提问作者Peter V. Mørch

