与覆盖网络联网
预计阅读时间:20分钟
本系列教程介绍了群集服务的网络。有关使用独立容器进行联网的信息,请参见 使用独立容器进行联网。如果您需要总体上了解有关Docker网络的更多信息,请参阅概述。
本主题包括四个不同的教程。您可以在Linux,Windows或Mac上运行它们中的每一个,但对于最后两个,则需要在其他位置运行第二个Docker主机。
-
使用默认覆盖网络演示了如何在初始化或加入群集时使用Docker自动为您设置的默认覆盖网络。该网络不是生产系统的最佳选择。
-
使用用户定义的覆盖网络显示了如何创建和使用自己的自定义覆盖网络来连接服务。建议将其用于生产中运行的服务。
-
将覆盖网络用于独立容器 显示了如何使用覆盖网络在不同Docker守护程序上的独立容器之间进行通信。
-
容器与群集服务 之间的通信使用可连接的覆盖网络在独立容器与群集服务之间建立通信。
先决条件
这些要求您至少有一个单节点集群,这意味着您已经启动了Docker并docker swarm init
在主机上运行。您也可以在多节点集群上运行示例。
使用默认的覆盖网络
在此示例中,您将启动alpine
服务并从各个服务容器的角度检查网络的特征。
本教程不涉及有关如何实现重叠网络的特定于操作系统的详细信息,而是从服务的角度着重介绍重叠的功能。
先决条件
本教程需要三台可以相互通信的物理或虚拟Docker主机。本教程假定这三台主机在同一网络上运行,并且不涉及防火墙。
这些主机将被称为manager
,worker-1
和worker-2
。该
manager
主机将作为既是经理和工人,这意味着它可以运行服务任务和管理群。worker-1
并且worker-2
将作为唯一的工人,
如果您没有三台主机,一个简单的解决方案是在云提供商(例如Amazon EC2)上设置三台Ubuntu主机,它们全部位于同一网络上,并允许与该网络上的所有主机进行所有通信(使用诸如EC2安全组),然后按照Ubuntu上Docker Engine-Community的 安装说明进行操作。
演练
创建群
在此过程结束时,所有三个Docker主机都将加入集群,并将使用一个名为的覆盖网络将其连接在一起ingress
。
-
上
manager
。初始化群。如果主机只有一个网络接口,则该--advertise-addr
标志是可选的。$ docker swarm init --advertise-addr=<IP-ADDRESS-OF-MANAGER>
记下所打印的文本,因为其中包含将用于加入
worker-1
和加入worker-2
群体的令牌。将令牌存储在密码管理器中是个好主意。 -
在上
worker-1
,加入群体。如果主机只有一个网络接口,则该--advertise-addr
标志是可选的。$ docker swarm join --token <TOKEN> \ --advertise-addr <IP-ADDRESS-OF-WORKER-1> \ <IP-ADDRESS-OF-MANAGER>:2377
-
在上
worker-2
,加入群体。如果主机只有一个网络接口,则该--advertise-addr
标志是可选的。$ docker swarm join --token <TOKEN> \ --advertise-addr <IP-ADDRESS-OF-WORKER-2> \ <IP-ADDRESS-OF-MANAGER>:2377
-
在上
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
-
名单上的多克尔网络
manager
,worker-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_gwbridge
该连接ingress
网络到主机多克尔的网络接口,以便通信可以流入和流出群经理和工人。如果创建群集服务但未指定网络,则它们将连接到ingress
网络。建议对将一起使用的每个应用程序或一组应用程序使用单独的覆盖网络。在下一个过程中,您将创建两个覆盖网络并将服务连接到每个覆盖网络。
创建服务
-
在上
manager
,创建一个新的覆盖网络,称为nginx-net
:$ docker network create -d overlay nginx-net
您无需在其他节点上创建覆盖网络,因为当那些节点之一开始运行需要该服务的服务任务时,该覆盖网络将自动创建。
-
在上
manager
,创建一个连接到的5副本Nginx服务nginx-net
。该服务将向外部世界发布80端口。所有服务任务容器都可以彼此通信,而无需打开任何端口。注意:服务只能在管理器上创建。
$ docker service create \ --name my-nginx \ --publish target=80,published=80 \ --replicas=5 \ --network nginx-net \ nginx
默认发布的模式
ingress
,这是用来当你没有指定mode
的--publish
标志,这意味着,如果你浏览到端口80上manager
,worker-1
或者worker-2
,你会被连接到端口80上的5项服务任务之一,即使没有任务当前正在您浏览到的节点上运行。如果要使用host
模式发布端口 ,则可以将其添加mode=host
到--publish
输出中。但是,在这种情况下,也应使用--mode global
代替--replicas=5
,因为只有一个服务任务可以绑定给定节点上的给定端口。 -
运行
docker service ls
以监视服务启动的进度,这可能需要几秒钟。 -
检查
nginx-net
网络manager
,worker-1
和worker-2
。请记住,您不需要手动创建它worker-1
,worker-2
因为Docker是为您创建的。输出将很长,但请注意Containers
和Peers
部分。Containers
列出了从该主机连接到覆盖网络的所有服务任务(或独立容器)。 -
从
manager
,使用检查服务,docker service inspect my-nginx
并注意有关服务使用的端口和端点的信息。 -
创建一个新网络
nginx-net-2
,然后更新服务以使用该网络,而不是nginx-net
:$ docker network create -d overlay nginx-net-2
$ docker service update \ --network-add nginx-net-2 \ --network-rm nginx-net \ my-nginx
-
运行
docker service ls
以验证服务已更新,并且所有任务都已重新部署。运行docker network inspect nginx-net
以验证没有任何容器连接到它。运行相同的命令nginx-net-2
,请注意所有服务任务容器都已连接到该命令。注意:即使根据需要在群集工作程序节点上自动创建了覆盖网络,也不会自动将其删除。
-
清理服务和网络。从
manager
,运行以下命令。管理者将指示工作人员自动删除网络。$ docker service rm my-nginx $ docker network rm nginx-net nginx-net-2
使用用户定义的覆盖网络
先决条件
本教程假定已经建立了集群,并且您正在管理中。
演练
-
创建用户定义的覆盖网络。
$ docker network create -d overlay my-overlay
-
使用覆盖网络启动服务,并将端口80发布到Docker主机上的端口8080。
$ docker service create \ --name my-nginx \ --network my-overlay \ --replicas 1 \ --publish published=8080,target=80 \ nginx:latest
-
通过查看部分,运行
docker network inspect my-overlay
并验证my-nginx
服务任务已连接到该任务Containers
。 -
删除服务和网络。
$ docker service rm my-nginx $ docker network rm my-overlay
对独立容器使用覆盖网络
此示例演示了DNS容器发现-具体来说,如何使用覆盖网络在不同Docker守护程序上的独立容器之间进行通信。步骤如下:
- 在上
host1
,将节点初始化为群集(管理器)。 - 在上
host2
,将节点加入群集(工作人员)。 - 在上
host1
,创建一个可附加的覆盖网络(test-net
)。 - 在上
host1
运行一个交互式高山容器(alpine1
)test-net
。 - 在上
host2
运行,并在上运行一个独立的交互式高山容器(alpine2
)test-net
。 - 在ping
host1
会话中进行。alpine1
alpine2
先决条件
对于此测试,您需要两个可以相互通信的不同Docker主机。每个主机必须在两个Docker主机之间打开以下端口:
- TCP端口2377
- TCP和UDP端口7946
- UDP端口4789
一种简单的设置方法是拥有两个VM(本地或在AWS等云提供商上),每个VM均已安装并正在运行Docker。如果您使用的是AWS或类似的云计算平台,最简单的配置是使用一个安全组,该安全组打开两个主机之间的所有传入端口以及来自客户端IP地址的SSH端口。
本示例将群中的两个节点称为host1
和host2
。此示例还使用Linux主机,但是在Windows上也可以使用相同的命令。
演练
-
设置群。
一种。在上
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.
b。在上
host2
,按照上述说明加入群组:$ docker swarm join --token <your_token> <your_ip_address>:2377 This node joined a swarm as a worker.
如果节点无法加入群集,则该
docker swarm join
命令超时。要解决此问题,请在docker swarm leave --force
上运行host2
,验证您的网络和防火墙设置,然后重试。 -
在上
host1
,创建一个名为的可连接覆盖网络test-net
:$ docker network create --driver=overlay --attachable test-net uqsof8phj3ak0rq9k86zta6ht
请注意返回的NETWORK ID-当您从连接到它时,您将再次看到它
host2
。 -
在上
host1
,启动一个与(-it
)alpine1
连接的交互式()容器()test-net
:$ docker run -it --name alpine1 --network test-net alpine / #
-
在上
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
-
在上
host2
,启动一个独立的(-d
)和交互式(-it
)容器(alpine2
),该容器连接到test-net
:$ docker run -dit --name alpine2 --network test-net alpine fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342
自动DNS容器发现仅适用于唯一的容器名称。
-
上
host2
,验证test-net
被创建(和具有相同的网络ID为test-net
上host1
):$ docker network ls NETWORK ID NAME DRIVER SCOPE ... uqsof8phj3ak test-net overlay swarm
-
在
host1
,alpine2
的以下交互式终端中pingalpine1
:/ # 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
,则可以从ping (在这里,我们添加了 remove选项以自动清除容器):alpine1
host2
$ docker run -it --rm --name alpine3 --network test-net alpine / # ping -c 2 alpine1 / # exit
-
在上
host1
,关闭alpine1
会话(这也会停止容器):/ # exit
-
清理您的容器和网络:
您必须在每个主机上分别停止和删除容器,因为Docker守护程序是独立运行的,并且它们是独立的容器。您只需要删除网络就可以了,
host1
因为当您停止alpine2
时host2
,会test-net
消失。一种。在上
host2
,停止alpine2
,检查是否test-net
已删除,然后删除alpine2
:$ docker container stop alpine2 $ docker network ls $ docker container rm alpine2
一种。在上
host1
,删除alpine1
并test-net
:$ docker container rm alpine1 $ docker network rm test-net
在容器和集群服务之间进行通信
在此示例中,您alpine
将在同一Docker主机上启动两个不同的容器,并进行一些测试以了解它们如何相互通信。您需要安装并运行Docker。
-
打开一个终端窗口。在执行其他任何操作之前,请先列出当前网络。如果您从未在此Docker守护程序上添加网络或初始化过集群,则应该看到以下内容。您可能会看到不同的网络,但至少应该看到以下内容(网络ID会有所不同):
$ docker network ls NETWORK ID NAME DRIVER SCOPE 17e324f45964 bridge bridge local 6ed54d316334 host host local 7092879f2cc8 none null local
bridge
列出了默认网络,以及host
和none
。后两个不是完全成熟的网络,但是用于启动直接连接到Docker守护程序主机的网络堆栈的容器,或者用于启动不具有网络设备的容器。本教程将把两个容器连接到bridge
网络。 -
启动两个
alpine
运行的容器ash
,这是Alpine的默认外壳程序,而不是bash
。这些-dit
标志意味着以分离的方式(在后台),交互的(具有输入能力)和TTY(以便您可以看到输入和输出)启动容器。由于您是分开启动的,因此您不会立即连接到该容器。而是将打印容器的ID。由于未指定任何--network
标志,因此容器将连接到默认bridge
网络。$ 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
-
检查
bridge
网络以查看连接了哪些容器。$ 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": {} } ]
在顶部附近,
bridge
列出了有关网络的信息,包括Docker主机和bridge
网络之间的网关的IP地址(172.17.0.1
)。在该Containers
键下,列出了每个已连接的容器以及有关其IP地址的信息(172.17.0.2
foralpine1
和172.17.0.3
foralpine2
)。 -
容器在后台运行。使用
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
与上一步中显示的地址相同。 -
从内部
alpine1
,确保可以通过ping连接到Internetgoogle.com
。该-c 2
标志将命令限制两次两次ping
。# ping -c 2 google.com PING google.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 --- google.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 9.841/9.869/9.897 ms
-
现在尝试对第二个容器执行ping操作。首先,通过其IP地址对其进行ping操作
172.17.0.3
:# 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
这样成功了。接下来,尝试
alpine2
按容器名称ping容器。这将失败。# ping -c 2 alpine2 ping: bad address 'alpine2'
-
分离从
alpine1
没有通过使用分离的序列,停止它CTRL
+p
CTRL
+q
(按住CTRL
和类型p
,随后q
)。如果你愿意,重视alpine2
并重复步骤4,5和6出现,取代alpine1
了alpine2
。 -
停止并取出两个容器。
$ docker container stop alpine1 alpine2 $ docker container rm alpine1 alpine2
请记住,bridge
不建议将默认网络用于生产。要了解用户定义的桥接网络,请继续
阅读下一个教程。
其他网络教程
既然您已经完成了覆盖网络的网络教程,则可能需要遍历以下其他网络教程:
网络,桥,路由,端口,群,覆盖