多网卡主机Docker网络配置问题

测试环境

使用树莓派作为多网卡主机,其具备两张网卡,eth0和wlan0。另外需要一台PC用于测试与树莓派上容器的通信。将两台设备接入同一WiFi,然后再将两设备用网线直连。这样两设备即可以通过WiFi通信,也可以通过网线通信。

WiFi网段为192.168.31.0/24,树莓派IP为192.168.31.154,PC的IP为192.168.31.69。

网线连接的局域网网段为169.254.0.0/16,树莓派IP为169.254.140.68,PC的IP为169.254.140.66。

在树莓派上部署容器(容器为PLC运行环境容器),并对容器做不同的docker网络配置,测试PC与容器的通信,包括PC上的PLC开发环境软件(IDE)是否能接入容器,以及从容器内部是否能ping通PC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 树莓派网卡信息
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether dc:a6:32:de:1f:25 brd ff:ff:ff:ff:ff:ff
inet 169.254.140.68/16 brd 169.254.255.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::9b39:2105:8832:4cf6/64 scope link
valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether dc:a6:32:de:1f:26 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.154/24 brd 192.168.31.255 scope global dynamic noprefixroute wlan0
valid_lft 36699sec preferred_lft 31299sec
inet6 fd00:6868:6868::b91/128 scope global dynamic noprefixroute
valid_lft 36696sec preferred_lft 36696sec
inet6 fd00:6868:6868:0:77e8:ee3:7b24:7a36/64 scope global mngtmpaddr noprefixroute
valid_lft forever preferred_lft forever
inet6 fe80::215f:7f6a:a210:4d05/64 scope link
valid_lft forever preferred_lft forever
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# PC网卡信息
以太网适配器 以太网 2:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::62de:7603:51d9:79cc%17
IPv4 地址 . . . . . . . . . . . . : 169.254.140.66
子网掩码 . . . . . . . . . . . . : 255.255.0.0
默认网关. . . . . . . . . . . . . :
无线局域网适配器 WLAN:
连接特定的 DNS 后缀 . . . . . . . :
IPv6 地址 . . . . . . . . . . . . : fd00:6868:6868::69
IPv6 地址 . . . . . . . . . . . . : fd00:6868:6868:0:dbe:a764:e44b:ccec
临时 IPv6 地址. . . . . . . . . . : fd00:6868:6868:0:314c:b64b:89c7:1ef7
本地链接 IPv6 地址. . . . . . . . : fe80::b5c6:65b9:81b9:948f%16
IPv4 地址 . . . . . . . . . . . . : 192.168.31.69
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : fe80::cabf:4cff:fe4b:76d7%16
192.168.31.1

入向网络配置

入向:网络内其他设备访问主机上部署的容器

host模式

使用host网络模式创建容器,容器直接使用主机的网络配置。

1
docker run -it --net=host 0375866a09cd

所以PC上的IDE以树莓派的两个IP(169.254.140.68、192.168.31.154)作为目的地址都可以接入RTE。

bridge模式(不绑定网卡)

使用bridge网络模式创建容器,会将容器接入docker0虚拟网桥下,网桥网段一般为172.17.0.0/16。这种模式下,主机所在网段内的其他设备无法直接访问主机上的容器,需要在容器与主机间做端口映射。

1
docker run -it --net=bridge -p 3000:3000 0375866a09cd

因为没有绑定网卡,只设置了端口映射,所以IDE以树莓派两个IP(169.254.140.68、192.168.31.154)作为目的地址都可以接入RTE。

bridge模式(绑定网卡)

bridge模式创建容器,可以在设置端口映射时指定网卡IP,这样网络内其他设备只能从指定的主机网卡IP访问到容器。

1
docker run -it --net=bridge -p 192.168.31.154:3000:3000 0375866a09cd

使用上面命令创建容器后,PC上的IDE只有把树莓派IP 192.168.31.154作为目的地址,才能访问到RTE容器。

相应的,如果创建容器时设置容器绑定到169.254.140.68,那IDE只有把树莓派IP 169.254.140.68作为目的地址,才能访问到RTE容器。

出向网络配置

出向:主机上部署的容器访问网络内其他设备

host模式

在容器内ping主机所在的两个网段内的其他设备(如PC的两个网卡IP:192.168.31.69、169.254.140.66)都可以ping通。

bridge模式

使用bridege模式创建容器,可以使用自带的docker0网桥,但缺点是创建容器时不能指定IP。使用自己创建的docker网桥,可以解决这个问题。另外对于docker0以及所有新建的docker网桥,系统会自动添加一条路由规则,使网桥网段内的所有容器都可以访问到主机所在的所有网段。使用命令“iptables -t nat -nvL –line-numbers”查看路由规则。如下面POSTROUTING链中的第一条规则,docker0网段(172.17.0.0/16)下的容器可以访问主机所在的所有网段。

1
2
3
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0

新创建一个名为docker1的docker网络后,重新查看发现多出一条规则。

1
2
# 创建网络(此处新建的网络本质也是bridge模式)
docker network create --dirver bridge --subnet 172.18.0.0/16 --opt "com.docker.network.bridge.name"="docker1" docker1
1
2
3
4
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 MASQUERADE all -- * !docker1 172.18.0.0/16 0.0.0.0/0
2 17 1234 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0

然后创建容器,网络使用自己创建的docker1,但就像前面提到的,每个docker网络自动添加的路由规则,会使docker网络下的容器能访问到主机所在的所有网段。创建两个容器(rte1、rte2)测试一下。

1
2
3
#创建容器,加入docker1
docker run -d --net docker1 -p 3001:3000 --name rte1 0375866a09cd
docker run -d --net docker1 -p 3001:3000 --name rte2 0375866a09cd

创建容器后,查看规则,没有针对容器IP的出向规则,只有针对整个网段的。

1
2
3
4
5
6
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 MASQUERADE all -- * !docker1 172.18.0.0/16 0.0.0.0/0
2 17 1234 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
3 0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:3000
4 0 0 MASQUERADE tcp -- * * 172.18.0.3 172.18.0.3 tcp dpt:3000

从两个容器内向192.168.31.0/24和169.254.0.0/16两个网段ping,发现都能ping通。

如果想让某个容器只能访问192.168.31.0/24网段,另一个容器只能访问169.254.0.0/16网段,可以手动添加路由规则。

再创建两个容器(rte3、rte4),在创建时指定两个容器的IP。

1
2
3
#创建容器,加入docker1,指定IP
docker run -d --net docker1 --ip 172.18.0.33 -p 3002:3000 --name rte3 0375866a09cd
docker run -d --net docker1 --ip 172.18.0.44 -p 3004:3000 --name rte4 0375866a09cd

此时新建的两个容器(rte3、4)都在原本路由规则覆盖下,所以先删除docker1网络自动添加的路由规则。以规则序号为目标删除。

1
2
# 删除POSTROUTING链下的序号1的规则
iptables -t nat -D POSTROUTING 1

重新查看路由规则,docker1的出向路由规则已被删除。在rte3、4内向两个网段ping,都ping不通。

1
2
3
4
5
6
7
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 17 1234 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
2 0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:3000
3 0 0 MASQUERADE tcp -- * * 172.18.0.3 172.18.0.3 tcp dpt:3000
4 0 0 MASQUERADE tcp -- * * 172.18.0.33 172.18.0.33 tcp dpt:3000
5 0 0 MASQUERADE tcp -- * * 172.18.0.44 172.18.0.44 tcp dpt:3000

然后手动添加规则,完成目标需求。

1
2
3
4
5
# 配置nat,容器rte3向外发送的数据包源IP转换成192.168.31.154
iptables -t nat -I POSTROUTING -p all -s 172.18.0.33 -j SNAT --to-source 192.168.31.154

# 配置nat,容器rte4向外发送的数据包源IP转换成169.254.140.66
iptables -t nat -I POSTROUTING -p all -s 172.18.0.44 -j SNAT --to-source 169.254.140.68
1
2
3
4
5
6
7
8
9
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 SNAT all -- * * 172.18.0.44 0.0.0.0/0 to:169.254.140.68
2 0 0 SNAT all -- * * 172.18.0.33 0.0.0.0/0 to:192.168.31.154
3 17 1234 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
4 0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:3000
5 0 0 MASQUERADE tcp -- * * 172.18.0.3 172.18.0.3 tcp dpt:3000
6 0 0 MASQUERADE tcp -- * * 172.18.0.33 172.18.0.33 tcp dpt:3000
7 0 0 MASQUERADE tcp -- * * 172.18.0.44 172.18.0.44 tcp dpt:3000

配置完成后,再次测试,发现在容器rte3中,ping 192.168.31.69能通,ping 169.254.140.66不通;在容器rte4中,ping 169.254.140.66能通,ping 192.168.31.69不通,和目标需求一致。

当然除了可以添加针对IP的规则,也可以添加针对网段的规则,比如:创建了两个docker网络(docker11、docker22),希望docker11下的容器只能访问网段192.168.31.0/24,docker22下的容器只能访问网段169.254.0.0/16。可以如下配置。

1
2
3
4
5
6
7
# 创建docker网络
docker network create --dirver bridge --subnet 172.19.0.0/16 --opt "com.docker.network.bridge.name"="docker11" docker11
docker network create --dirver bridge --subnet 172.20.0.0/16 --opt "com.docker.network.bridge.name"="docker22" docker22

# 配置nat
iptables -t nat -I POSTROUTING -p all -s 172.19.0.0/16 -j SNAT --to-source 192.168.31.154
iptables -t nat -I POSTROUTING -p all -s 172.20.0.0/16 -j SNAT --to-source 169.254.140.68

总结

  • 入向(网络内其他设备到主机上的容器):创建容器时通过参数(-p)绑定某一网卡后,网络内其他设备只能通过容器绑定的网卡IP访问容器
  • 出向(主机上的容器到网络内其他设备):配置nat,将容器IP转换为网卡IP,从而使容器能够访问网卡IP所在网段内的其他设备