[Kubernetes]Docker的网络模型

1. Docker默认自动创建的网络

当安装完Docker时,Docker默认自动创建了三个网络:bridege, none和host. 使用docker network list命令可以查看。

$ docker network ls
NETWORK ID          NAME                DRIVER
507d17743c82        bridge              bridge              
14984b1fcc40        host                host                
0aba874b7156        none                null  

使用Docker运行容器时,可以用--net来指定容器运行时所在的网络。例如:

$ docker run --net=host -itd --name=container busybox

1.1 Docker的bridge网络

使用ifconfig命令,可以看到多出了一个docker0:

$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:bf:c2:8c:9a  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  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:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

docker0是Docker在安装时创建的bridge网络。如果运行容器时没有使用--net,则容器默认运行在bridge网络。使用docker network inspect命令查看bridge的详细信息:

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "507d17743c82d550f4c367f42f17f210f08f9b679191a3fb610fed33dfe525e1",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

从上面的信息可以看到,Docker引擎在docker0上所创建的bridge子网为172.17.0.0/16,目前该网络内还没有容器在运行。现在使用docker run命令增加两个容器

$ docker run -itd --name=container1 busybox
de95bdb8761f646a3f8423724f944f420e332fa3070bae1ff3fec127411e015e
$ docker run -itd --name=container2 busybox
c5bdfc331017107be4ae1bdfff12e531b3eaa3424852c4404a301b1f1c28f31b

再次用docker network inspect命令查看bridge的详细信息:

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "507d17743c82d550f4c367f42f17f210f08f9b679191a3fb610fed33dfe525e1",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "c5bdfc331017107be4ae1bdfff12e531b3eaa3424852c4404a301b1f1c28f31b": {
                "Name": "container2",
                "EndpointID": "04160448f248c68d37644da7dc4e4eff56e814a9efd9f4499095ece494cf6926",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "de95bdb8761f646a3f8423724f944f420e332fa3070bae1ff3fec127411e015e": {
                "Name": "container1",
                "EndpointID": "610a49518b5a15c28e3931fc20107c9e380c5c7b267feebd101a59e099977e43",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

可以看到,增加的两个容器运行在了bridge网络,并且被分配了172.17.0.0/16子网下的IP地址:172.17.0.3/16和172.17.0.2/16.容器使用这个IP地址可以相互通信。

使用docker attach命令连上一个容器container1,并查看容器的ifconfig信息:

$ docker attach container1
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:66 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:9990 (9.7 KiB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Docker引擎为该容器分配了一个虚拟的网络设备eth0,并为其分配了IP 172.17.0.2/16。 继续留在容器会话,用ping命令来测试容器之间的连通性,并查看容器/etc/hosts文件的内容

/ # ping -w3 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.147 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.088 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.088 ms

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.107/0.147 ms
/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
<span style="color:#FF0000;">172.17.0.2	de95bdb8761f</span>

也可以用同样的方式连接上container2查看网络信息并测试连通性。

现在再来看看主机的网络信息ifconfig,会发现多了两个网络接口:

veth94fa486 Link encap:Ethernet  HWaddr 46:d9:22:67:7e:bf  
          inet6 addr: fe80::44d9:22ff:fe67:7ebf/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:61 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1124 (1.1 KB)  TX bytes:8778 (8.7 KB)

veth9dc4552 Link encap:Ethernet  HWaddr 1a:8d:37:3e:a9:f1  
          inet6 addr: fe80::188d:37ff:fe3e:a9f1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:32 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:648 (648.0 B)  TX bytes:5084 (5.0 KB)

这两个名为vethXXXX的网络接口,正和container1和container2的eth0对应。他们与主机的docker0接口形成了网络管道,能让容器和主机互联——我们即可以在主机上ping通容器,也可以在容器里ping通主机:

$ ping -w3 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.052 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.040 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.051 ms

--- 172.17.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2997ms
rtt min/avg/max/mdev = 0.040/0.055/0.079/0.016 ms

$ docker attach container1
/ # ping -w3 10.12.129.131
PING 10.12.129.131 (10.12.129.131): 56 data bytes
64 bytes from 10.12.129.131: seq=0 ttl=64 time=0.116 ms
64 bytes from 10.12.129.131: seq=1 ttl=64 time=0.092 ms
64 bytes from 10.12.129.131: seq=2 ttl=64 time=0.062 ms

--- 10.12.129.131 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.062/0.090/0.116 ms

运行在bridge网络的容器,可以通过发布端口的方法获得与主机所在的外界网络的通信。如在docker run命令中使用-P就能将容器所有的端口暴露出来,绑定到主机的某随机端口上

$ docker run -dP --name=nginxcontainer nginx
f7ada9face148d5123cfd89f70ba223f2f5090c50ee2081f742377e75d2abb79
$ docker port nginxcontainer
443/tcp -> 0.0.0.0:32770
80/tcp -> 0.0.0.0:32771

nginxcontainer容器创建在了bridge网络上,其443端口和80端口分别绑定到了主机的32770端口和32771上。使用curl命令能访问。

$ curl http://127.0.0.1:32771
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

下图很形象地展示了Docker bridge网络里运行的容器及端口暴露。

[Kubernetes]Docker的网络模型

1.2 Docker的none网络

使用docker network inspect命令查看none的详细信息:

$ docker network inspect none
[
    {
        "Name": "none",
        "Id": "0aba874b71562e7ec802a25ea8c6609ad295ff6ea6f3ea42633c9d7cf8b4133e",
        "Scope": "local",
        "Driver": "null",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

目前该网络内还没有容器在运行。现在使用docker run命令增加容器

$ docker run --net=none -itd --name=container3 busybox
1996e52719ff3852efeabfe9f6accc504aa0d140f1be024c8fa9fb73e0dac704

再次用docker network inspect命令查看none网络的详细信息:

$ docker network inspect none
[
    {
        "Name": "none",
        "Id": "0aba874b71562e7ec802a25ea8c6609ad295ff6ea6f3ea42633c9d7cf8b4133e",
        "Scope": "local",
        "Driver": "null",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Containers": {
            "1996e52719ff3852efeabfe9f6accc504aa0d140f1be024c8fa9fb73e0dac704": {
                "Name": "container3",
                "EndpointID": "0745453691f3f8efaf47d8180f9b04f72c64bbcf4bc313ea9ba3ccb65bac6e4d",
                "MacAddress": "",
                "IPv4Address": "",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

可以看到,增加的容器运行在none网络,没有MAC地址,没有IP地址。使用docker attach命令连上该容器查看其网络配置:

$ docker attach container3
/ # ifconfig
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
	<span style="color:#FF0000;">1996e52719ff</span>
/ # 

Docker引擎没有为该容器分配虚拟的网络设备和IP地址。

1.3 Docker的host网络

使用docker network inspect命令查看host网络的详细信息:

$ docker network inspect host
[
    {
        "Name": "host",
        "Id": "14984b1fcc40a9ea7a655048bbcda98c8142f24d68a072a21199266a218da511",
        "Scope": "local",
        "Driver": "host",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

目前该网络内还没有容器在运行。现在使用docker run命令增加容器

$ docker run --net=host -itd --name=container4 busybox
c532064f4a98940a34f7185b9347025f0bb9bebf73474d1f8368bb50048a4f22

再次用docker network inspect命令查看host网络的详细信息:

$ docker network inspect host
[
    {
        "Name": "host",
        "Id": "14984b1fcc40a9ea7a655048bbcda98c8142f24d68a072a21199266a218da511",
        "Scope": "local",
        "Driver": "host",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Containers": {
            "c532064f4a98940a34f7185b9347025f0bb9bebf73474d1f8368bb50048a4f22": {
                "Name": "container4",
                "EndpointID": "e5100943faea59d3fe9ef247745528a1bf434ec6bd6702d102c56893d32a8696",
                "MacAddress": "",
                "IPv4Address": "",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

可以看到,增加的容器运行在host网络,同样没有MAC地址,没有IP地址。使用docker attach命令连上该容器查看其网络配置:

$ docker attach container4
/ # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:BF:C2:8C:9A  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:bfff:fec2:8c9a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17 errors:0 dropped:0 overruns:0 frame:0
          TX packets:37 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1100 (1.0 KiB)  TX bytes:6055 (5.9 KiB)

enp4s0    Link encap:Ethernet  HWaddr A4:1F:72:6B:4E:D9  
          inet addr:10.12.165.28  Bcast:10.12.165.255  Mask:255.255.255.0
          inet6 addr: fe80::de02:484e:467d:7e83/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:51485 errors:0 dropped:0 overruns:0 frame:0
          TX packets:32784 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:25389130 (24.2 MiB)  TX bytes:6960263 (6.6 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:5088 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5088 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:467265 (456.3 KiB)  TX bytes:467265 (456.3 KiB)

veth435e89d Link encap:Ethernet  HWaddr 4A:64:11:22:A9:B1  
          inet6 addr: fe80::4864:11ff:fe22:a9b1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:13 errors:0 dropped:0 overruns:0 frame:0
          TX packets:46 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1026 (1.0 KiB)  TX bytes:7094 (6.9 KiB)

veth9250953 Link encap:Ethernet  HWaddr 0E:BA:74:68:E4:08  
          inet6 addr: fe80::cba:74ff:fe68:e408/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:13 errors:0 dropped:0 overruns:0 frame:0
          TX packets:78 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1026 (1.0 KiB)  TX bytes:11673 (11.3 KiB)

wlp3s0    Link encap:Ethernet  HWaddr 1C:3E:84:98:71:A2  
          inet addr:10.12.137.123  Bcast:10.12.139.255  Mask:255.255.252.0
          inet6 addr: fe80::c40f:83fe:c57:7f0a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:42260 errors:0 dropped:0 overruns:0 frame:0
          TX packets:511 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:3733911 (3.5 MiB)  TX bytes:92175 (90.0 KiB)

/ # cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	jiayuan-Vostro-270

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

容器的网络配置信息与所在的主机完全相同。这就意味着该主机能访问的网络,该容器也能访问。用ping命令来测试:

/ # ping -w3 jd.com
PING jd.com (111.206.227.118): 56 data bytes
64 bytes from 111.206.227.118: seq=0 ttl=53 time=1.886 ms
64 bytes from 111.206.227.118: seq=1 ttl=53 time=1.716 ms
64 bytes from 111.206.227.118: seq=2 ttl=53 time=1.564 ms

--- jd.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.564/1.722/1.886 ms

说明容器能访问jd.com

1.3 Docker的"container"网络

Docker还可以将容器运行在另一个容器的网络上,例如,上面的容器container1运行在bridge网络,现在再创建一个容器container7,用--net=container:container1来指定容器container7与container1共享一个网卡eth0

$ docker run --net=container:container1 -itd --name=container7 busybox
479a3b2e17bb634098a7b89cb088f813ab7bbe8d54988121ff06046eed479034

如果用docker network inspect bridge查看bridge网络,不会看到容器container7运行在该网络。但是container7也能和container1一样监听来自eth0的请求。用docker inspect container7命令查看容器的详细信息里NetworkMode为container:container1.

连上container7看看网络信息,发现container7和container1的网络接口信息完全相同,说明两个容器共享一个网卡eth0

$ docker attach container7
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:28 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4476 (4.3 KiB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/hosts
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
ff00::0    ip6-mcastprefix
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters
172.17.0.2    de95bdb8761f

Docker的这种网络模式,被Kubernetes用来创建Pod内部的网络环境。关于这个,我会在Kubernetes网络模型一文中介绍。

2. 用户自定义网络

除了Docker自动创建的三个网络,用户可以为Docker创建新的网络。为Docker增加新的网络的方式有两种——使用Docker自带的bridge和overlay两种网络驱动,或提供新的网络插件。

用户可以创建多个网络,并且将容器运行在多个网络上。容器之间的通信只能在同一个网络里进行,容器之间不能跨网络通信。

2.1 使用bridge驱动创建网络

使用docker network create命令创建一个新的bridge网络

$ docker network create --driver bridge my_network
5d0513f620613f8e14d13ab44034be57ee40dada3a8a63159d8cad8c5b840ad9

使用ifconfig命令,可以看到,除了docker0,又多出一个br-5d0513f62061:

$ ifconfig
<span style="color:#FF0000;">br-5d0513f62061</span> Link encap:Ethernet  HWaddr 02:42:eb:66:d2:0e  
          inet addr:172.18.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  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:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

docker0   Link encap:Ethernet  HWaddr 02:42:bf:c2:8c:9a  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:bfff:fec2:8c9a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17 errors:0 dropped:0 overruns:0 frame:0
          TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1100 (1.1 KB)  TX bytes:6418 (6.4 KB)

此时,使用docker network list命令可以看到多出了一个叫my_network的网络:

$ docker network ls
NETWORK ID          NAME                DRIVER
507d17743c82        bridge              bridge              
14984b1fcc40        host                host                
<span style="color:#FF0000;">5d0513f62061        my_network          bridge </span>             
0aba874b7156        none                null  

my_network网络的使用方式和1.1 Docker的bridge网络里介绍的bridge网络的使用方式完全相同,如下。

$ docker run --net=my_network -itd --name=container5 busybox
d54c5dd948a66b0835fca0e5a2ade03cf912bad832a3032967a9079292cd5836
$ docker network inspect my_network
[
    {
        "Name": "my_network",
        "Id": "5d0513f620613f8e14d13ab44034be57ee40dada3a8a63159d8cad8c5b840ad9",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "d54c5dd948a66b0835fca0e5a2ade03cf912bad832a3032967a9079292cd5836": {
                "Name": "container5",
                "EndpointID": "0cf4df182681761b0fe2a07bbb71ae3fc87ebea7b8ed55831ef5c951c3bef9b9",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
$ docker attach container5
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:12:00:02  
          inet addr:172.18.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:54 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:8530 (8.3 KiB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.18.0.2	d54c5dd948a6
/ # ping -w3 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

可以看到容器container5运行在了my_network网络,并且被分配了172.18.0.0/16子网下的IP地址:172.18.0.2/16. 上面的操作还显示了运行在my_network网络里的容器container5无法和运行在bridge网络里的容器container2(IP地址为172.17.0.3)通信。

使用docker network connect命令可以将已经运行容器加入到另一个网络,如将container2加入到my_network网络:

$ docker network connect my_network container2
$ docker network inspect my_network
[
    {
        "Name": "my_network",
        "Id": "5d0513f620613f8e14d13ab44034be57ee40dada3a8a63159d8cad8c5b840ad9",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "c5bdfc331017107be4ae1bdfff12e531b3eaa3424852c4404a301b1f1c28f31b": {
                "Name": "container2",
                "EndpointID": "ea05ecf305b8e71daf608cde09a41eb2c1efe322a9c07c0c38fa0df3bd26b47c",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "d54c5dd948a66b0835fca0e5a2ade03cf912bad832a3032967a9079292cd5836": {
                "Name": "container5",
                "EndpointID": "0cf4df182681761b0fe2a07bbb71ae3fc87ebea7b8ed55831ef5c951c3bef9b9",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
$ docker attach container2
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03  
          inet addr:172.17.0.3  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:50 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:7747 (7.5 KiB)  TX bytes:1026 (1.0 KiB)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:12:00:03  
          inet addr:172.18.0.3  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe12:3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:23 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3577 (3.4 KiB)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          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:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
<span style="color:#FF0000;">172.17.0.3	c5bdfc331017
172.18.0.3	c5bdfc331017</span>
/ # ping -w3 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.126 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.096 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.096 ms

--- 172.18.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.096/0.106/0.126 ms

可以看到容器container2运行在了my_network网络,Docker引擎为该容器分配了一个新虚拟的网络设备eth1并分配了172.18.0.0/16子网下的IP地址:172.18.0.3/16. 现在container2和container5在my_network网络里可以互相通信,container2和container1在bridge网络里可以互相通信,而container1和container5由于不在同一个网络里,不能互相通信。

bridge模式下的网络适用于在单台主机上运行几个较小的相互隔离的网络。如果要创建跨主机的网络,可以使用overlay模式。

2.2 Docker overlay网络模式

本文只对Docker的overlay网络模式作简要介绍,其详细说明创建方法可参考Getting started for overlay.

Docker内置overlay网路模式驱动libnetwork,可以用于创建跨多个主机的网络。在同一个overlay网络里的容器,无论运行在哪个主机上,都能相互通信。

要创建overlay网络,首先要有一个提供key-value存储服务的服务器,当前Docker支持的key-value存储服务器有Consul, Etcd和ZooKeeper. 组成overlay网络的Docker的主机之间、Docker主机和key-value存储服务器之间都要能互通互联。在Docker主机上还要开放如下两个端口:

udp端口:4789,用于传输VXLAN网络数据的端口
tcp/udp端口:7946,用于控制平面的端口

对要加入overlay网络里里的每台Docker主机,都要完成如下配置:/span>

--cluster-store=PROVIDER://URL/span> 配置key-value存储服务器的地址
--cluster-advertise=HOST_IP|HOST_IFACE:PORT Docker所在的当前主机的地址和端口
--cluster-store-opt=KEY-VALUE OPTIONS 其他配置信息,如TLS证书等

然后用docker network create命令就能创建一个overlay网络,并能在该网络上创建容器。

$ docker network create --driver overlay my-multi-host-network
$ docker run -itd --net=my-multi-host-network busybox

作者猜想:Docker的overlay网络的原理应该是在key-value存储服务器上创建了ip地址表,当overlay网络里的容器相互通信时,通过查找key-value服务器上的ip地址标来找到目标容器所在的主机地址,经过地址转换(NAT)完成通信。待作者分析源码后看看是否猜想正确。

[Kubernetes]Docker的网络模型

2.3 定制Docker网络插件

使用Docker的插件机制,用户也能通过开发网络插件来提供定制的Docker网络驱动。使用定制的网络插件来创建Docker网络的方法与上面所述的相同,一样是使用docker network create命令。例如,weave是用户提供的定制的网络驱动:

$ docker network create --driver weave mynet
$ docker run -itd --net=mynet busybox

上面的命令。创建了一个weave驱动的网络mynet,并在上面运行了一个容器。

关于如何定制Docker的网络插件,可参考Extending DockerWriting a network driver plugin.

后续作者将介绍Docker容器DNS配置和Kubernetes的网络模型,敬请关注,谢谢。