如何让Docker容器C2流量经C1转发并实现二者直接连接?
当然可以实现你的需求!下面我会分享几种可行的方案,你可以根据自己的场景选择最适合的:
方案一:利用自定义桥接网络(最简便)
Docker默认的自定义网络确实会创建网桥,但这并不影响我们实现C1作为C2网关的需求,反而可以利用这个网桥来让两个容器互通。步骤如下:
创建自定义桥接网络
先创建一个带指定子网的自定义网络,方便我们手动分配静态IP:docker network create --subnet=192.168.100.0/24 c1c2-net启动黑盒容器C1并开启IP转发
C1需要具备转发流量的能力,所以要给它添加NET_ADMIN权限,同时分配静态IP:docker run -d --name C1 --net c1c2-net --ip 192.168.100.2 --cap-add=NET_ADMIN --sysctl net.ipv4.ip_forward=1 your-c1-image这里
--sysctl net.ipv4.ip_forward=1直接在启动时开启了IP转发,确保C1能把C2的流量转发出去。启动C2并设置默认网关为C1
给C2分配同子网的静态IP,然后修改它的默认路由指向C1:# 先启动C2 docker run -d --name C2 --net c1c2-net --ip 192.168.100.3 --cap-add=NET_ADMIN your-c2-image # 修改默认网关 docker exec -it C2 ip route del default docker exec -it C2 ip route add default via 192.168.100.2如果你想在启动C2时自动配置网关,可以用
--entrypoint来整合命令:docker run -d --name C2 --net c1c2-net --ip 192.168.100.3 --cap-add=NET_ADMIN --entrypoint /bin/sh your-c2-image -c "ip route del default; ip route add default via 192.168.100.2; exec your-c2-start-command"这样配置后,C2的所有出站流量都会先经过C1,再由C1转发到外部网络。如果需要让入站到C2的流量也经过C1,只需在C1上配置端口转发规则,比如把C1的80端口转发到C2的80端口:
docker exec -it C1 iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.100.3:80 docker exec -it C1 iptables -t nat -A POSTROUTING -p tcp --dport 80 -d 192.168.100.3 -j MASQUERADE
方案二:使用IPvlan网络(无网桥点对点)
如果你确实不想通过网桥连接两个容器,可以用Docker的ipvlan驱动创建直接绑定到主机物理网卡的网络,这样两个容器处于同一二层网络,没有中间网桥:
创建IPvlan网络
假设主机的物理网卡是eth0,创建一个ipvlan网络:docker network create -d ipvlan --subnet=192.168.100.0/24 -o parent=eth0 ipvlan-c1c2启动C1并开启IP转发
docker run -d --name C1 --net ipvlan-c1c2 --ip 192.168.100.2 --cap-add=NET_ADMIN --sysctl net.ipv4.ip_forward=1 your-c1-image启动C2并设置网关为C1
步骤和方案一类似,修改C2的默认路由指向C1即可:docker run -d --name C2 --net ipvlan-c1c2 --ip 192.168.100.3 --cap-add=NET_ADMIN your-c2-image docker exec -it C2 ip route del default docker exec -it C2 ip route add default via 192.168.100.2
方案三:手动创建Veth Pair(完全自定义)
如果需要更极致的点对点连接,可以手动创建Veth对,分别挂载到两个容器的网络命名空间里,完全不需要Docker网络的介入:
启动C1和C2在none网络
先让两个容器脱离Docker的默认网络管理:docker run -d --name C1 --net none your-c1-image docker run -d --name C2 --net none your-c2-image创建并配置Veth Pair
我们用ip命令手动创建一对虚拟网卡,然后分别加到两个容器里:# 创建veth对 ip link add veth-c1 type veth peer name veth-c2 # 获取C1和C2的网络命名空间PID PID_C1=$(docker inspect -f '{{.State.Pid}}' C1) PID_C2=$(docker inspect -f '{{.State.Pid}}' C2) # 将veth-c1挂载到C1的命名空间 ip link set veth-c1 netns $PID_C1 docker exec -it C1 ip link set veth-c1 name eth0 docker exec -it C1 ip addr add 192.168.100.2/24 dev eth0 docker exec -it C1 ip link set eth0 up # 将veth-c2挂载到C2的命名空间 ip link set veth-c2 netns $PID_C2 docker exec -it C2 ip link set veth-c2 name eth0 docker exec -it C2 ip addr add 192.168.100.3/24 dev eth0 docker exec -it C2 ip link set eth0 up配置C2的网关并开启C1的IP转发
# 开启C1的IP转发 docker exec -it C1 sysctl -w net.ipv4.ip_forward=1 # 设置C2的默认网关为C1 docker exec -it C2 ip route add default via 192.168.100.2
这样两个容器就实现了完全的点对点连接,没有任何中间网桥,所有流量都会经过C1。
内容的提问来源于stack exchange,提问作者João Barraca Filipe

