8.6 Swarm 高可用与负载均衡

实验介绍

在 Docker Swarm 集群中,高可用性(High Availability)负载均衡(Load Balancing) 是两个核心特性。

  • 高可用:当集群中的某个节点发生故障时,Swarm 会自动检测并将其上运行的任务调度到其他健康节点,确保服务不中断。
  • 负载均衡:Swarm 将访问流量自动分发到后端的各个容器副本。

首先,创建一个拥有 2 个副本(Replicas)的服务,模拟一个需要高可靠运行的应用。

执行以下命令:

bash

docker service create \
  --replicas 2 \
  --name s[学号后两位] \
  -p [8000+学号]:80 \
  -e STUDENT_ID=[学号] \
  harbor.seahi.me/stu/whoami-for-swarm
参数说明
  • --replicas 2:告诉 Swarm 我们希望始终有 2 个容器实例在运行。

查看服务中的任务目前运行在哪些节点上:

bash

docker service ps s[学号后两位]

你会看到两个任务分别运行在不同的节点上(例如 manager-1manager-2)。Swarm 默认会尽量将任务分散到不同节点。

为了验证高可用,我们模拟其中一个节点发生故障。假设其中一个任务运行在 manager-2 上,我们登录到该节点并停止 Docker 服务:

bash

# [在 manager-2 节点上执行]
systemctl stop docker

回到主节点(Manager Leader),再次查看任务状态:

bash

docker service ps s[学号后两位]

实验现象

你会发现:

  1. 原来运行在 manager-2 上的期望状态变成了 Shutdown,当前状态为 Running 但节点已失联)。
  2. Swarm 检测到了节点丢失,立即在另一个健康节点(如 manager-3)上启动了一个 新任务(拥有新的 ID)。
  3. 最终结果:服务的可用副本数恢复到了 2 个,业务未受持续影响。
注意
如果 s00.1 的状态依然显示为 Running,这是因为 Swarm 的节点心跳检测有一定的超时时间,此时管理节点还没判定它完全“死亡”,但为了保险起见,它已经启动了新任务。

实验结束后,请务必恢复 manager-2 的 Docker 服务:

bash

# [在 manager-2 节点上执行]
systemctl start docker

虽然 Swarm 自带了 Routing Mesh(路由网格),但我们也可以在集群外部再部署一个负载均衡器(如 HAProxy 或 Nginx),作为流量的统一入口。

将刚才的服务扩容为 3 个副本,尽量保证每个节点都有任务:

bash

docker service scale s[学号后两位]=3

查看任务分布,确认每个节点都在运行。

我们将使用 HAProxy 作为外部负载均衡器。

创建配置目录:

bash

mkdir /srv/haproxy
cd /srv/haproxy

在该目录下创建名为 haproxy.cfg 的配置文件:

内容如下:

haproxy

defaults
    mode http

# 前端设置
frontend http-in
    # HAProxy 监听的端口
    bind *:[9000+学号后两位]
    default_backend servers

# 后端设置
backend servers
    balance roundrobin
    # 这里填写 Swarm 集群节点的 IP 和服务暴露的端口
    # 即使某个节点上没有运行该容器,Swarm 的路由网络也会自动转发流量
    server node1 192.168.192.199:[8000+学号后两位] check
    server node2 192.168.192.202:[8000+学号后两位] check
    server node3 192.168.192.203:[8000+学号后两位] check
原理解析
双层负载均衡:外部负载均衡器(HAProxy)将流量轮询分发给 Swarm 的 3 个节点。Swarm 节点接收到请求后,通过内部的 VIP 将请求转发给具体的容器。

在主节点上启动 HAProxy 容器(一般在独立于集群的外部设备上部署 Haproxy):

bash

docker run -d \
-v /srv/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg \
-p [9000+学号后两位]:[9000+学号后两位] \
--name haproxy-[学号后两位] \
haproxy

在浏览器中访问 http://<主节点IP>:[9000+学号后两位]

多次刷新页面,观察结果:

你会发现虽然访问入口不变,但提供服务的容器(Hostname 和 IP)在不断变化,这证明负载均衡正在工作。

删除集群中的服务与 HAProxy 容器


在前面的任务中,Swarm 使用 VIP 模式,所有容器共用一个虚拟 IP。

但在某些场景下,我们需要直接获取每个容器的真实 IP 地址,比如:

  • 使用外部负载均衡器时,需要知道每个容器的具体位置
  • 某些应用需要直连到特定的容器实例

这时就可以使用 DNSRR(DNS 轮询) 模式。

注意
DNSRR 模式的服务 不支持 使用 -p 端口映射(Routing Mesh 失效)。它主要用于集群内部通信。

在本任务中,我们将创建 DNSRR 服务,并使用 HAProxy 直接连接到每个容器的真实 IP 进行负载均衡。

创建一个指定 endpoint-modednsrr 的新服务。我们需要先创建一个 overlay 网络,以便容器间通信:

bash

# 创建 overlay 网络(添加 --attachable 参数允许单机容器连接)
docker network create -d overlay --attachable dnsrr-[学号后两位]

# 创建 DNSRR 服务(3 个副本)
docker service create \
  --name s[学号后两位] \
  --replicas 3 \
  --network dnsrr-[学号后两位] \
  --endpoint-mode dnsrr \
  harbor.seahi.me/stu/whoami-for-swarm

查看服务状态:

bash

docker service ps s[学号后两位]

在 Swarm 的 dnsrr 模式下,Docker 专门预留了一个 DNS 记录前缀叫 tasks.。使用 nslookup 命令获取所有容器的真实 IP 地址:

bash

# 启动一个 busybox 容器并执行 nslookup
docker run -it --rm --network dnsrr-[学号后两位] busybox nslookup tasks.s[学号后两位]

你将看到 DNS 解析返回了 3 个不同的 IP 地址,而不是原来的 1 个 VIP:

记录 IP 地址
请记录下这 3 个 IP 地址,我们将在下一步的 HAProxy 配置中使用。

修改 haproxy.cfg 的配置文件,内容如下:

haproxy

defaults
    mode http

# 前端设置
frontend http-in
    # HAProxy 监听的端口
    bind *:[9000+学号后两位]
    default_backend dns-servers

# 后端设置
backend dns-servers
    balance roundrobin
    # 这里填写从 nslookup 获取的容器真实 IP
    # 注意:容器默认监听 80 端口
    server container1 10.0.4.4:80 check
    server container2 10.0.4.2:80 check
    server container3 10.0.4.6:80 check

启动 HAProxy 容器,并将其加入到与服务相同的 overlay 网络:

bash

docker run -d \
  -v /srv/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg \
  -p [9000+学号后两位]:[9000+学号后两位] \
  --network dnsrr-[学号后两位] \
  --name haproxy-[学号后两位] \
  haproxy
网络配置要点
与任务二不同,这里 HAProxy 容器必须加入 dnsrr-[学号后两位] 网络,才能访问到容器的真实 IP。

在浏览器中访问 http://<主节点IP>:[10000+学号后两位]

多次刷新页面,验证负载均衡是否正常工作。

原理对比
  • 任务二:HAProxy → Swarm 节点(VIP) → 容器
  • 任务三:HAProxy → 容器真实 IP(直连)

在 DNSRR 模式下,HAProxy 可以直接连接到每个容器的真实 IP,不再依赖 Swarm 的 VIP 机制。

相关内容