阅读 579

Docker中的网络配置

Docker容器和服务如此强大的原因之一是您可以将它们连接在一起,或将它们连接到非Docker工作负载。 Docker容器和服务甚至不需要知道它们已部署在Docker上,也不必知道它们的对等对象是否也是Docker工作负载。 无论您的Docker主机运行Linux,Windows还是两者结合,您都可以使用Docker以与平台无关的方式管理它们。

本主题定义了一些基本的Docker网络概念,并帮助您设计和部署应用程序以充分利用这些功能。

网络驱动

Docker的网络子系统使用驱动程序是可插拔的。默认情况下存在几个驱动程序,并提供核心网络功能:

  • bridge:默认的网络驱动程序。 如果您未指定驱动程序,则这是您正在创建的网络类型。当您的应用程序在需要通信的独立容器中运行时,通常会使用网桥网络。
  • host:对于独立容器,去掉容器和Docker主机之间的网络隔离,直接使用主机的网络。host仅可用于Docker 17.06及更高版本上的集群服务。
  • overlay:overlay网络将多个Docker守护程序连接在一起,并使群集服务能够相互通信。您还可以使用overlay网络来促进群集服务和独立容器之间或不同Docker守护程序上的两个独立容器之间的通信。这种策略消除了在这些容器之间进行操作系统级路由的需要
  • macvlan:Macvlan网络允许您将MAC地址分配给容器,使其在网络上显示为物理设备。 Docker守护程序通过其MAC地址将流量路由到容器。 在处理希望直接连接到物理网络而不是通过Docker主机的网络堆栈进行路由的传统应用程序时,使用macvlan驱动程序有时是最佳选择。
  • none:对于此容器,禁用所有联网。 通常与自定义网络驱动程序一起使用。none不适用于swarm services.
  • 网络插件:您可以在Docker中安装和使用第三方网络插件。 这些插件可从Docker Hub或第三方供应商处获得。 有关安装和使用给定网络插件的信息,请参阅供应商的文档。

网络驱动总结

  • 当需要多个容器在同一Docker主机上通信时,用户定义的桥接网络是最佳选择。
  • 当网络堆栈不应该与Docker主机隔离,而希望隔离容器的其他方面时,主机网络是最佳选择。
  • 当您需要在不同Docker主机上运行的容器进行通信时,或者当多个应用程序使用群服务一起工作时,overlay网络是最好的。
  • 从VM设置迁移或需要容器看起来像网络上的物理主机时,Macvlan网络是最好的,每个主机都有唯一的MAC地址。
  • 第三方网络插件使您可以将Docker与专用网络堆栈集成。

独立Docker容器的连网

这一部分包含三个不同的指南,你可以在不同系统上运行他们,但是对于最后两个,你需要额外的Docker主机运行。

  • 使用默认的桥接网络:这一章节演示如何使用Docker自动为您设置的默认桥接网络。 该网络不是生产系统的最佳选择。
  • 使用用户定义的桥网络:显示了如何创建和使用自己的自定义桥网络,以连接在同一Docker主机上运行的容器。 建议将其用于生产中运行的独立容器。

尽管overlay网络通常用于集群服务,但Docker 17.06及更高版本允许您将overlay网络用于独立容器。 这是使用overlay网络的教程的一部分。

使用默认的桥接网络

在此示例中,您在同一Docker主机上启动了两个不同的alpine容器,并进行了一些测试以了解它们如何相互通信。 您需要安装并运行Docker。

1.打开一个终端窗口。 在执行其他任何操作之前,请先列出当前网络。 如果您从未在此Docker守护程序上添加网络或初始化群组,则应该看到以下内容。 您可能会看到不同的网络,但至少应该看到以下内容(网络ID会有所不同):

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
17e324f45964        bridge              bridge              local
6ed54d316334        host                host                local
7092879f2cc8        none                null                local
复制代码

列出了默认的桥接网络,以及hostnone。 后两个不是完全成熟的网络,但用于启动直接连接到Docker守护程序主机的网络堆栈的容器,或用于启动不包含网络设备的容器。 本教程将把两个容器连接到桥接网络。

2.开启两个alpine容器运行ash,它是Alpine默认的shell,而不是bash. -dit标志的意思是启动分离的容器(在后台),交互的(具有键入的能力)和TTY(以便您可以看到输入和输出)。由于您是分开启动的,因此您不会立即连接到该容器。而是将打印容器的ID。 因为你未指定任何--network选项,所以容器将连接到默认的网桥网络。

$ docker run -dit --name alpine1 alpine ash

$ docker run -dit --name alpine2 alpine ash
复制代码

检查两个容器是否确实已启动:

$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
602dbf1edc81        alpine              "ash"               4 seconds ago       Up 3 seconds                            alpine2
da33b7aa74b0        alpine              "ash"               17 seconds ago      Up 16 seconds                           alpine1
复制代码

3.检查网桥网络以查看连接了哪些容器。

$ docker network inspect bridge

[
    {
        "Name": "bridge",
        "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
        "Created": "2017-06-22T20:27:43.826654485Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
            "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": {
                "Name": "alpine2",
                "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": {
                "Name": "alpine1",
                "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5",
                "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": {}
    }
]
复制代码

在顶部附近,列出了有关桥接网络的信息,包括Docker主机和桥接网络之间的网关的IP地址(172.17.0.1)。 在Containers项下,列出了每个连接的容器及其IP地址信息(lpine1172.17.0.2alpine2172.17.0.3

4.容器在后台运行。 使用docker attach命令连接到alpine1

$ docker attach alpine1

/ #
复制代码

提示符将更改为以指示您是容器中的root用户。 使用ip addr show命令从容器中查看alpine1的网络接口:

# ip addr show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link
       valid_lft forever preferred_lft forever
复制代码

第一个接口是环回设备。现在先忽略它。注意,第二个接口的IP地址是172.17.0.2,这与前面步骤中显示的alpine1的地址相同。

5.在alpine1内部,通过ping baidu.com确保您可以连接到互联网。 -c 2标志将命令限制为两ping尝试。

➜  ~ ping -c 2 baidu.com
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: icmp_seq=0 ttl=46 time=34.887 ms
64 bytes from 39.156.69.79: icmp_seq=1 ttl=46 time=41.880 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 34.887/38.383/41.880/3.497 ms
➜  ~ 
复制代码

6.现在尝试ping第二个容器。 首先,通过其IP地址172.17.0.3对其执行ping操作:

# ping -c 2 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.086 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms

--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.086/0.090/0.094 ms
复制代码

这样成功了。 接下来,尝试按容器名称ping alpine2容器。 这将失败。

# ping -c 2 alpine2

ping: bad address 'alpine2'
复制代码

7.通过使用分离序列CTRL + p CTRL + q(按住CTRL并键入p后跟q)从alpine1分离而不停止它。 如果愿意,请附加到alpine2并在那里重复步骤4、5和6,将alpine1代替为alpine2

8.停止并删除两个容器。

$ docker container stop alpine1 alpine2
$ docker container rm alpine1 alpine2
复制代码

请记住,不建议将默认网桥网络用于生产。

使用用户定义的桥接网络

在此示例中,我们再次启动两个Alpine容器,但是将它们附加到我们已经创建的名为alpine-net的用户定义网络上。 这些容器根本没有连接到默认桥接网络。 然后,我们启动连接到默认网桥网络但未连接alpine-net的第三个alpine容器,以及连接到两个网络的第四个alpine容器。

1.创建 alpine-net网络. 你不需要指定--driver bridge标签因为它是默认的,但本实例展示了如何指定它:

$ docker network create --driver bridge alpine-net
复制代码

2.列出Docker网络

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
e9261a8c9a19        alpine-net          bridge              local
17e324f45964        bridge              bridge              local
6ed54d316334        host                host                local
7092879f2cc8        none                null                local
复制代码

检查alpine-net网络。这显示了它的IP地址,以及没有容器连接到它的事实.


$ docker network inspect alpine-net

[
    {
        "Name": "alpine-net",
        "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
        "Created": "2017-09-25T21:38:12.620046142Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
复制代码

请注意,该网络的网关是172.18.0.1,而默认网桥网络的网关是172.17.0.1。 您系统上的确切IP地址可能不同。

3.创建你的第四个容器,注意--network选项。在docker run命令中你只能连接到一个网络。所以,你需要使用docker network connect命令将第四个容器连接到默认桥接网络上。

$ docker run -dit --name alpine1 --network alpine-net alpine ash

$ docker run -dit --name alpine2 --network alpine-net alpine ash

$ docker run -dit --name alpine3 alpine ash

$ docker run -dit --name alpine4 --network alpine-net alpine ash

$ docker network connect bridge alpine4
复制代码

验证所有容器都在运行:

$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
156849ccd902        alpine              "ash"               41 seconds ago       Up 41 seconds                           alpine4
fa1340b8d83e        alpine              "ash"               51 seconds ago       Up 51 seconds                           alpine3
a535d969081e        alpine              "ash"               About a minute ago   Up About a minute                       alpine2
0a02c449a6e9        alpine              "ash"               About a minute ago   Up About a minute                       alpine1
复制代码

4.再次检查默认网桥网络和alpine-net网:

$ docker network inspect bridge

[
    {
        "Name": "bridge",
        "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
        "Created": "2017-06-22T20:27:43.826654485Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
            "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
                "Name": "alpine4",
                "EndpointID": "7277c5183f0da5148b33d05f329371fce7befc5282d2619cfb23690b2adf467d",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "fa1340b8d83eef5497166951184ad3691eb48678a3664608ec448a687b047c53": {
                "Name": "alpine3",
                "EndpointID": "5ae767367dcbebc712c02d49556285e888819d4da6b69d88cd1b0d52a83af95f",
                "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": {}
    }
]
复制代码

alpine3alpine4 容器连接到了默认的桥接网络

$ docker network inspect alpine-net

[
    {
        "Name": "alpine-net",
        "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
        "Created": "2017-09-25T21:38:12.620046142Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
            "0a02c449a6e9a15113c51ab2681d72749548fb9f78fae4493e3b2e4e74199c4a": {
                "Name": "alpine1",
                "EndpointID": "c83621678eff9628f4e2d52baf82c49f974c36c05cba152db4c131e8e7a64673",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
                "Name": "alpine4",
                "EndpointID": "058bc6a5e9272b532ef9a6ea6d7f3db4c37527ae2625d1cd1421580fd0731954",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "a535d969081e003a149be8917631215616d9401edcb4d35d53f00e75ea1db653": {
                "Name": "alpine2",
                "EndpointID": "198f3141ccf2e7dba67bce358d7b71a07c5488e3867d8b7ad55a4c695ebb8740",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
复制代码

alpine1, alpine2, 和 alpine4容器连接到了alpine-net 网络.

5。在像alpine-net这样的用户定义网络上,容器不仅可以按IP地址进行通信,而且还可以将容器名称解析为IP地址。 此功能称为自动服务发现。 让我们连接到alpine1并进行测试。 alpine1应该能够将alpine2alpine4(以及alpine1本身)解析为IP地址。

$ docker container attach alpine1

# ping -c 2 alpine2

PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.085 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.090 ms

--- alpine2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.085/0.087/0.090 ms

# ping -c 2 alpine4

PING alpine4 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.076 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.091 ms

--- alpine4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.076/0.083/0.091 ms

# ping -c 2 alpine1

PING alpine1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.026 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.054 ms

--- alpine1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.026/0.040/0.054 ms

复制代码

6.从 alpine1, 你无法连接到alpine3, 因为它不在alpine-net网内.

# ping -c 2 alpine3

ping: bad address 'alpine3'
复制代码

不仅如此,你也不能通过它的IP地址从alpine1连接到alpine3。查看docker network inspect的输出,找到alpine3的IP地址:172.17.0.2,试着ping它。

# ping -c 2 172.17.0.2

PING 172.17.0.2 (172.17.0.2): 56 data bytes

--- 172.17.0.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
复制代码

使用分离序列CTRL + p CTRL + q(按住CTRL并键入p后跟q)从alpine1分离。

7.请记住,alpine4已连接到默认网桥网络和alpine-net。 它应该能够到达所有其他容器。 但是,您将需要按其IP地址寻址alpine3。 附加到它并运行测试。

$ docker container attach alpine4

# ping -c 2 alpine1

PING alpine1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.082 ms

--- alpine1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.074/0.078/0.082 ms

# ping -c 2 alpine2

PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.075 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.080 ms

--- alpine2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.075/0.077/0.080 ms

# ping -c 2 alpine3
ping: bad address 'alpine3'

# ping -c 2 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.089 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms

--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.075/0.082/0.089 ms

# ping -c 2 alpine4

PING alpine4 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.033 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.064 ms

--- alpine4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.033/0.048/0.064 ms

复制代码

8.作为最后一个测试,通过ping www.baidu.com确保所有的容器都可以连接到网络.因为当前你已经连接到了alpine4,就从它开始,然后退出alpine4并连接到alpine3 (该容器仅仅连接到默认的桥接网络) 进行重试。最后连接到alpine1 (它仅仅连接到alpine-net网络)并进行重试.

# ping -c 2 baidu.com

PING baidu.com (172.217.3.174): 56 data bytes
64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.778 ms
64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.634 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 9.634/9.706/9.778 ms

CTRL+p CTRL+q

$ docker container attach alpine3

# ping -c 2 baidu.com

PING baidu.com (172.217.3.174): 56 data bytes
64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.706 ms
64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.851 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 9.706/9.778/9.851 ms

CTRL+p CTRL+q

$ docker container attach alpine1

# ping -c 2 baidu.com

PING baidu.com (172.217.3.174): 56 data bytes
64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.606 ms
64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.603 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 9.603/9.604/9.606 ms

CTRL+p CTRL+q

复制代码

9.最后停止并删除所有容器并删除alpine-net网络

$ docker container stop alpine1 alpine2 alpine3 alpine4

$ docker container rm alpine1 alpine2 alpine3 alpine4

$ docker network rm alpine-net
复制代码

overlay network联网

本系列教程讨论集群服务的网络。本主题包含四个部分内容,你可以在不同的平台上运行他们,但是对于最后两个,你需要一个额外的Docker主机。

  • 使用默认overlay network演示了如何在初始化或加入群集时使用Docker自动为您设置的默认的overlay network。 该网络不是生产系统的最佳选择。
  • 使用用户定义的overlay network显示了如何创建和使用自己的自定义overlay network来连接服务。 建议将其用于生产中运行的服务。
  • overlay network用于独立容器显示了如何使用overlay network在不同Docker守护程序上的独立容器之间进行通信。
  • 容器与群集服务之间的通信使用可连接的overlay network在独立容器与群集服务之间建立通信。 Docker 17.06及更高版本支持此功能。

准备工作

这些要求您至少有一个单节点群集,这意味着您已启动Docker并在主机上运行docker swarm init。 您也可以在多节点集群上运行示例。 最后一个示例需要Docker 17.06或更高版本。

使用默认的overlay network

在本例中,您启动一个alpine服务,并从各个服务容器的角度分析网络的特征。

本教程不讨论有关如何实现overlay network的特定于操作系统的详细信息,而是重点关注从服务的角度来看,overlay network是如何工作的。

准备

本教程需要三台物理或虚拟Docker主机,它们可以相互通信,并且都运行Docker 17.03或更高版本的新安装。 本教程假定这三台主机在同一网络上运行,并且不涉及防火墙。

这些主机将被称为manager, worker-1,worker-2.manager主机即作为管理者又作为工作者,这意味着它既要运行服务任务,也要管理swarm. worker-1worker-2 将仅作为工作者。

如果您没有三台主机,一个简单的解决方案是在云提供商(例如Amazon EC2)上设置三台Ubuntu主机,它们全部位于同一网络上,并允许与该网络上的所有主机进行所有通信(使用诸如 EC2安全组),然后按照Ubuntu上Docker Engine-Community的安装说明进行操作。

演示

创建swarm

在此过程结束时,所有三个Docker主机都将加入集群,并使用称为ingress的覆盖网络连接在一起.

1.在manager上,初始化swarm集群。如果主机只有一个网络接口,--advertise-addr 选项是可选的。

$ docker swarm init --advertise-addr=<IP-ADDRESS-OF-MANAGER>
复制代码

记下所打印的文本,因为其中包含将用于将worker-1和worker-2加入到群中的令牌。 将令牌存储在密码管理器中是一个好主意。

2.在worker-1上,加入到swarm。如果主机只有一个网络接口,--advertise-addr 选项是可选的.

$ docker swarm join --token <TOKEN> \
  --advertise-addr <IP-ADDRESS-OF-WORKER-1> \
  <IP-ADDRESS-OF-MANAGER>:2377
复制代码

3.在worker-2上,加入到swarm。如果主机只有一个网络接口,--advertise-addr 选项是可选的.

$ docker swarm join --token <TOKEN> \
  --advertise-addr <IP-ADDRESS-OF-WORKER-2> \
  <IP-ADDRESS-OF-MANAGER>:2377
复制代码

4.在manager上, 列出所有的节点。这个命令只能在manager上执行。

$ docker node ls

ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
d68ace5iraw6whp7llvgjpu48 *   ip-172-31-34-146    Ready               Active              Leader
nvp5rwavvb8lhdggo8fcf7plg     ip-172-31-35-151    Ready               Active
ouvx2l7qfcxisoyms8mtkgahw     ip-172-31-36-89     Ready               Active
复制代码

您也可以使用--filter标志按角色进行过滤:

$ docker node ls --filter role=manager

ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
d68ace5iraw6whp7llvgjpu48 *   ip-172-31-34-146    Ready               Active              Leader

$ docker node ls --filter role=worker

ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
nvp5rwavvb8lhdggo8fcf7plg     ip-172-31-35-151    Ready               Active
ouvx2l7qfcxisoyms8mtkgahw     ip-172-31-36-89     Ready               Active
复制代码

5.列出managerworker-1,worker-2上所有的网络,请注意它们现在都有一个叫做ingress的覆盖网络和一个叫docker_gwbridge的桥接网络。这里只展示manager的:

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
495c570066be        bridge              bridge              local
961c6cae9945        docker_gwbridge     bridge              local
ff35ceda3643        host                host                local
trtnl4tqnc3n        ingress             overlay             swarm
c8357deec9cb        none                null                local
复制代码

docker_gwbridgeingress网络连接到Docker主机的网络接口,以便流量可以往返于群管理者和工作者。如果创建群集服务但未指定网络,则它们将连接到ingress网络.建议对将一起使用的每个应用程序或一组应用程序使用单独的覆盖网络。在下一个过程中,您将创建两个覆盖网络并将服务连接到每个覆盖网络。

创建服务

1.在manager上,创建一个新的overlay network叫nginx-net:

$ docker network create -d overlay nginx-net
复制代码

您无需在其他节点上创建overlay网络,因为当其中一个节点开始运行需要该overlay网络的服务任务时,overlay网络会自动创建。

2.在manager上, 创建一个有5个副本的Nginx服务并连接到nginx-net.服务将对外开放80端口。所有服务任务容器都可以在不打开任何端口的情况下彼此通信。

服务只能在管理器上创建

$ docker service create \
  --name my-nginx \
  --publish target=80,published=80 \
  --replicas=5 \
  --network nginx-net \
  nginx
复制代码

ingress的默认发布模式, 在你没有为--publish选项指定一个mode时使用。这意味着你如果访问manager,worker-1或者worker-2上的80端口,你将连接到5个服务任务中的任何一个的80端口上。即便当前并没有任务运行在你访问的节点上。如果你想使用host模式发布端口,你可以在--publish选项上增加mode=host。但是,这种情况下,你要用--mode global代替 --replicas=5, 因为只有一个服务任务可以绑定到给定节点上的给定端口。

3.运行docker service ls监视服务启动进度,这可能需要几秒钟。

4.在manage,worker-1,worker-2上检查nginx-net网络. 记住你不需要在worker-1worker-2上手动创建它,因为Docker会给你创建好。输出会很长,但是只需注意ContainersPeers部分。Containers列出了所有的从该主机连接到overlay网络的服务任务(或者单独的容器)。

5.对于manager,使用docker service inspect my-nginx检查服务,并注意有关服务使用的端口和端点的信息。

6.创建一个新的网络nginx-net-2, 然后使用这个新的网络更新服务:

$ docker network create -d overlay nginx-net-2
复制代码
$ docker service update \
  --network-add nginx-net-2 \
  --network-rm nginx-net \
  my-nginx
复制代码

7.运行docker service ls检查服务已经更新了,并且所有的任务已经重新部署。运行 docker network inspect nginx-net验证没有容器连接到它。对nginx-net-2运行相同的命令,注意所有的服务任务容器都连接到它。

overlay网络在swarn工作节点上是按需自动创建的,他们不会自动删除

8.清理服务和网络。 在管理器中,运行以下命令。 管理者将指示工作人员自动删除网络。

$ docker service rm my-nginx
$ docker network rm nginx-net nginx-net-2
复制代码

使用用户自定义的overlay网络

准备

本教程假设群集已经设置好,并且您是在一个管理器上。

演示

1.创建用户定义的覆盖网络。

$ docker network create -d overlay my-overlay
复制代码

2.使用overlay网络启动一个服务并将端口80发布到Docker主机上的8080上

$ docker service create \
  --name my-nginx \
  --network my-overlay \
  --replicas 1 \
  --publish published=8080,target=80 \
  nginx:latest
复制代码

3.运行docker network inspect my-overlay验证my-nginx服务任务已经连接到它了。通过查看Containers部分。

4.删除服务和网络

$ docker service rm my-nginx

$ docker network rm my-overlay
复制代码

在独立容器上使用overlay网络

此示例演示了DNS容器发现-具体地说,是如何使用覆盖网络在不同Docker守护程序上的独立容器之间进行通信。 步骤如下:

  • host1上, 初始化节点为一个swarm(manager).
  • host2上, 加入该节点到swarm中(作为工作节点)
  • host1上,创建一个可连接的覆盖网络(test-net)。
  • host1上,在test-net上运行一个交互式alpine容器 (alpine1)。
  • host2上,在test-net上运行一个交互式,可分离的alpine容器 (alpine2)。
  • host1上, 从一个alpine1会话中,ping alpine2

准备

对于此测试,您需要两个可以相互通信的不同Docker主机。 每个主机必须具有Docker 17.06或更高版本,并且在两个Docker主机之间打开以下端口:

  • TCP port 2377
  • TCP and UDP port 7946
  • UDP port 4789

一种简单的设置方法是拥有两个VM(本地或在AWS等云提供商上),每个VM均已安装并正在运行Docker。 如果您使用的是AWS或类似的云计算平台,最简单的配置是使用一个安全组,该安全组打开两个主机之间的所有传入端口以及来自客户端IP地址的SSH端口。

此示例将我们群中的两个节点称为host1host2。 此示例还使用Linux主机,但是相同的命令在Windows上也可以使用。

演示

1.设置swarm 在host1上,初始化集群(如果出现提示,请使用--advertise-addr指定与集群中其他主机通信的接口的IP地址,例如AWS上的私有IP地址):

$ docker swarm init
Swarm initialized: current node (vz1mm9am11qcmo979tlrlox42) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5g90q48weqrtqryq4kj6ow0e8xm9wmv9o6vgqc5j320ymybd5c-8ex8j0bc40s6hgvy5ui5gl4gy 172.31.47.252:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
复制代码

host2上,按照上述说明加入群集:

$ docker swarm join --token <your_token> <your_ip_address>:2377
This node joined a swarm as a worker.
复制代码

如果节点无法加入群集,则docker swarm join命令会超时。 要解决此问题,请在host2上运行docker swarm leave --force,验证您的网络和防火墙设置,然后重试。

2.在host1上, 创建一个可以连接的overlay网络叫test-net:

$ docker network create --driver=overlay --attachable test-net
uqsof8phj3ak0rq9k86zta6ht
复制代码

注意返回的NETWORK ID-从host2连接到它时,您将再次看到它。

3.在host1上,启动一个交互式的容器alpine1连接到test-net:

$ docker run -it --name alpine1 --network test-net alpine
/ #
复制代码

4.在host2上, 罗列出可用的网络--注意test-net还不存在:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
ec299350b504        bridge              bridge              local
66e77d0d0e9a        docker_gwbridge     bridge              local
9f6ae26ccb82        host                host                local
omvdxqrda80z        ingress             overlay             swarm
b65c952a4b2b        none                null                local
复制代码

5.On host2, 以-d和 (-it) 的方式启动一个容器 (alpine2) 并连接到test-net:

$ docker run -dit --name alpine2 --network test-net alpine
fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342
复制代码

自动DNS容器发现仅适用于唯一的容器名称。

6.在host2上, 验证 test-net网络已经创建(和host1上的test-net 有一样的NETWORK ID):

 $ docker network ls
 NETWORK ID          NAME                DRIVER              SCOPE
 ...
 uqsof8phj3ak        test-net            overlay             swarm
复制代码

7.在host1上, 在alpine1的交互终端内ping alpine2:

/ # ping -c 2 alpine2
PING alpine2 (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.600 ms
64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.555 ms

--- alpine2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.555/0.577/0.600 ms
复制代码

两个容器与连接两个主机的覆盖网络通信。如果你在host2主机上运行另一个alpine容器,而不detached.你可以从host2ping alpine1 (这里我们增加了容器自动清理的选项):

$ docker run -it --rm --name alpine3 --network test-net alpine
/ # ping -c 2 alpine1
/ # exit
复制代码

8.在host1上, 关闭alpine1的会话 (这将停止容器):

/ # exit
复制代码

9.清理你的容器和网络 您必须在每个主机上独立停止和删除容器,因为Docker守护程序是独立运行的,并且它们是独立的容器。 您只需要删除host1上的网络,因为当您停止host2上的alpine2时,test-net就会消失。

host2上,停止alpine2,检查test-net已经删除,然后删除alpine2:

$ docker container stop alpine2
$ docker network ls
$ docker container rm alpine2
复制代码

host1上, 删除 alpine1 and test-net:

$ docker container rm alpine1
$ docker network rm test-net
复制代码

在容器和集群服务之间进行通信

准备

本例需要Docker 17.06或更高版本。

演示

在此示例中,您在同一Docker主机上启动了两个不同的alpine1容器,并进行了一些测试以了解它们如何相互通信。 您需要安装并运行Docker。

1.打开一个终端窗口。 在执行其他任何操作之前,请先列出当前网络。 如果您从未在此Docker守护程序上添加网络或初始化群组,则应该看到以下内容。 您可能会看到不同的网络,但至少应该看到以下内容(网络ID会有所不同):

$ docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
17e324f45964        bridge              bridge              local
6ed54d316334        host                host                local
7092879f2cc8        none                null                local
复制代码

列出了默认的桥接网络,以及hostnone。 后两个不是完全成熟的网络,但用于启动直接连接到Docker守护程序主机的网络堆栈的容器,或用于启动不包含网络设备的容器。 本教程将把两个容器连接到桥接网络。

2.启动两个运行ash的Alpine容器,这是Alpine的默认shell,而不是bash-dit标志的意思是启动分离的容器(在后台),交互的(具有键入的能力)和TTY(以便您可以看到输入和输出)。 由于您是分开启动的,因此您不会立即连接到该容器。 而是将打印容器的ID。 因为您未指定任何--network标志,所以容器将连接到默认的网桥网络。

$ docker run -dit --name alpine1 alpine ash

$ docker run -dit --name alpine2 alpine ash
复制代码

检查两个容器是否确实已启动:

$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
602dbf1edc81        alpine              "ash"               4 seconds ago       Up 3 seconds                            alpine2
da33b7aa74b0        alpine              "ash"               17 seconds ago      Up 16 seconds                           alpine1
复制代码

3.检查网桥网络以查看连接了哪些容器。

$ docker network inspect bridge

[
    {
        "Name": "bridge",
        "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
        "Created": "2017-06-22T20:27:43.826654485Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
            "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": {
                "Name": "alpine2",
                "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": {
                "Name": "alpine1",
                "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5",
                "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": {}
    }
]
复制代码

在顶部附近,列出了有关桥接网络的信息,包括Docker主机和桥接网络之间的网关的IP地址(172.17.0.1)。 在Containers项下,列出了每个连接的容器及其IP地址信息(alpine1172.17.0.2alpine2172.17.0.3)。

4.容器在后台运行。 使用docker attach命令连接到alpine1

$ docker attach alpine1

/ #
复制代码

提示符将更改为以指示您是容器中的root用户。 使用ip addr show命令从容器中查看alpine1的网络接口:

# ip addr show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link
       valid_lft forever preferred_lft forever
复制代码

第一个接口是环回设备。现在先忽略它。注意,第二个接口的IP地址是172.17.0.2,这与前面步骤中显示的alpine1的地址相同。

5.在alpine1内部,通过ping baidu.com确保您可以连接到互联网。-c 2标志将命令限制两次两次ping操作。

# ping -c 2 baidu.com

PING baidu.com (172.217.3.174): 56 data bytes
64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.841 ms
64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.897 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 9.841/9.869/9.897 ms
复制代码

7.现在尝试ping第二个容器。 首先,通过其IP地址172.17.0.3对其执行ping操作:

# ping -c 2 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.086 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms

--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.086/0.090/0.094 ms
复制代码

这样成功了。 接下来,尝试按容器名称ping alpine2容器。 这将失败。

# ping -c 2 alpine2

ping: bad address 'alpine2'
复制代码

7.通过使用分离序列CTRL + p CTRL + q(按住CTRL并键入p后跟q)从alpine1分离而不停止它。 如果愿意,请附加到alpine2并在那里重复步骤4、5和6,将alpine1代替为alpine2

8.停止并删除容器;

$ docker container stop alpine1 alpine2
$ docker container rm alpine1 alpine2
复制代码

请记住,不建议将默认网桥网络用于生产。