简介
Docker Swarm 模式能够更加容易地开放服务端口,在之前一旦创建了容器,那么容器的端口是不允许修改的,也就使得服务开放端口受到限制。在 Swarm 中,所有的节点形成了一个 ingress routing mesh (路由组网),各个节点之间的服务都可以通过开放的端口连接通信。通过路由组网,可以把入站到节点开放端口的请求路由至某一可用的服务容器。
开放服务访问端口
在创建服务时使用--publish
来开放服务端口。--pulish published=8080,target=80
可以粗略地认为是-p 8080:80
;published
,用于指定绑定到路由组网的端口号,如不指定则绑定随机端口号;target
,用于指定映射到容器内的端口号。
docker service create \
--name nginx_m \
--publish published=8090,target=80 \
nginx:1.21.5-alpine
更新服务访问端口
当需要为服务增加端口或更改服务端口时,可以使用--publish-add
。
docker service update \
--publish-add published=8090,target=80 \
nginx_m
查看服务开放端口
执行docker service inspect
命令可以查看服务开放的端口。
docker service inspect --format="{{json .Endpoint.Spec.Ports}}" nginx_m
输出示例:
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8090,"PublishMode":"ingress"}]
访问服务
浏览器访问:http://192.168.199.103:8090/
Routing mesh示意图
开启 Swarm 集群后,Docker 会创建两个网络。
其中的ingress
网络就是应用在 ingress routing mesh 中。
### docker network list
NETWORK ID NAME DRIVER SCOPE
7cdb1917b668 docker_gwbridge bridge local
p7lhor2iyy2j ingress overlay swarm
当访问任意节点的服务(如访问 node1 的 8080 端口)时,Docker 会将请求路由到可用的容器。
在主机节点上,8080端口并不会被绑定到容器内,Swarm routing mesh 会自动路由,并且避免端口冲突。
绕过routing mesh
绕过 routing mesh,这样就可以在访问某个节点端口时直接访问此节点上的服务实例。
绕过 ingress 路由组网需要用到host
模式,但有几点需要注意:
- 如果访问的节点没有运行的服务,那么此时是没有服务监听所访问的端口的,又或者完全是另外的应用在监听此端口。
- 如果期望在每个节点运行多个服务(比如有5个节点但需运行10个实例),那么就无法指定静态的目标服务端口。要么让 Docker 分配随机端口(不指定
published
),要么确保每个节点只有一个服务实例运行(通过指定为global
式服务,而非replicated
式服务)。
以下命令用于创建global
式服务并应用host
模式绕过 routing mesh;--publish
中的mode
必须设置为host
,如果忽略mode
或者设置为了ingress
(默认值) 那么还是会应用 routing mesh。
docker service create \
--name nginx_m \
--publish published=8090,target=80,protocol=udp,mode=host \
--mode global \
nginx:1.21.5-alpine
配置外部负载均衡
在 Swarm service 中也可以配置外部负载均衡,并可选择性地应用 routing mesh 或者绕过它。
外部负载均衡应用 routing mesh
在这个例子中(如下图),各个节点都需要开放 8080 端口,因为这是 Swarm service 访问入口;
对于 Swarm 中的节点,可以配置在内网中并只对代理服务器可访问,而不对外开放;
反向代理服务器则开放 80 端口,这是对外的服务访问入口。
外部负载均衡绕过 routing mesh
指定--endpoint-mode
为dnsrr
,而不是默认的vip
。如此,就没有任何虚拟 IP 了;相反地,Docker 会配置 DNS,在查询服务时能够返回服务的 IP 地址列表,而客户端访问时就能直接连接。不过对于负载均衡器则需要提供服务的 IP 地址列表,这里可以参考服务发现。
配置服务发现
服务发现可以把外部客户端的请求路由到某一个独立节点,而客户端则不需要知道有多少个节点以及它们的 IP 地址端口。通常只需开放必要服务的访问端口,而处在同一内部网络服务的通信则不需要开放。比如说 WordPress 服务可以对外开放,而它存储的数据库服务 MySQL 则没有必要对外开放端口。
服务发现有两种实现方式:基于内部连接的负载均衡(第3层网络协议层的内置DNS,以及第4层传输协议层的虚拟 IP);外部自定义的基于请求的负载均衡(第7层应用层的 DNS round-robin,DNSRR,DNS 轮询负载均衡)。
默认地 (没有任何外部服务发现配置时),当把服务加入网络并开放端口时,Docker 会给服务分配一个虚拟 IP,同时维护节点列表,把客户端请求路由到节点上,每次请求都有可能路由到不同节点。
如果配置了服务使用 DNSRR 服务发现,那么就不会有虚拟网了。Docker 会配置 DNS 记录,在查询服务时能够返回服务的 IP 地址列表,而客户端访问时就能直接连接。
DNSRR 在只使用自己的负载均衡器时 (也就是绕过 routing mesh) 是很有必要的,例如使用 Nginx、HAProxy。要配置服务应用 DNSRR,需要在创建或更新服务时使用--endpoint-mode dnsrr
。
参考
[1] Use swarm mode routing mesh
[2] Configure service discovery