Docker的网络

这节是对Docker的网络行为进行概括,包括默认创建的所有网络类型和如何创建自己网络,同时描述了如何使用网络在一台主机或者集群中

默认网络

当你安装docker后,会自动创建3个网络设备,你可以通过命令行查看他们

$ docker network ls

NETWORK ID          NAME                DRIVER
7fca4eb8c647        bridge              bridge
9f904ee27bf5        none                null
cf03ee007fb4        host                host

这三个网络设备创建到Docker中。当你启动一个容器的时候,你可以通过--network去设置容器使用的网络设备

在Docker中桥接网络(bridge network)是默认的,除非你通过--network=选项,docker的守护进程默认会让容器连接这个桥接网络。在宿主机器中你可以通过ifconfig命令看到这个桥接网络

$ 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 network)容器中,容器是没有网络设备的,可以连接这个容器,执行如下命令:

$ 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:/#

注意:你可以按ctrl-p ctrl-q 退出容器

在一个主机网络(host network)容器中,主机和容器是没有隔离的。例如,你在容器中启动一个服务监听80端口,那么在宿主机中80端口是可以访问的。

无网络(none network)和主机网络(host network)在Docker中是不可以直接配置的,但是你可以配置默认桥接网络像配置自定义桥接网络一样。

默认的桥接网络

这个默认的桥接网络(bridge network)会出现在所有Docker中,如果你没有特殊设置网络,新的docker容器都会连接到这个桥接网络(bridge network)

docker的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"
       },
       "Labels": {}
   }
]

运行下面两条命令启动两个桥接网络(bridge network)的busybox 容器

$ docker run -itd --name=container1 busybox

3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c

$ docker run -itd --name=container2 busybox

94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c

在两个容器启动后再次运行inspect命令,会看到两个busybox的容器连接到网络上。他们的ip地址会在宿主机器中是不同的

$ 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"
        },
        "Labels": {}
    }
]

连接到默认桥接网络容器可以通过ip地址彼此通信。docker在默认桥接网络中不支持自动服务发现( automatic service discovery),如果你想让容器通过容器名称自动解析ip,你应该创建自定义网络。可以通过--link参数让两个容器连接起来,但是这个大多数时候是不推荐的。

你可以连接到一个运行的容器中,看到容器的网络状态。连接后是root权限,所以可以看见命令行是#开头的

$ docker attach container1

root@0cb243cd1293:/# 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命令测试是否可以连接到其他容器

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

在容器中通过cat命令查看/etc/hosts文件。可以看到容器中域名和ip地址的对应关系

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可以脱离容器的连接,你可以重复上述命令在另一个容器中.

通过--link参数可以配置hosts中的ip和容器对应关系,在默认的桥接网络中,但是这个是不推荐的。如果可能你应该用自定义桥接网络替换默认桥接网络。

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

用户自定义桥接网络是被推荐的,用来控制哪些容器可以连接彼此,同时支持自动dns解析容器名称和ip地址的对应关系。Docker提供默认的网络驱动去创建自定义网络。你可以创建一个新的自定义桥接网络,覆盖默认网络或者MACVLAN(mac虚拟网络)。你同时可以创建一个网络插件或者完全定制化和控制的远程网络。

You can create as many networks as you need, and you can connect a container to zero or more of these networks at any given time. In addition, you can connect and disconnect running containers from networks without restarting the container. When a container is connected to multiple networks, its external connectivity is provided via the first non-internal network, in lexical order.
如果需要你可以创建任意多的自定义网络,同时你可以在任意时间使一个容器同时连接零或多个自定义网络中,同时你可以连接或者断开一个容器的网络,而不用重启容器。当一个容器链接到多个网络中的时候,他的外部网络访问时通过一个非内部网络提供。

下面一些章节描述了Docker内置的所有网络驱动细节

桥接网络

桥接网络是docker中做常用的。自定义桥接网络几乎和默认桥接网络一致,只是有一些新的功能和移除了一些老的特性。下面的例子中创建了一些自定义桥接网络和容器应用

$ 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": {},
        "Labels": {}
    }
]

$ docker network ls

NETWORK ID          NAME                DRIVER
9f904ee27bf5        none                null
cf03ee007fb4        host                host
7fca4eb8c647        bridge              bridge
c5ee82f76de3        isolated_nw         bridge

当你创建了网络,通过--network=参数,你可以让容器应用这个网络

$ docker run --network=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": {},
        "Labels": {}
    }
]

在同一台Docker宿主机中和这个网络中的所有容器,可以快速的彼此通信。但是在这个网络外的容器是不可以访问的。


Docker的网络_第1张图片
An isolated network

在自定义桥接网络中 link是不允许的。在这个自定义桥接网络中你可以导出和发布这些容器的端口,你这样可以在自定义桥接网络外访问这些端口。


Docker的网络_第2张图片
Bridge network

如果你想创建一个小型的网络在单个宿主机上,自定义桥接网络是很有用的。如果想创建一个大型网络结构,那么需要通过创建覆盖网络(overlay network)。

docker_gwbridge网关桥接网络

docker_gwbridge网关桥接网络是一个本地桥接网络,由docker在两个不同的主机环境中自动创建的

  • 当你初始化或者叫加入一个集群,Docker会创建一个docker_gwbridge网络,用来集群中的不同宿主机节点的通信
  • 当docker容器的网络中没有一个提供外部链接时,Docker链接这些容器到docker_gwbridge网络用来和外部容器通信,这样这些容器就可以链接到外部网络或者其它的集群节点。

你可以创建一个自定义配置的docker_gwbridge网络,下面的例子展示如何创建一个自定义配置的docker_gwbridge网络

$ docker network create --subnet 172.30.0.0/16 \
                        --opt com.docker.network.bridge.name=docker_gwbridge \
            --opt com.docker.network.bridge.enable_icc=false \
            docker_gwbridge

当你使用覆盖网络时,docker_gwbridge 网关桥接网络总会出现。

覆盖网络在集群模式中

在集群模式中,不需要key-value缓存,你可以创建一个覆盖网络在一个集群管理节点。在集群中所有节点都可以访问覆盖网络。当你在覆盖网络中创建一个服务,管理节点可以自动扩展覆盖网络到其它集群节点。

学习更多关于docker集群模式可以看 集群模式预览.
下面是如何创建一个覆盖网络并用它作为集群服务的网络

$ docker network create \
  --driver overlay \
  --subnet 10.0.9.0/24 \
  my-multi-host-network

400g6bwzd68jizzdx5pgyoe95

$ docker service create --replicas 2 --network my-multi-host-network --name my-web nginx

716thylsndqma81j6kkkb5aus

只有集群服务可以连接覆盖网络,单独的容器是不可以的。更多关于集群的信息,请查看 Docker集群模式和覆盖网络的安全 和 在覆盖网络中连接容器.

覆盖网络在非集群模式中

如果你的Docker机器不是用在集群模式中,那么覆盖网络需要一个key-value缓存服务。支持的key-value缓存服务包括Consul, Etcd,和 ZooKeeper (Distributed store).在创建这个覆盖网络之前,你必须安装和配置你的key-value缓存服务。在宿主机器必须可以上网,而且key-value缓存服务是可以访问的。

注意: Docker在集群模式中是不需要key-value缓存服务的

不在集群模式下使用覆盖网络是不推荐的。但是在单宿主机中和Docker开发者这种模式可能很有用。在未来它很可能被删掉。如果你需要在这种模式中启动,可以查看 介绍.

自定义网络插件

如果你的需求上述网络都不能满足,那么你可以根据docker的插件基础开发自己的网络插件。这个插件将会单独运行在一个进程里。用网络插件是一个高级的课题。

网络插件跟其他docker插件有一样的规则和约束。所有的插件都用同一套API,有同样的生命周期,包括安装,启动,停止,和激活。

一旦你创建并安装了一个自定义网络插件,你可以通过driver参数指定通过插件创建你需要的网络。

$ docker network create --driver weave mynet

你可以检查一个网络,容器跟网络的链接和断开链接,或者移除他。网络插件可能有自己的特殊依赖,检查插件的文档获取信息。写网络插件的更多信息打开链接 docker扩展 和写一个网络插件.

内置dns服务

docker守护进程会运行一个内置的dns服务,用来提供在同一个网络下容器之间可以通过域名(容器名称)解析彼此,即把容器名解析成ip。如果内置的dns服务器不能解析,则会交给外部主机的dns解析。当容器创建的时候,内置的dns服务的ip是127.0.0.11 容器域名会列在resolv.conf文件里。获取更多关于内置dns服务的信息,看 内置dns服务在自定义网络的使用

暴露和发布端口

docker的网络环境中,有两种方式开放端口,发布端口和暴露端口。在默认桥接网络和自定义网络都可以。

  • 暴露端口通过EXPOSE关键字在Dockerfile或者通过docker run --expose参数
  • 发布端口通过PUBLISH关键字在Dockerfile中,或者通过docker run的--publish参数。docker会根据这些参数或者关键字打开端口给网络接口。当发布端口后,宿主机会有一个大于30000的端口与之对应,除非你在运行时指定宿主机哪个端口与之对应。 你不能在Dockerfile中指定对应端口,因为不能确定宿主机那个端口是否可用。

下面是发布80端口的例子,会有随机的一个大于30000的端口与之对应

$ docker run -it -p 80 nginx
$ docker ps
64879472feea        nginx               "nginx -g 'daemon ..."   43 hours ago
       Up About a minute   443/tcp, 0.0.0.0:32768->80/tcp   blissful_mclean

这个例子是指定80端口与8080端口对应,如果8080不可用,会失败。

$ docker run -it -p 80:8080 nginx
$ docker ps
b9788c7adca3        nginx               "nginx -g 'daemon ..."   43 hours ago
        Up 3 seconds        80/tcp, 443/tcp, 0.0.0.0:80->8080/tcp   goofy_brahmagupta

链接Links

在Docker提供自定义网络前,你只能通过--link功能让容器间网通过容器名称解析ip地址,也让两个容器可以互相通信。现在你应该避免使用--link标记。

当你创建links时,它和自定义桥接网络是不同的。更多信息查看 link功能在默认桥接网络的传统链接 和 用户自定义桥接网络容器通信自定义桥接网络中容器通信

你可能感兴趣的:(Docker的网络)