我们在上一篇中介绍了Docker中三种网络,none、host和bridge,除了这三种网络,Docker还允许我们创建自定义网络,当我们要创建自定义网络时,Docker提供了三种网络驱动供我们选择:bridge、macvlan和overlay,其中macvlan和ovelay都是用于创建跨主机网络,我们后面在研究,本篇我们主要使用bridge驱动。
一、创建自定义网络
我们通过docker network create命令创建test_net1网络:
$ sudo docker network create --driver bridge test_net1 c7c20444a940135c92958f4434ca2b7428ba17f70a2a8a954a6cf160a011b513 $ sudo docker network ls NETWORK ID NAME DRIVER SCOPE 6f0087fd32cd bridge bridge local 464b3d11003c host host local faa8eb8310b4 none null local c7c20444a940 test_net1 bridge local $ brctl show bridge name bridge id STP enabled interfaces br-c7c20444a940 8000.024200e01cd2 no docker0 8000.0242cef1fc32 no
我们可以看到,此时系统中新增了一个网桥br-c7c20444a940,其中c7c20444a940就是我们刚刚创建的桥接网络的ID。我们可以看下test_net1的详细配置信息:
$ sudo docker network inspect test_net1 [ { "Name": "test_net1", "Id": "c7c20444a940135c92958f4434ca2b7428ba17f70a2a8a954a6cf160a011b513", "Created": "2021-10-23T07:07:47.870947684Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.19.0.0/16", "Gateway": "172.19.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
如果我们在创建网络时,不指定--subnet和--gateway参数时,Docker会自动为其分配网段和网关,我们接着再创建一个网络并指定其网段和网关:
$ sudo docker network create --driver bridge --subnet 173.20.0.0/16 --gateway 173.20.0.1 test_net2 47542e86dc4474779a3b105f1d22f005fc39a529b1f19aa7af2c68297c1c0a41 $ sudo docker network inspect test_net2 [ { "Name": "test_net2", "Id": "47542e86dc4474779a3b105f1d22f005fc39a529b1f19aa7af2c68297c1c0a41", "Created": "2021-10-23T07:21:47.684305897Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "173.20.0.0/16", "Gateway": "173.20.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ] $ brctl show bridge name bridge id STP enabled interfaces br-47542e86dc44 8000.024273a34080 no br-c7c20444a940 8000.024200e01cd2 no docker0 8000.0242cef1fc32 no $ ifconfig br-47542e86dc44 br-47542e86dc44: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 173.20.0.1 netmask 255.255.0.0 broadcast 173.20.255.255 ether 02:42:73:a3:40:80 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
二、自定义网络的使用
容易要使用自定义的网络,同样需在启动时通过--network参数指定即可:
$ sudo docker run -it --network=test_net2 busybox / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ad:14:00:02 brd ff:ff:ff:ff:ff:ff inet 173.20.0.2/16 brd 173.20.255.255 scope global eth0 valid_lft forever preferred_lft forever
我们可以看到Docker会根据容易所使用的网络的网段自动为容器分配IP,当然,容器IP也是可以指定的,即通过--ip参数,但是要注意两点:
-
只有使用--subnet参数指定网段创建的网络才能指定IP
-
所指定的IP一定要在所使用的网络的网段中
下图所示的错误就是容器使用的网络并没有通过--subnet指定网段:
$ sudo docker run -it --network=test_net1 --ip 172.19.0.5 busybox docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.
下图所示的错误是容器所指定IP不在其网络的指定网段内:
$ sudo docker run -it --network=test_net2 --ip 172.20.0.2 busybox ERRO[0000] error waiting for container: context canceled docker: Error response from daemon: Invalid address 172.20.0.2: It does not belong to any of this network's subnets.
以下是正确的指定容器IP的例子:
$ sudo docker run -it --network=test_net2 --ip 173.20.0.4 busybox / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ad:14:00:04 brd ff:ff:ff:ff:ff:ff inet 173.20.0.4/16 brd 173.20.255.255 scope global eth0 valid_lft forever preferred_lft forever
三、Docker网络隔离及原理
到目前位置我们创建使用test_net2网络创建了两个容器,那么这两个容器网络应该是互通的,我们来验证一下:
$ sudo docker run -it --network=test_net2 --ip 173.20.0.4 busybox / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ad:14:00:04 brd ff:ff:ff:ff:ff:ff inet 173.20.0.4/16 brd 173.20.255.255 scope global eth0 valid_lft forever preferred_lft forever / # ping 173.20.0.2 PING 173.20.0.2 (173.20.0.2): 56 data bytes 64 bytes from 173.20.0.2: seq=0 ttl=64 time=1.319 ms 64 bytes from 173.20.0.2: seq=1 ttl=64 time=0.106 ms 64 bytes from 173.20.0.2: seq=2 ttl=64 time=0.107 ms ^C --- 173.20.0.2 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.106/0.510/1.319 ms
所以,我们得出结论,使用同一网络创建的容器是能够互通的。相反,如果是不同网络创建的容器应该是无法通信,我们使用test_net1创建一个容器验证一下:
$ sudo docker run -it --network=test_net1 busybox / # ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0 valid_lft forever preferred_lft forever / # ping 173.20.0.2 PING 173.20.0.2 (173.20.0.2): 56 data bytes ^C --- 173.20.0.2 ping statistics --- 146 packets transmitted, 0 packets received, 100% packet loss
从结果来看确实无法通信,但是如果我们开启系统的ip forwarding,把当前主机当作路由,那么不同网络应该是能通信的,我们首先来看下系统的配置:
$ ip r default via 172.16.194.2 dev ens33 proto dhcp src 172.16.194.135 metric 100 172.16.194.0/24 dev ens33 proto kernel scope link src 172.16.194.135 172.16.194.2 dev ens33 proto dhcp scope link src 172.16.194.135 metric 100 172.19.0.0/16 dev br-c7c20444a940 proto kernel scope link src 172.19.0.1 173.20.0.0/16 dev br-47542e86dc44 proto kernel scope link src 173.20.0.1 $ sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
看来我们的主机已经开启了ip forwarding并且172.19.0.0/16和173.20.0.0/16的路由也定义好了,那为什么网络还是不通呢?我们看看iptables:
$ sudo iptables-save # Generated by iptables-save v1.6.1 on Sat Oct 23 08:19:03 2021 *filter ... # 如果数据包是从br-47542e86dc44流入但不从br-47542e86dc44流出则跳转到DOCKER-ISOLATION-STAGE-2处理 -A DOCKER-ISOLATION-STAGE-1 -i br-47542e86dc44 ! -o br-47542e86dc44 -j DOCKER-ISOLATION-STAGE-2 # 如果数据包是从br-c7c20444a940流入但不从br-c7c20444a940流出则跳转到DOCKER-ISOLATION-STAGE-2处理 -A DOCKER-ISOLATION-STAGE-1 -i br-c7c20444a940 ! -o br-c7c20444a940 -j DOCKER-ISOLATION-STAGE-2 # 如果数据包是从docker0流入但不从docker0流出则跳转到DOCKER-ISOLATION-STAGE-2处理 -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2 # 如果非以上三种情况则返回上一层处理 -A DOCKER-ISOLATION-STAGE-1 -j RETURN # 如果数据包流出到br-47542e86dc44则drop -A DOCKER-ISOLATION-STAGE-2 -o br-47542e86dc44 -j DROP # 如果数据包流出到br-c7c20444a940则drop -A DOCKER-ISOLATION-STAGE-2 -o br-c7c20444a940 -j DROP # 如果数据包流出到docker0则drop -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP # 如果非以上三种情况则返回上一层处理 -A DOCKER-ISOLATION-STAGE-2 -j RETURN -A DOCKER-USER -j RETURN ...
看来原因就在这里了,我们发现Docker创建了一系列网络隔离策略,当数据包流入的网络与流出的网络相同则不作处理,不同则弃数据包,我们通过一个流程图来说明DOCKER-ISOLATION-STAGE-1和DOCKER-ISOLATION-STAGE-2规则的逻辑:
如果我一定要两个使用不同网络的容器通信,该怎么处理呢?也很简单,我们只需要给容器添加对应的网络的的网卡即可,我们通过docker network connect命令可以实现:
# 0da5e260a4b6容器使用的是test_net1,另外两个使用的是test_net2, # 如果大家在这一步不确定的话可以使用docker inspect 容器id进行确定 $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0da5e260a4b6 busybox "sh" 2 hours ago Up 2 hours objective_blackburn c6871db3efbb busybox "sh" 2 hours ago Up 2 hours goofy_swartz c683eacc4eae busybox "sh" 2 hours ago Up 2 hours modest_swartz yangye@ayato:~$ sudo docker network connect test_net2 0da5e260a4b6
我们在容器0da5e260a4b6中先ping下173.20.0.2发现网络是通的,我们再看看其网络配置,发现容器中添加了my_net2网络的网卡eth1:
/ # ping 173.20.0.2 PING 173.20.0.2 (173.20.0.2): 56 data bytes 64 bytes from 173.20.0.2: seq=0 ttl=64 time=0.291 ms 64 bytes from 173.20.0.2: seq=1 ttl=64 time=0.100 ms 64 bytes from 173.20.0.2: seq=2 ttl=64 time=0.097 ms 64 bytes from 173.20.0.2: seq=3 ttl=64 time=0.100 ms 64 bytes from 173.20.0.2: seq=4 ttl=64 time=0.147 ms ^C --- 173.20.0.2 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max = 0.097/0.147/0.291 ms / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:13:00:02 inet addr:172.19.0.2 Bcast:172.19.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:25 errors:0 dropped:0 overruns:0 frame:0 TX packets:150 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1830 (1.7 KiB) TX bytes:14476 (14.1 KiB) eth1 Link encap:Ethernet HWaddr 02:42:AD:14:00:03 inet addr:173.20.0.3 Bcast:173.20.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:18 errors:0 dropped:0 overruns:0 frame:0 TX packets:7 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1440 (1.4 KiB) TX bytes:574 (574.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
好的,以上就是本篇的内容,下一篇我们再看看容器间通信方式。