使用 Swarm 模式路由网格
Docker Engine 的 Swarm 模式使得为服务发布端口变得轻而易举,从而使集群外部的资源能够访问这些服务。所有节点都参与入口路由网(ingress routing mesh)。该路由网使集群中的每个节点都能在任何正在运行的服务的已发布端口上接受连接,即使该节点上当前没有运行相关任务也是如此。路由网会将所有发往已发布端口的传入请求,智能地引导至拥有活动容器的可用节点上。
要在 Swarm 中使用 Ingress 网络,您需要在启用 Swarm 模式之前确保 Swarm 节点之间开放以下端口:
- 端口
7946TCP/UDP 用于容器网络发现。 - Port
4789UDP (configurable) for the container ingress network.
在设置 Swarm 中的网络时,应格外小心。请查阅 教程 以获取概述。
您还必须在 Swarm 节点和任何需要访问该端口的外部资源(例如外部负载均衡器)之间打开已发布的端口。
您也可以 绕过路由网格 针对特定 服务。
为服务发布端口
创建服务时,使用 --publish 标志来发布端口。target
用于指定容器内部的端口,published 用于
指定路由网格上要绑定的端口。如果省略 published
端口,则每个服务任务会绑定一个随机的高位端口。您
需要检查该任务以确定具体端口。
$ docker service create \
--name <SERVICE-NAME> \
--publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<IMAGE>
注意
这种语法的旧形式是一个冒号分隔的字符串,其中 已发布的端口在前,目标端口在后,例如
-p 8080:80。新语法更受青睐,因为它更易读且 提供了更大的灵活性。
<PUBLISHED-PORT> 是 Swarm 使服务可用的端口。
如果省略它,则会绑定一个随机的高编号端口。
<CONTAINER-PORT> 是容器监听的端口。此参数
是必需的。
例如,以下命令将 nginx 容器中的 80 端口发布到集群中任何节点的 8080 端口:
$ docker service create \
--name my-web \
--publish published=8080,target=80 \
--replicas 2 \
nginx
当您在任何节点上访问 8080 端口时,Docker 会将您的请求路由到一个活动的容器。在群集节点本身,8080 端口可能实际上并未绑定,但路由网格知道如何转发流量,并防止任何端口冲突的发生。
路由网格监听分配给节点的任意 IP 地址的已发布端口。对于外部可路由的 IP 地址,该端口可从主机外部访问。对于所有其他 IP 地址,访问仅限于主机内部。

您可以使用以下命令为现有服务发布一个端口:
$ docker service update \
--publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<SERVICE>
您可以使用 docker service inspect 来查看服务已发布的端口。例如:
$ docker service inspect --format="{{json .Endpoint.Spec.Ports}}" my-web
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080}]
输出显示了容器的 <CONTAINER-PORT>(标记为 TargetPort)以及节点监听服务请求的 <PUBLISHED-PORT>(标记为 PublishedPort)。
仅发布 TCP 端口或仅发布 UDP 端口
默认情况下,当您发布一个端口时,它是一个 TCP 端口。您可以选择专门发布一个 UDP 端口,或者在保留 TCP 端口的同时额外发布一个 UDP 端口。当同时发布 TCP 和 UDP 端口时,如果省略了协议说明符,该端口将作为 TCP 端口发布。如果您使用较长的语法(推荐),请将 protocol 键设置为 tcp 或 udp。
仅 TCP
长语法:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
dns-cache
简短语法:
$ docker service create --name dns-cache \
-p 53:53 \
dns-cache
TCP 和 UDP
长语法:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
--publish published=53,target=53,protocol=udp \
dns-cache
简短语法:
$ docker service create --name dns-cache \
-p 53:53 \
-p 53:53/udp \
dns-cache
仅支持 UDP
长语法:
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp \
dns-cache
简短语法:
$ docker service create --name dns-cache \
-p 53:53/udp \
dns-cache
绕过路由网格
默认情况下,发布端口的 Swarm 服务使用路由网格(routing mesh)。 当您连接到任何 Swarm 节点上已发布的端口时(无论该节点是否正在运行特定服务),您都会被透明地重定向到运行该服务的 worker 节点。实质上,Docker 充当了您的 Swarm 服务的负载均衡器。
您可以绕过路由网格,这样当您在给定节点上访问绑定端口时,始终访问的是在该节点上运行的服务实例。这被称为 host 模式。有几点需要注意。
如果您访问的节点未运行服务任务,则服务不会在该端口上监听。可能没有任何内容在监听,或者有完全不同的应用程序在监听。
如果您期望在每个节点上运行多个服务任务(例如,当您有 5 个节点但运行 10 个副本时),则无法指定静态目标端口。您可以选择允许 Docker 分配一个随机的高编号端口(通过省略
published),或者通过使用全局服务而不是复制服务,或使用放置约束,来确保在给定节点上仅运行该服务的单个实例。
要绕过路由网格,您必须使用长 --publish 服务并将 mode 设置为 host。如果您省略 mode 键或将其设置为 ingress,则会使用路由网格。以下命令使用 host 模式创建全局服务并绕过路由网格。
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp,mode=host \
--mode global \
dns-cache
配置外部负载均衡器
您可以为 Swarm 服务配置外部负载均衡器,既可以与路由网格结合使用,也可以完全不使用路由网格。
使用路由网格
您可以配置外部负载均衡器,将请求路由到 Swarm 服务。例如,您可以配置 HAProxy 以 将请求均衡地分发给发布到端口 8080 的 Nginx 服务。

在这种情况下,负载均衡器与集群节点之间的 8080 端口必须处于开放状态。集群节点可以位于一个私有网络上,该网络可供代理服务器访问,但不对公网开放。
您可以配置负载均衡器,以便在 Swarm 中的每个节点之间均衡请求,即使该节点上没有调度任务。例如,您可以在 /etc/haproxy/haproxy.cfg 中拥有以下 HAProxy 配置:
global
log /dev/log local0
log /dev/log local1 notice
...snip...
# Configure HAProxy to listen on port 80
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
# Configure HAProxy to route requests to swarm nodes on port 8080
backend http_back
balance roundrobin
server node1 192.168.99.100:8080 check
server node2 192.168.99.101:8080 check
server node3 192.168.99.102:8080 check当您通过 80 端口访问 HAProxy 负载均衡器时,它会将请求转发到 Swarm 中的节点。Swarm 路由网格会将请求路由到一个活跃的任务。 如果由于任何原因,Swarm 调度器将任务分发到不同的节点,您无需重新配置负载均衡器。
您可以配置任何类型的负载均衡器,将请求路由到 Swarm 节点。 有关 HAProxy 的更多信息,请参阅 HAProxy 文档。
没有路由网格
要使用外部负载均衡器而不使用路由网格,请将 --endpoint-mode 设置为 dnsrr,而不是默认值 vip。在这种情况下,没有单一的虚拟 IP。相反,Docker 会为服务设置 DNS 条目,使得对该服务名称的 DNS 查询返回一个 IP 地址列表,然后客户端直接连接到其中某一个地址。
您不能将 --endpoint-mode dnsrr 与 --publish mode=ingress 一起使用。
您必须在服务前面运行自己的负载均衡器。在 Docker 主机上对服务名称进行 DNS 查询将返回运行该服务的节点的 IP 地址列表。配置您的负载均衡器以消耗此列表并在节点之间平衡流量。
参见
配置服务发现。