[翻译] 理解Docker容器网络

理解Docker容器网络

翻译自Understand Docker container networks

如果你需要创建互相协作并安全的web应用,使用Docker的网络的特性可以帮你解决。定义上,网络为容器了完全的隔离性,所以,控制你的应用程序运行所在的网络很重要。Docker的网络特性变提供了你对网络的控制能力。

这篇文章将提供Docker原生的网络的行为的概览,Docker默认会创建哪些网络,以及创建自定义的自有的网络,以及在单节点或者集群上创建自定义的网络需要哪些资源。

默认网络(Default Networks)

当你安装好Docker之后, 它会自动的创建三个网络,你可以使用docker network ls命令列举这些3个网络:

$ docker network ls
NETWORK ID          NAME                DRIVER
7fca4eb8c647        bridge              bridge
9f904ee27bf5        none                null
cf03ee007fb4        host                host

最初,这3个网络是docker代码实现的一部分,你可以通过指定--net参数来指定容器启动(run)所在的网络。现在这三个网络还是同样可用的。

在所有的Docker安装版本中都会提供默认的bridge网络即docker0网络。如果你不指定docker run --net=<NETWORK>的话,Docker daemon 会默认将容器连接到这个网络。你可以通过ifconfig命令看到这个bridge是存在于宿主机(host)的网络栈中的。

$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:47:bc:3a:eb  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:47ff:febc:3aeb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
          RX packets:17 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:1100 (1.1 KB)  TX bytes:648 (648.0 B)

none网络会添加容器到一个容器自己的网络栈,然后这个容器会并没有网络接口。Attach到这样的容器上然后检查他的网络栈,会有这样的结果:

$ docker attach nonenetcontainer

root@0cb243cd1293:/# 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
root@0cb243cd1293:/# 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:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@0cb243cd1293:/#

Note: 你可以通过CTRL-p CTRL-q detach并离开容器终端。

host网络添加一个容器到宿主机(host)的网络栈中,你会发现容器中的网络的配置是和宿主机(host)是一致的。

除了bridge网络,你不需要直接的操作这些默认的网络。你可以查看他们的列表和详情,但你不能移除他们。因为他们是Docker所需要的。不过你可以添加自定义的网络以及当你不需要这个网络是删除自定义的网络。不过在你了解自定义网络之前,可以先稍微了解一下默认的bridge网络。

默认bridge网络详解

在所有的docker主机上都会提供默认的bridge网络。通过docker network inspect命令可以查看关于这个网络的信息:

$ docker network inspect bridge
[
   {
       "Name": "bridge",
       "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
       "Scope": "local",
       "Driver": "bridge",
       "IPAM": {
           "Driver": "default",
           "Config": [
               {
                   "Subnet": "172.17.0.1/16",
                   "Gateway": "172.17.0.1"
               }
           ]
       },
       "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": "9001"
       }
   }
]

Docker Enging会自动的为这个网络创建SubnetGateway的配置。然后docker run命令会将新的容器添加到这个网络中。

$ docker run -itd --name=container1 busybox
3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c

$ docker run -itd --name=container2 busybox
94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c

启动两个容器之后再通过inspect这个bridge网络可以看到在这个网络中新加入的两个容器。

$ docker network inspect bridge
{[
    {
        "Name": "bridge",
        "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {
                    "Subnet": "172.17.0.1/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Containers": {
            "3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c": {
                "EndpointID": "647c12443e91faf0fd508b6edfe59c30b642abb60dfab890b4bdccee38750bc1",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c": {
                "EndpointID": "b047d090f446ac49747d3c37d63e4307be745876db7f0ceef7b311cbba615f48",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/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": "9001"
        }
    }
]

上面的docker network inspect会展示这个网络上所有连接的容器以及他们的网络资源,在这个默认网络中的容器可以通过他们的IP互相通信。Docker在默认的bridge网络中不支持自动服务发现(automatic service discovery)。如果你需要在这个默认bridge网络中使用容器名进行通信,你必须通过过时(legacy)的docker run --link选项连接这些容器。

你可以attach到一个运行中的container然后查看他的网络配置:

$ docker attach container1

root@0cb243cd1293:/# ifconfig
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:9001  Metric:1
          RX packets:16 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:1296 (1.2 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:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

然后使用ping三秒钟测试与这个默认bridge网络中与其他容器的联通。

root@0cb243cd1293:/# 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.096 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.074 ms

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.074/0.083/0.096 ms

Finally, use the cat command to check the container1 network configuration:
最后,用cat命令检查container1网络的配置:

root@0cb243cd1293:/# cat /etc/hosts
172.17.0.2    3386a527aa08
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

使用CTRL-p CTRL-q detach container1这个容器并保持它后台运行。然后attach到container2并重复上面4个命令。

$ docker attach container2

root@0cb243cd1293:/# 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:9001  Metric:1
          RX packets:15 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:1166 (1.1 KiB)  TX bytes:1026 (1.0 KiB)

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:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

root@0cb243cd1293:/# ping -w3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.067 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.072 ms

--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.071/0.075 ms
/ # cat /etc/hosts
172.17.0.3    94447ca47985
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

默认的docker0bridge网络支持用docker run --link允许容器间直接通信,那些在技术设置复杂并易于出现错误。虽然现在在技术上仍然可以使用它们,但是最好避免使用,并使用自定义网络替代这种方案。

用户自定义网络(User-defined networks)

你可以通过创建自定义网络以便更好的容器网络隔离,Docker为创建自定义网络提供了一些默认的 network driver。你可以创建一个新的 bridge network 或者 overlay network。或者你可以编写及创建一个 network plugin 或者 remote network

你也可以创建多个网络,可以把容器连接到不止一个网络中。容器仅可以同网络内的容器进行通信而不能跨网络通信。连接到两个网络中的容器可以分别同两个网络中其他容器进行通信。

下面的几章会非常详细的介绍每个Docker内置的网络驱动。

bridge 网络

最简单的用户自定义网络就是创建一个bridge网络。这个网络类似于之前的,默认的docker0网络。有一些新加的特性以及废弃的老特性。

$ docker network create --driver bridge isolated_nw
1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b

$ docker network inspect isolated_nw
[
    {
        "Name": "isolated_nw",
        "Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {
                    "Subnet": "172.21.0.0/16",
                    "Gateway": "172.21.0.1/16"
                }
            ]
        },
        "Containers": {},
        "Options": {}
    }
]

$ docker network ls
NETWORK ID          NAME                DRIVER
9f904ee27bf5        none                null
cf03ee007fb4        host                host
7fca4eb8c647        bridge              bridge
c5ee82f76de3        isolated_nw         bridge

当以创建完网络之后,你可以通过使用docker run --net=<NETWORK>选项指定容器运行在这个网络上。

$ docker run --net=isolated_nw -itd --name=container3 busybox
8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c

$ docker network inspect isolated_nw
[
    {
        "Name": "isolated_nw",
        "Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {}
            ]
        },
        "Containers": {
            "8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c": {
                "EndpointID": "93b2db4a9b9a997beb912d28bcfc117f7b0eb924ff91d48cfa251d473e6a9b08",
                "MacAddress": "02:42:ac:15:00:02",
                "IPv4Address": "172.21.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {}
    }
]

这些你在这个网络中启动的容器必须运行在同一个Docker主机上,每个在这个网络中的容器可以直接同其他容器进行通信。不过网络将容器同外部的网络隔离开。

[翻译] 理解Docker容器网络

在一个用户自定义网络中,是不支持link的。在这个网络中你可以expose和publish一个容器的端口,这个方案可以让你的自定义网络的一部分供外部网络访问。

[翻译] 理解Docker容器网络

如果你希望在单个机器上使用一个相对小的网络,bridge网络是非常有用的。然而,如果你需要一个相对大的网络,可以通过创建overlay的网络。

overlay网络(An overlay network)

Docker的overlay网络驱动提供原生开箱即用(out-of-the-box)的跨主机网络。完成这个支持是基于libnetwork和Docker的libkvlibnetwork是一个内置基于VXLAN overlay网络驱动的一个库。

overlay网络需要一个可用的key-value存储的服务。目前Docker的libkv支持Consul,Etcd,和ZooKepper(分布式存储),在创建网络之前,你必须安装以及配置你所选的key-value存储服务。然后你需要创建网络的Docker机器以及存储服务的机器必须能够互相通信。

[翻译] 理解Docker容器网络

网络中的每个宿主机必须运行有Docker engine实例,最简单的创建运行Docker engine实例的方法是使用Docker Machine。

[翻译] 理解Docker容器网络

你在主机之间需要打开下面这些端口。

Protocol Port Description
udp 4789 Data plane (VXLAN)
tcp/udp 7946 Control plane

你的Key-value存储服务可能需要额外的一些端口,可以检查服务提供者的文档然后打开需要的端口。

一旦你有很多的机器创建出来,你可以使用Docker Swarm快速的形成一个集群。

为了创建overlay网络,你需要为每台机器上的Docker Engine的daemon配置overlay网络所需的选项。下面就是需要设置的三个选项。

Option Description
--cluster-store=PROVIDER://URL
Describes the location of the KV service.
--cluster-advertise=HOST_IP|HOST_IFACE:PORT
The IP address or interface of the HOST used for clustering.
--cluster-store-opt=KEY-VALUE OPTIONS
Options such as TLS certificate or tuning discovery Timers

在Swarm集群中的一个节点上创建overlay网络

$ docker network create --driver overlay my-multi-host-network

这个的结果就是创建了一个跨越多个节点的网络。一个overlay网络为容器提供了完全的网络隔离。

[翻译] 理解Docker容器网络

然后在每台主机上,通过制定网络名启动容器。

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

连接上之后,每个容器便可以可以互相访问这个网络中的其他容器,不管另外的容器是在那一台主机上。

[翻译] 理解Docker容器网络

如果你想自己尝试下这个流程的话,可以参考Getting started for overlay.

自定义网络插件(Custom network plugin)

你可以编写自己的网络驱动,驱动是和Docker daemon运行在同一台主机上一个进程,并且由Docker plugin系统调用和使用。

网络插件和其他的Docker插件一样受到一些限制和安装规则。所有的插件使用Plugin API,有自己的生命周期,包含:安装,启动,停止,激活。

当你创建并安装了自定义的网络驱动是,你可以像内置的网络驱动一样使用它,例如:

$ docker network create --driver weave mynet

你可以在这个网络上做查看(inspect),添加删除容器等操作。当然,不同的驱动使用不同的技术和框架,所以自定义网络也会包含很多Docker默认网络没有的特性。如果需要了解更多关于编写插件的内容,可以参考Extending DockerWriting a network driver plugin.

Docker内置DNS服务器(embedded DNS server)

Docker damon 会为每个连接到自定义网络的容器运行一个内置的DNS服务提供自动的服务发现。域名解析的请求会首先被内置的DNS服务器拦截,如果内置的DNS服务器不能够解析这个请求,它才会转发到外部的容器配置的DNS服务器。以便于这个机制,容器的resolv.conf文件会将DNS服务器配置为127.0.0.11,既内置DNS服务器监听的地址。更多关于在自定义网络中的内置DNS服务器可以参考embedded DNS server in user-defined networks

Links

在Docker有这些网络特性之前,你需要使用Docker link的功能让容器间可以互相发现。有了Docker网络特性之后,容器可以通过容器名自动的互相发现。不过你可以继续使用link在自定义网络中,不过这个link的行为会和默认的docker0 bridge网络上的行为不同。关于更多的信息和对比,可以参考:默认的docker0bridge上的Legacy Links,以及在用户自定义网络中的linking containers in user-defined networks

相关链接

想了解更多容器服务的内容,请点击https://www.aliyun.com/product/containerservice

上一篇:Docker 容器的网络连接


下一篇:虚拟化VMWare问题集锦之一: vSphere Client无法连接到"192.168.1.224"