使用覆盖网络进行网络连接
这一系列教程涉及 swarm 服务的网络。对于独立容器的网络,请参阅 独立容器网络。如果您需要了解更多关于 Docker 网络的一般信息,请参阅概述。
本页包含以下教程。您可以在 Linux、Windows 或 Mac 上运行它们中的每一个,但对于最后一个,您需要另一个在别处运行的 Docker 主机。
使用默认覆盖网络 演示了如何使用 Docker 在您初始化或加入 swarm 时自动为您设置的默认覆盖网络。该网络不是生产系统的最佳选择。
使用用户定义的覆盖网络 展示了如何创建和使用您自己的自定义覆盖网络来连接服务。建议在生产环境中运行的服务使用此方法。
为独立容器使用覆盖网络 展示了如何使用覆盖网络在不同 Docker 守护进程上的独立容器之间进行通信。
先决条件
这些要求您至少拥有一个单节点 swarm,这意味着您已经启动了 Docker 并在主机上运行了 `docker swarm init`。您也可以在多节点 swarm 上运行这些示例。
使用默认覆盖网络
在此示例中,您将启动一个 `alpine` 服务,并从单个服务容器的角度检查网络的特性。
本教程不涉及覆盖网络如何实现的操作系统特定细节,而是侧重于从服务的角度看覆盖网络如何运作。
先决条件
本教程需要三台可以相互通信的物理或虚拟 Docker 主机。本教程假设这三台主机在同一网络上运行,且没有防火墙干预。
这些主机将被称为 `manager`、`worker-1` 和 `worker-2`。`manager` 主机将同时作为管理器和工作节点,这意味着它既可以运行服务任务,也可以管理 swarm。`worker-1` 和 `worker-2` 将仅作为工作节点。
如果您手头没有三台主机,一个简单的解决方案是在云提供商(如 Amazon EC2)上设置三台 Ubuntu 主机,所有主机都在同一个网络上,并且允许该网络上所有主机之间的所有通信(使用 EC2 安全组等机制),然后按照在 Ubuntu 上安装 Docker Engine - Community 的说明进行操作。
演练
创建 swarm
在此过程结束时,所有三个 Docker 主机都将加入 swarm,并使用名为 `ingress` 的覆盖网络连接在一起。
在 `manager` 上初始化 swarm。如果主机只有一个网络接口,`--advertise-addr` 标志是可选的。
$ docker swarm init --advertise-addr=<IP-ADDRESS-OF-MANAGER>记下打印出的文本,因为它包含了您将用于将 `worker-1` 和 `worker-2` 加入 swarm 的令牌。最好将令牌存储在密码管理器中。
在 `worker-1` 上,加入 swarm。如果主机只有一个网络接口,`--advertise-addr` 标志是可选的。
$ docker swarm join --token <TOKEN> \ --advertise-addr <IP-ADDRESS-OF-WORKER-1> \ <IP-ADDRESS-OF-MANAGER>:2377在 `worker-2` 上,加入 swarm。如果主机只有一个网络接口,`--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` 上列出 Docker 网络,并注意它们现在都有一个名为 `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` 网络连接到 Docker 主机的网络接口,以便流量可以进出 swarm 管理器和工作节点。如果您创建 swarm 服务并且不指定网络,它们将连接到 `ingress` 网络。建议您为每个应用程序或将协同工作的应用程序组使用单独的覆盖网络。在下一个过程中,您将创建两个覆盖网络并将一个服务连接到每个网络。
创建服务
在 `manager` 上,创建一个名为 `nginx-net` 的新覆盖网络
$ docker network create -d overlay nginx-net您不需要在其他节点上创建覆盖网络,因为当这些节点中的一个开始运行需要它的服务任务时,它将自动创建。
在 `manager` 上,创建一个连接到 `nginx-net` 的 5 副本 Nginx 服务。该服务将向外部世界发布端口 80。所有服务任务容器都可以在不打开任何端口的情况下相互通信。
注意服务只能在管理器上创建。
$ docker service create \ --name my-nginx \ --publish target=80,published=80 \ --replicas=5 \ --network nginx-net \ nginx当您没有为 `--publish` 标志指定 `mode` 时,使用的默认发布模式是 `ingress`,这意味着如果您在 `manager`、`worker-1` 或 `worker-2` 上浏览端口 80,您将被连接到 5 个服务任务之一的端口 80,即使您浏览的节点上当前没有任务在运行。如果您想使用 `host` 模式发布端口,可以向 `--publish` 输出添加 `mode=host`。但是,在这种情况下,您还应该使用 `--mode global` 而不是 `--replicas=5`,因为在给定节点上只有一个服务任务可以绑定一个给定端口。
运行 `docker service ls` 监控服务启动进度,这可能需要几秒钟。
在 `manager`、`worker-1` 和 `worker-2` 上检查 `nginx-net` 网络。请记住,您不需要在 `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` 运行相同的命令,并注意所有服务任务容器都连接到它。
注意尽管覆盖网络会根据需要在 swarm 工作节点上自动创建,但它们不会自动删除。
清理服务和网络。从 `manager` 运行以下命令。管理器将指示工作节点自动删除网络。
$ docker service rm my-nginx $ docker network rm nginx-net nginx-net-2
使用用户定义的覆盖网络
先决条件
本教程假设 swarm 已经设置好,并且您在一个管理器上。
演练
创建用户定义的覆盖网络。
$ 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` 并通过查看 `Containers` 部分验证 `my-nginx` 服务任务已连接到该网络。
移除服务和网络。
$ docker service rm my-nginx $ docker network rm my-overlay
为独立容器使用覆盖网络
此示例演示了 DNS 容器发现——具体来说,是如何使用覆盖网络在不同 Docker 守护进程上的独立容器之间进行通信。步骤如下
- 在 `host1` 上,将节点初始化为 swarm(管理器)。
- 在 `host2` 上,将节点加入 swarm(工作节点)。
- 在 `host1` 上,创建一个可附加的覆盖网络 (`test-net`)。
- 在 `host1` 上,在 `test-net` 上运行一个交互式的 alpine 容器 (`alpine1`)。
- 在 `host2` 上,在 `test-net` 上运行一个交互式且分离的 alpine 容器 (`alpine2`)。
- 在 `host1` 上,在 `alpine1` 的会话中,ping `alpine2`。
先决条件
对于此测试,您需要两个可以相互通信的不同 Docker 主机。每个主机必须在两个 Docker 主机之间打开以下端口:
- TCP 端口 2377
- TCP 和 UDP 端口 7946
- UDP 端口 4789
一个简单的设置方法是拥有两台虚拟机(本地或在 AWS 等云提供商上),每台都安装并运行 Docker。如果您使用 AWS 或类似的云计算平台,最简单的配置是使用一个安全组,在两台主机之间打开所有传入端口,并从您客户端的 IP 地址打开 SSH 端口。
此示例将我们 swarm 中的两个节点称为 `host1` 和 `host2`。此示例还使用 Linux 主机,但相同的命令在 Windows 上也适用。
演练
设置 swarm。
a. 在 `host1` 上,初始化一个 swarm(如果提示,使用 `--advertise-addr` 指定与 swarm 中其他主机通信的接口的 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` 上,按照上述说明加入 swarm
$ docker swarm join --token <your_token> <your_ip_address>:2377 This node joined a swarm as a worker.如果节点加入 swarm 失败,`docker swarm join` 命令会超时。要解决此问题,请在 `host2` 上运行 `docker swarm leave --force`,验证您的网络和防火墙设置,然后重试。
在 `host1` 上,创建一个名为 `test-net` 的可附加覆盖网络
$ docker network create --driver=overlay --attachable test-net uqsof8phj3ak0rq9k86zta6ht注意返回的 **NETWORK ID** —— 当你从 `host2` 连接到它时,你会再次看到它。
在 `host1` 上,启动一个连接到 `test-net` 的交互式 (`-it`) 容器 (`alpine1`)
$ 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` 上,启动一个连接到 `test-net` 的分离式 (`-d`) 且交互式 (`-it`) 容器 (`alpine2`)
$ docker run -dit --name alpine2 --network test-net alpine fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342注意自动 DNS 容器发现仅适用于唯一的容器名称。
在 `host2` 上,验证 `test-net` 已被创建(并且与 `host1` 上的 `test-net` 具有相同的 NETWORK ID)
$ docker network ls NETWORK ID NAME DRIVER SCOPE ... uqsof8phj3ak test-net overlay swarm在 `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 容器,你可以从 `host2` ping `alpine1`(这里我们添加了 remove 选项 以便自动清理容器)
$ docker run -it --rm --name alpine3 --network test-net alpine / # ping -c 2 alpine1 / # exit在 `host1` 上,关闭 `alpine1` 会话(这也会停止容器)
/ # exit清理您的容器和网络
您必须在每个主机上独立停止和删除容器,因为 Docker 守护进程是独立运行的,并且这些是独立容器。您只需要在 `host1` 上删除网络,因为当您在 `host2` 上停止 `alpine2` 时,`test-net` 会消失。
a. 在 `host2` 上,停止 `alpine2`,检查 `test-net` 是否已移除,然后移除 `alpine2`
$ docker container stop alpine2 $ docker network ls $ docker container rm alpine2a. 在 `host1` 上,移除 `alpine1` 和 `test-net`
$ docker container rm alpine1 $ docker network rm test-net