跳到主要内容

Docker Swarm 集群管理

Docker Swarm 是 Docker 原生的容器编排工具,内置于 Docker Engine 中,可以将多个 Docker 主机组成一个虚拟的 Docker 主机集群。本章将详细介绍 Docker Swarm 的核心概念和使用方法。

什么是 Docker Swarm?

Docker Swarm 是 Docker 公司开发的容器编排工具,用于管理集群化的 Docker 主机。它将多个 Docker Engine 实例组合成一个虚拟的整体,让你可以像管理单个 Docker 主机一样管理整个集群。

Swarm 与其他编排工具对比

特性Docker SwarmKubernetesDocker Compose
部署复杂度简单复杂最简单
学习曲线
功能完整性中等最完整基础
适用规模中小型集群大规模集群单机
高可用性支持支持不支持
内置于 Docker否(Docker Desktop 可选)

Swarm 模式的优势

  • 集群管理集成:无需额外安装编排软件,直接使用 Docker CLI
  • 去中心化设计:Manager 和 Worker 节点使用相同的 Docker Engine
  • 声明式服务模型:定义期望状态,Docker 自动维护
  • 自动扩缩容:轻松调整服务副本数量
  • 期望状态协调:自动检测和修复异常状态
  • 多主机网络:Overlay 网络实现跨主机通信
  • 服务发现:内置 DNS 自动解析服务名称
  • 负载均衡:路由网格自动分发请求
  • 安全通信:节点间 TLS 加密通信
  • 滚动更新:零停机更新服务

核心概念

节点(Node)

节点是参与 Swarm 集群的 Docker Engine 实例。节点分为两种角色:

Manager 节点

  • 负责集群管理和编排
  • 调度任务到 Worker 节点
  • 维护集群期望状态
  • 建议奇数个 Manager(3、5、7)实现高可用
  • 可以同时运行服务任务(默认行为)

Worker 节点

  • 执行 Manager 分配的任务
  • 报告任务状态给 Manager
  • 不参与集群管理决策

Raft 共识算法深入理解

Swarm Manager 节点使用 Raft 共识算法来管理集群状态。理解 Raft 的核心概念对于正确部署和维护 Swarm 集群至关重要。

Quorum(法定人数)机制

Raft 要求大多数 Manager 节点(称为 Quorum)必须就集群状态更新达成一致。这意味着:

  • 集群配置变更(添加/删除节点)需要 Quorum 同意
  • 服务创建、更新、扩缩容需要 Quorum 同意
  • 任何管理操作都需要 Quorum 参与

为什么必须是奇数个 Manager?

增加 Manager 节点会提高容错能力,但也会降低写入性能,因为更多节点需要确认状态更新。关键原则是:偶数个 Manager 并不能比少一个的奇数个提供更多容错能力。

Manager 数量Quorum(大多数)容错能力说明
110单点故障,不推荐生产使用
220无容错,不推荐
321最小生产配置,可容忍 1 个节点故障
431与 3 个 Manager 容错能力相同,不推荐
532推荐配置,可容忍 2 个节点故障
642与 5 个 Manager 容错能力相同,不推荐
743大型集群推荐,可容忍 3 个节点故障

公式:容错能力 = (N-1)/2(N 为 Manager 数量)

失去 Quorum 的后果

当集群失去 Quorum 时:

  • 现有的 Worker 节点上的任务继续运行(不受影响)
  • 无法执行任何管理操作:添加/删除节点、创建/更新服务、扩缩容等
  • Manager 节点之间的通信中断
# 失去 Quorum 时尝试管理操作的错误
docker node ls
# Error: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state.
# Please run this command on a manager node or promote the current node to a manager.

Manager 节点资源隔离

由于 Manager 节点运行 Raft 共识算法,对资源敏感,建议将 Manager 节点与重负载任务隔离:

# 将 Manager 节点设为 Drain 状态,不运行任务
docker node update --availability drain <manager-node>

# 查看 Manager 状态
docker node inspect <manager-node> --format '{{.Spec.Availability}}'
# 输出: drain

Manager 节点 Drain 后

  • 已运行的任务会被重新调度到其他 Worker 节点
  • Manager 专注于集群管理,不受工作负载影响
  • 提高集群稳定性

服务(Service)

服务是 Swarm 中的核心概念,定义了需要在集群中运行的任务。服务有两种模式:

副本模式(Replicated):指定运行指定数量的副本任务

# 创建 3 个副本的 Nginx 服务
docker service create --name web --replicas 3 -p 80:80 nginx

全局模式(Global):在每个符合条件的节点上运行一个任务

# 在每个节点运行一个监控代理
docker service create --name monitor --mode global alpine ping localhost

任务(Task)

任务是 Swarm 调度的最小单位,每个任务对应一个容器。任务一旦分配到节点,就不能迁移到其他节点。

服务 (Service)

├── 任务 1 (Task 1) ──→ 容器 ──→ 节点 A
├── 任务 2 (Task 2) ──→ 容器 ──→ 节点 B
└── 任务 3 (Task 3) ──→ 容器 ──→ 节点 C

负载均衡

Swarm 提供两种负载均衡机制:

入口负载均衡(Ingress)

  • 通过路由网格(Routing Mesh)实现
  • 外部请求可访问任意节点的发布端口
  • 自动路由到后端任务

内部负载均衡

  • 基于 DNS 的服务发现
  • 服务名称自动解析
  • 请求分发到所有副本

创建 Swarm 集群

环境准备

网络要求

  • 所有节点网络互通
  • 开放必要端口:
    • TCP 2377:集群管理通信
    • TCP/UDP 7946:节点发现
    • UDP 4789:Overlay 网络流量(VXLAN)

初始化 Swarm

在 Manager 节点上执行:

# 初始化 Swarm,指定 Manager 节点的 IP
docker swarm init --advertise-addr <MANAGER-IP>

# 输出示例
Swarm initialized: current node (abc123) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-xxx <MANAGER-IP>:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

初始化参数说明

参数说明
--advertise-addrManager 节点的广播地址
--listen-addr监听地址(默认 0.0.0.0:2377)
--data-path-addr数据流量地址

添加 Worker 节点

在其他主机上执行加入命令:

# 使用初始化时显示的命令
docker swarm join --token SWMTKN-1-xxx <MANAGER-IP>:2377

# 如果忘记了 token,在 Manager 上查看
docker swarm join-token worker

添加 Manager 节点

为了高可用,建议部署多个 Manager 节点:

# 在 Manager 上获取 Manager 加入 token
docker swarm join-token manager

# 在新节点上执行
docker swarm join --token SWMTKN-1-xxx <MANAGER-IP>:2377

高可用建议

  • 3 个 Manager:容忍 1 个 Manager 故障
  • 5 个 Manager:容忍 2 个 Manager 故障
  • 7 个 Manager:容忍 3 个 Manager 故障

跨可用区部署策略

对于生产环境,建议将 Manager 节点分布在不同的可用区(Availability Zone),以应对整个数据中心或机架的故障:

Manager 总数可用区分布(3 个 AZ)每区 Manager 数量
3Zone-A, Zone-B, Zone-C1-1-1
5Zone-A, Zone-B, Zone-C2-2-1
7Zone-A, Zone-B, Zone-C3-2-2
9Zone-A, Zone-B, Zone-C3-3-3

部署原则

  1. 至少 3 个可用区:确保单可用区故障时仍能保持 Quorum
  2. Manager 分布均匀:避免某个可用区 Manager 过于集中
  3. Worker 可按需分布:Worker 节点可根据业务需求灵活分布
  4. 网络延迟考虑:Manager 节点之间网络延迟应尽量低(建议 < 10ms)

示例:跨可用区初始化

# 可用区 A 的第一个 Manager
docker swarm init --advertise-addr 10.0.1.10

# 可用区 B 的 Manager
docker swarm join --token SWMTKN-1-xxx 10.0.1.10:2377 --advertise-addr 10.0.2.10

# 可用区 C 的 Manager
docker swarm join --token SWMTKN-1-xxx 10.0.1.10:2377 --advertise-addr 10.0.3.10

Swarm 锁定(Autolock)安全配置

Swarm Autolock 是一个重要的安全特性,用于保护 Swarm 的加密密钥。启用后,每次 Manager 节点重启都需要提供解锁密钥才能访问 Swarm 数据。

为什么需要 Autolock?

Swarm 存储以下敏感数据:

  • Raft 日志加密密钥
  • 用于加密/解密网络通信的 TLS 证书
  • Secrets 和 Configs 的加密密钥

默认情况下,这些密钥存储在磁盘上。如果攻击者获取了磁盘访问权限,就可能读取这些敏感数据。Autolock 通过要求解锁密钥来保护这些数据。

启用 Autolock

# 在现有 Swarm 上启用 Autolock
docker swarm update --autolock=true

# 输出
Swarm updated.
To unlock a swarm manager after it restarts, run the `docker swarm unlock`
command and provide the following key:

SWMKEY-1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Please remember to store this key in a secure location!

# 初始化时启用 Autolock
docker swarm init --autolock=true --advertise-addr <IP>

重要:解锁密钥必须安全保存!丢失密钥将无法恢复 Swarm 数据。

管理 Autolock 密钥

# 查看当前解锁密钥
docker swarm unlock-key

# 轮换解锁密钥(建议定期执行)
docker swarm unlock-key --rotate

# 输出
Successfully rotated key.
The new key is: SWMKEY-1-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

解锁 Swarm

当 Manager 节点重启后,需要解锁才能重新加入集群:

# Manager 重启后尝试执行命令
docker node ls
# Error: Swarm is encrypted and needs to be unlocked.

# 解锁 Swarm
docker swarm unlock
# 提示输入密钥
Please enter unlock key: SWMKEY-1-xxx...

# 解锁成功后即可正常操作
docker node ls

Autolock 最佳实践

实践说明
密钥备份将解锁密钥存储在安全的密钥管理系统(如 HashiCorp Vault)
定期轮换建议每 90 天轮换一次解锁密钥
访问控制限制解锁密钥的访问权限
文档化记录解锁流程和紧急联系人
测试恢复定期测试解锁流程是否正常工作

固定 IP 地址配置

Manager 节点应该使用固定 IP 地址作为广播地址。如果所有 Manager 节点重启后获得新 IP,它们将无法相互联系,导致集群处于挂起状态。

# 使用固定 IP 初始化 Swarm
docker swarm init --advertise-addr 192.168.1.100

# 加入时也指定固定 IP
docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377 --advertise-addr 192.168.1.101

注意:Worker 节点可以使用动态 IP,因为它们不参与 Raft 共识

查看集群状态

# 查看节点列表
docker node ls

# 输出示例
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
abc123xyz * manager1 Ready Active Leader 24.0.0
def456uvw worker1 Ready Active 24.0.0
ghi789rst worker2 Ready Active 24.0.0

# 查看节点详情
docker node inspect <NODE-ID>

# 查看当前节点信息
docker node inspect self

节点状态说明

状态说明
Ready节点健康,可以接受任务
Down节点不可达或停止
UnknownManager 无法获取节点状态

可用性说明

可用性说明
Active可以接受新任务
Pause不接受新任务,现有任务继续运行
Drain不接受新任务,现有任务迁移到其他节点

节点管理

节点操作

# 更新节点可用性
docker node update --availability active <NODE-ID>
docker node update --availability pause <NODE-ID>
docker node update --availability drain <NODE-ID>

# 添加节点标签(用于服务调度)
docker node update --label-add zone=east <NODE-ID>
docker node update --label-add env=prod <NODE-ID>

# 移除节点标签
docker node update --label-rm zone <NODE-ID>

# 提升 Worker 为 Manager
docker node promote <NODE-ID>

# 降级 Manager 为 Worker
docker node demote <NODE-ID>

# 删除节点(先 drain)
docker node update --availability drain <NODE-ID>
docker node rm <NODE-ID>

节点离开集群

# Worker 节点主动离开
docker swarm leave

# Manager 节点离开(需要先降级或强制)
docker swarm leave --force

# 从 Manager 上移除已离开的节点
docker node rm <NODE-ID>

服务管理

创建服务

# 基本创建
docker service create --name web nginx

# 指定副本数量
docker service create --name web --replicas 3 nginx

# 指定镜像版本
docker service create --name web nginx:1.25-alpine

# 发布端口
docker service create --name web --replicas 3 -p 80:80 nginx

# 指定网络
docker service create --name web --network mynet nginx

# 设置环境变量
docker service create --name app -e MYSQL_HOST=db -e MYSQL_PORT=3306 myapp

# 挂载卷
docker service create --name db --mount type=volume,source=db-data,target=/var/lib/mysql mysql

# 设置资源限制
docker service create --name app \
--limit-cpu 0.5 \
--limit-memory 512m \
myapp

# 使用私有镜像仓库
docker service create --name app --with-registry-auth myregistry/myapp:v1

查看服务

# 列出所有服务
docker service ls

# 输出示例
ID NAME REPLICAS IMAGE PORTS
abc123xyz web 3/3 nginx:latest *:80->80/tcp

# 查看服务详情
docker service ps web

# 输出示例
ID NAME NODE DESIRED STATE CURRENT STATE ERROR PORTS
def456uvw web.1 worker1 Running Running 2 minutes ago
ghi789rst web.2 worker2 Running Running 2 minutes ago
jkl012opq web.3 worker1 Running Running 2 minutes ago

# 查看服务配置
docker service inspect web

# 查看服务日志
docker service logs web

# 实时查看日志
docker service logs -f web

服务扩缩容

# 扩展到 5 个副本
docker service scale web=5

# 缩减到 2 个副本
docker service scale web=2

# 批量扩缩容
docker service scale web=5 api=3 db=1

更新服务

# 更新镜像版本
docker service update --image nginx:1.26 web

# 添加环境变量
docker service update --env-add NODE_ENV=production web

# 发布新端口
docker service update --publish-add 443:443 web

# 移除端口
docker service update --publish-rm 80 web

# 更新资源限制
docker service update --limit-memory 1g web

# 添加挂载
docker service update --mount-add type=volume,source=data,target=/data web

# 强制更新(重新部署所有任务)
docker service update --force web

滚动更新配置

# 创建带滚动更新配置的服务
docker service create \
--name web \
--replicas 10 \
--update-delay 10s \
--update-parallelism 2 \
--update-failure-action pause \
nginx:latest

# 更新时触发滚动更新
docker service update --image nginx:1.26 web

滚动更新参数

参数默认值说明
--update-delay0s批次更新间隔
--update-parallelism1每批更新数量
--update-failure-actionpause失败时行为:pause/continue/rollback
--update-max-failure-ratio0允许的失败比例
--update-monitor5s监控任务健康的时长

服务回滚

# 手动回滚到上一版本
docker service rollback web

# 配置自动回滚
docker service create \
--name web \
--replicas 5 \
--rollback-delay 0s \
--rollback-parallelism 1 \
--rollback-monitor 20s \
--rollback-max-failure-ratio 0.2 \
nginx:latest

# 更新失败时自动回滚
docker service update \
--update-failure-action rollback \
--image nginx:1.26 web

删除服务

# 删除服务
docker service rm web

# 删除多个服务
docker service rm web api db

服务调度

Swarm 调度器负责将服务任务分配到合适的节点。理解调度器的工作原理有助于更好地设计服务部署策略。

调度器工作原理

Swarm 调度器遵循以下原则:

  1. 声明式模型:你定义期望状态,Swarm 自动维护
  2. 最终一致性:调度器逐步达到期望状态,而非立即强制
  3. 最小干扰原则:避免不必要地移动运行中的任务

调度决策流程

服务定义(期望状态)


┌─────────────────────────────────┐
│ 调度器评估 │
│ ┌─────────┐ ┌─────────┐ │
│ │ 约束检查 │ │ 资源检查 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ └─────┬─────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 节点打分排序 │ │
│ └────────┬────────┘ │
└─────────────┼───────────────────┘

选择最优节点分配任务

节点选择策略

调度器会为每个可用节点打分,考虑因素包括:

  • 是否满足约束条件
  • 资源可用性(CPU、内存)
  • 当前运行任务数量(倾向于负载均衡)
  • 节点可用性状态

放置约束

使用约束控制服务运行在哪些节点上:

# 只在指定节点运行
docker service create --name app \
--constraint node.id==abc123xyz \
myapp

# 按节点角色约束
docker service create --name app \
--constraint node.role==worker \
myapp

# 按主机名约束
docker service create --name app \
--constraint node.hostname==worker1 \
myapp

# 按标签约束
docker service create --name app \
--constraint node.labels.zone==east \
myapp

# 多个约束(AND 关系)
docker service create --name app \
--constraint node.labels.env==prod \
--constraint node.labels.zone==east \
myapp

# 排除约束
docker service create --name app \
--constraint node.labels.env!=dev \
myapp

约束操作符

操作符说明示例
==等于node.role==worker
!=不等于node.labels.env!=dev

放置偏好

使用偏好实现均匀分布:

# 按数据中心均匀分布
docker service create --name app \
--placement-pref spread=node.labels.datacenter \
--replicas 10 \
myapp

# 多级分布(先按数据中心,再按机架)
docker service create --name app \
--placement-pref spread=node.labels.datacenter \
--placement-pref spread=node.labels.rack \
--replicas 20 \
myapp

资源预留与限制

Swarm 支持两种资源管理方式:预留(Reservation)和限制(Limit)。

预留 vs 限制

类型说明作用
预留(Reservation)保证最小可用资源调度时确保节点有足够资源
限制(Limit)限制最大使用资源运行时限制容器资源使用
# 预留资源:确保任务分配到有足够资源的节点
docker service create --name app \
--reserve-cpu 2 \
--reserve-memory 1g \
myapp

# 限制资源:限制容器最多使用的资源
docker service create --name app \
--limit-cpu 1 \
--limit-memory 512m \
myapp

# 同时设置预留和限制
docker service create --name app \
--reserve-cpu 0.5 \
--reserve-memory 256m \
--limit-cpu 2 \
--limit-memory 1g \
myapp

资源调度示例

# compose.yaml 资源配置
services:
api:
image: myapi
deploy:
replicas: 3
resources:
reservations:
cpus: '0.5'
memory: 256M
limits:
cpus: '1'
memory: 512M

调度失败排查

当任务处于 Pending 状态时,可能是资源不足:

# 查看任务详情
docker service ps <service> --no-trunc

# 常见错误信息
# "no suitable node (insufficient resources on 3 nodes)"
# "no suitable node (missing plugin on 3 nodes)"

# 查看节点资源
docker node inspect <node> --format '{{.Description.Resources}}'

# 查看集群整体资源
docker info | grep -A 10 "Resources"

调度最佳实践

1. 为生产服务设置资源预留

# 确保数据库服务有足够资源
docker service create --name db \
--reserve-cpu 2 \
--reserve-memory 2g \
--constraint node.labels.role==db \
postgres:15

2. 使用标签区分节点角色

# 为节点添加角色标签
docker node update --label-add role=web web-node-1
docker node update --label-add role=db db-node-1
docker node update --label-add role=worker worker-node-1

# 按角色调度服务
docker service create --name web \
--constraint node.labels.role==web \
nginx

3. 结合约束和偏好实现高可用

# 跨可用区均匀分布
services:
app:
deploy:
replicas: 6
placement:
constraints:
- node.labels.env==prod
preferences:
- spread: node.labels.zone

4. 为 Manager 节点保留资源

# 将 Manager 节点标记为专用
docker node update --availability drain manager-1
docker node update --label-add role=manager manager-1

# 只有管理任务才能调度到 Manager
docker service create --name monitor \
--constraint node.labels.role==manager \
prom/prometheus

网络管理

Overlay 网络

Swarm 使用 Overlay 网络实现跨主机通信:

# 创建 Overlay 网络
docker network create --driver overlay mynet

# 创建加密的 Overlay 网络
docker network create --driver overlay --opt encrypted secure-net

# 查看网络
docker network ls

# 创建服务时连接网络
docker service create --name web --network mynet nginx

# 将现有服务连接到网络
docker service update --network-add mynet web

# 从网络断开服务
docker service update --network-rm mynet web

路由网格

Swarm 的路由网格(Routing Mesh)实现了入口负载均衡:

# 发布端口(所有节点监听)
docker service create --name web -p 80:80 nginx

# 指定协议
docker service create --name dns -p 53:53/udp -p 53:53/tcp bind9

# 绕过路由网格(直接绑定到任务所在节点)
docker service create --name web \
--publish published=80,target=80,mode=host \
nginx

端口发布模式对比

模式说明适用场景
ingress(默认)路由网格,所有节点监听大多数 Web 服务
host直接绑定到任务节点需要特定路由规则

服务发现

Swarm 内置 DNS 服务发现:

# 服务可以通过名称互相访问
# 创建数据库服务
docker service create --name db --network mynet postgres

# 创建 API 服务,可以通过 "db" 主机名访问数据库
docker service create --name api --network mynet -e DB_HOST=db myapi

# 服务 DNS 格式
# <service_name>
# <service_name>.<network_name>
# tasks.<service_name>(返回所有任务 IP)

Secrets 和 Configs

管理 Secrets

Secrets 用于存储敏感数据,如密码、密钥等:

# 从文件创建 secret
docker secret create db_password ./password.txt

# 从标准输入创建 secret
echo "my_secret_password" | docker secret create db_password -

# 列出 secrets
docker secret ls

# 查看 secret 详情
docker secret inspect db_password

# 创建服务时使用 secret
docker service create --name db \
--secret db_password \
-e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
postgres

# 删除 secret
docker secret rm db_password

Secret 特点

  • 存储在 Raft 日志中,加密保存
  • 传输过程中加密
  • 挂载到容器 /run/secrets/ 目录
  • 服务删除时自动清理

管理 Configs

Configs 用于存储非敏感配置数据:

# 从文件创建 config
docker config create nginx_config ./nginx.conf

# 从标准输入创建 config
echo "server { listen 80; }" | docker config create nginx_config -

# 列出 configs
docker config ls

# 查看 config 详情
docker config inspect nginx_config

# 创建服务时使用 config
docker service create --name web \
--config src=nginx_config,target=/etc/nginx/nginx.conf \
-p 80:80 \
nginx

# 更新服务的 config
docker service update --config-rm nginx_config --config-add src=nginx_config_v2,target=/etc/nginx/nginx.conf web

# 删除 config
docker config rm nginx_config

使用 Stack 部署

Stack 允许使用 Docker Compose 文件部署多服务应用。在 Swarm 中部署 Stack 时,支持 Compose Specification 格式,不需要指定 version 字段。

compose.yaml 示例

services:
web:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3

api:
image: myapi:latest
networks:
- frontend
- backend
environment:
- DB_HOST=db
deploy:
replicas: 2
resources:
limits:
cpus: '0.5'
memory: 512M
depends_on:
- db

db:
image: postgres:15-alpine
networks:
- backend
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
deploy:
placement:
constraints:
- node.role==worker

networks:
frontend:
backend:

volumes:
db-data:

secrets:
db_password:
external: true

Stack 命令

# 部署 stack
docker stack deploy -c compose.yaml myapp

# 列出 stacks
docker stack ls

# 查看 stack 中的服务
docker stack services myapp

# 查看 stack 中的任务
docker stack ps myapp

# 删除 stack
docker stack rm myapp

高可用配置

Manager 高可用

Swarm 使用 Raft 共识算法实现 Manager 高可用:

Manager 节点状态:
- Leader:处理所有集群管理任务
- Reachable:可达的 Follower
- Unreachable:不可达的 Follower

Raft 写入流程:
1. 客户端发送请求到任意 Manager
2. 请求转发到 Leader
3. Leader 提交到 Raft 日志
4. 复制到大多数 Manager
5. 应用更改并返回响应

高可用最佳实践

  • 部署 3、5 或 7 个 Manager(奇数个)
  • Manager 节点分布在不同故障域
  • 使用负载均衡器分发 API 请求
  • 定期备份 Swarm 数据

备份和恢复

Swarm 的状态数据存储在 /var/lib/docker/swarm/ 目录中,包含:

  • Raft 日志和快照
  • 加密密钥(用于 Raft 日志加密)
  • 网络配置
  • 服务定义
  • Secrets 和 Configs 的引用

重要提示:备份文件包含敏感的加密密钥,必须妥善保管。

备份 Swarm 数据

# 在任意 Manager 节点上执行(建议在 Leader 上)

# 方法 1:停止 Docker 服务后备份(推荐,确保数据一致性)
sudo systemctl stop docker
sudo tar czf swarm-backup-$(date +%Y%m%d-%H%M).tar.gz /var/lib/docker/swarm
sudo systemctl start docker

# 方法 2:不停机备份(可能有不一致风险)
sudo tar czf swarm-backup-$(date +%Y%m%d-%H%M).tar.gz /var/lib/docker/swarm

# 验证备份文件
tar tzf swarm-backup-*.tar.gz | head -20

备份最佳实践

实践说明
定期备份建议每天至少备份一次
多位置存储将备份存储在异地或云存储
加密备份使用 GPG 或其他工具加密备份文件
测试恢复定期测试备份恢复流程
记录解锁密钥如果启用了 Autolock,需要单独保存解锁密钥

恢复 Swarm 数据

场景一:单个 Manager 节点恢复

# 1. 停止 Docker 服务
sudo systemctl stop docker

# 2. 恢复数据
sudo rm -rf /var/lib/docker/swarm
sudo tar xzf swarm-backup.tar.gz -C /

# 3. 启动 Docker 服务
sudo systemctl start docker

# 4. 验证集群状态
docker node ls

场景二:恢复到新节点

# 1. 在新节点上停止 Docker
sudo systemctl stop docker

# 2. 恢复数据
sudo tar xzf swarm-backup.tar.gz -C /

# 3. 启动 Docker
sudo systemctl start docker

# 4. 使用 --force-new-cluster 重建单节点集群
docker swarm init --force-new-cluster --advertise-addr <NEW-IP>

# 5. 添加新的 Manager 节点
docker swarm join-token manager
# 在其他新节点上执行 join 命令

灾难恢复:失去 Quorum

当集群失去 Quorum(大多数 Manager 不可用)时,无法执行任何管理操作。

检查是否失去 Quorum

# 查看节点状态
docker node ls
# 如果显示 "Error: This node is not a swarm manager" 或类似错误
# 说明可能失去了 Quorum

# 检查 Raft 状态
docker info | grep -A 10 "Swarm"

恢复步骤

# 步骤 1:选择一个存活的 Manager 节点

# 步骤 2:强制重建单节点集群
# 这会移除所有其他 Manager,只保留当前节点
docker swarm init --force-new-cluster --advertise-addr <MANAGER-IP>

# 步骤 3:验证集群状态
docker node ls
# 应该只显示当前节点为 Leader

# 步骤 4:重新添加 Manager 节点
docker swarm join-token manager
# 在其他节点上执行 join 命令

# 步骤 5:验证高可用恢复
docker node ls

重要警告

  • --force-new-cluster 会丢失不可达 Manager 节点上的所有状态
  • Worker 节点上的任务会继续运行
  • 执行后需要重新添加其他 Manager 节点

节点恢复最佳实践

不要

  • 从另一个节点复制 /var/lib/docker/swarm/ 目录
  • 尝试手动修改 Raft 数据
  • 在不备份的情况下执行 --force-new-cluster

应该

  • 先尝试恢复不可达的 Manager 节点
  • 使用 docker node rm --force 移除故障节点
  • 在执行恢复操作前备份当前状态

监控和故障排除

查看集群状态

# 查看节点状态
docker node ls

# 查看服务状态
docker service ls

# 查看任务状态
docker service ps <service>

# 查看集群详细配置
docker info

日志查看

# 查看服务日志
docker service logs <service>

# 查看特定任务日志
docker service logs <task-id>

# 实时跟踪日志
docker service logs -f <service>

# 查看最近 100 行
docker service logs --tail 100 <service>

常见问题

任务一直处于 Pending 状态

症状:服务创建后,任务一直显示为 Pending,无法调度到节点。

诊断步骤

# 查看任务详细状态
docker service ps <service> --no-trunc

# 查看任务错误信息
docker service ps <service> --format "{{.Error}}"

# 查看节点资源状态
docker node ls
docker node inspect <node> --format '{{.Description.Resources}}'

常见原因及解决方案

原因错误信息解决方案
约束不匹配"no suitable node"检查节点标签是否满足约束
资源不足"insufficient resources"增加节点资源或减少资源预留
网络问题"network not found"检查网络是否创建成功
镜像拉取失败"image could not be pulled"检查镜像名称、认证、网络
节点不可用"node is not available"检查节点状态是否为 Ready
# 解决方案示例

# 1. 约束问题:添加或修改节点标签
docker node update --label-add zone=east <node>

# 2. 资源问题:调整服务资源预留
docker service update --reserve-memory 256m <service>

# 3. 网络问题:重新创建网络
docker network create --driver overlay mynet
docker service update --network-add mynet <service>

# 4. 镜像问题:使用正确的镜像名
docker service update --image myregistry/myapp:v1 <service>

节点 Unreachable

症状:Manager 节点显示为 Unreachable,无法参与集群管理。

诊断步骤

# 检查节点列表
docker node ls
# 输出可能显示:
# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
# xxx manager2 Ready Active Unreachable

# 检查网络连通性
ping <node-ip>
telnet <node-ip> 2377

# 检查 Docker 服务状态
sudo systemctl status docker

# 检查防火墙规则
sudo iptables -L -n | grep 2377

# 检查 Docker 日志
journalctl -u docker.service -f

解决方案

# 方案 1:恢复节点(如果网络问题已解决)
# 在 Unreachable 节点上执行
docker swarm leave
docker swarm join --token SWMTKN-1-xxx <leader-ip>:2377

# 方案 2:强制移除节点(如果无法恢复)
# 在 Leader Manager 上执行
docker node rm --force <unreachable-node-id>

# 方案 3:如果是 Manager 节点,先降级再移除
docker node demote <node-id>
docker node rm <node-id>

# 方案 4:添加新的 Manager 节点
docker swarm join-token manager

Manager 选举问题

症状:集群没有 Leader,所有 Manager 显示为 Unreachable。

诊断步骤

# 检查 Manager 状态
docker node ls

# 检查是否失去 Quorum
# 如果 Manager 总数为 N,需要 (N/2)+1 个可用才能有 Quorum

# 检查 Raft 日志
ls -la /var/lib/docker/swarm/raft/

解决方案

# 步骤 1:确认哪些 Manager 可用
# 在每个 Manager 上执行
docker info | grep "Is Manager"

# 步骤 2:如果仍有 Quorum(大多数 Manager 可用)
# 等待自动重新选举,或重启一个 Manager 触发选举
sudo systemctl restart docker

# 步骤 3:如果失去 Quorum
# 选择一个存活的 Manager 执行(会丢失部分数据!)
docker swarm init --force-new-cluster --advertise-addr <ip>

# 步骤 4:重新添加 Manager 节点
docker swarm join-token manager

服务更新失败

症状:服务更新后任务启动失败,服务状态异常。

诊断步骤

# 查看服务状态
docker service inspect <service> --pretty

# 查看任务状态
docker service ps <service>

# 查看任务日志
docker service logs <service>

解决方案

# 方案 1:回滚到上一版本
docker service rollback <service>

# 方案 2:更新到正确的镜像
docker service update --image <correct-image> <service>

# 方案 3:强制重新创建
docker service update --force <service>

# 方案 4:检查并修复配置
docker service update --env-rm WRONG_VAR --env-add CORRECT_VAR=value <service>

网络通信问题

症状:服务之间无法通信,或外部无法访问服务。

诊断步骤

# 检查网络配置
docker network inspect <network>

# 进入容器测试网络
docker exec -it <container> sh
ping <service-name>
nslookup <service-name>

# 检查端口映射
docker service inspect <service> --format '{{json .Endpoint.Ports}}'

# 检查 iptables 规则
sudo iptables -t nat -L -n | grep <port>

解决方案

# 方案 1:重新创建网络
docker network rm <network>
docker network create --driver overlay <network>
docker service update --network-add <network> <service>

# 方案 2:检查并开放防火墙端口
sudo ufw allow 2377/tcp
sudo ufw allow 7946/tcp
sudo ufw allow 7946/udp
sudo ufw allow 4789/udp

# 方案 3:检查 overlay 网络加密
# 如果启用了加密但有问题,尝试禁用
docker network create --driver overlay mynet
# 而非
# docker network create --driver overlay --opt encrypted mynet

存储卷问题

症状:服务重启后数据丢失,或多个副本无法共享数据。

诊断步骤

# 查看服务挂载配置
docker service inspect <service> --format '{{json .Spec.TaskTemplate.ContainerSpec.Mounts}}'

# 查看卷信息
docker volume ls
docker volume inspect <volume>

# 检查卷驱动
docker info | grep "Volume Plugins"

解决方案

# 方案 1:使用命名卷持久化数据
docker service create --name db \
--mount type=volume,source=db-data,target=/var/lib/postgresql/data \
postgres:15

# 方案 2:使用 NFS 等共享存储实现跨节点数据共享
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=10.0.0.1,rw \
--opt device=:/export/data \
shared-data

# 方案 3:检查卷权限问题
docker service update --force <service>

内存不足(OOM)

症状:任务频繁重启,退出码为 137。

诊断步骤

# 查看任务退出状态
docker service ps <service> --no-trunc | grep "OOM"

# 查看服务资源限制
docker service inspect <service> --format '{{json .Spec.TaskTemplate.Resources}}'

# 查看容器内存使用
docker stats --no-stream

解决方案

# 方案 1:增加内存限制
docker service update --limit-memory 1g <service>

# 方案 2:优化应用内存使用
# 检查应用是否有内存泄漏

# 方案 3:调整 JVM 等运行时内存配置
docker service update --env-add JAVA_OPTS="-Xmx512m" <service>

Token 过期

症状:使用 join-token 加入集群失败。

解决方案

# 刷新 Worker token
docker swarm join-token --rotate worker

# 刷新 Manager token
docker swarm join-token --rotate manager

# 获取新的 token
docker swarm join-token worker

命令速查

集群管理

命令说明
docker swarm init初始化 Swarm
docker swarm join加入 Swarm
docker swarm leave离开 Swarm
docker swarm join-token获取加入 token
docker swarm update更新 Swarm 配置

节点管理

命令说明
docker node ls列出节点
docker node inspect查看节点详情
docker node update更新节点配置
docker node rm删除节点
docker node promote提升为 Manager
docker node demote降级为 Worker

服务管理

命令说明
docker service create创建服务
docker service ls列出服务
docker service ps查看服务任务
docker service inspect查看服务详情
docker service update更新服务
docker service scale扩缩容服务
docker service rollback回滚服务
docker service rm删除服务
docker service logs查看服务日志

Stack 管理

命令说明
docker stack deploy部署 stack
docker stack ls列出 stack
docker stack services查看 stack 服务
docker stack ps查看 stack 任务
docker stack rm删除 stack

Secret 和 Config

命令说明
docker secret create创建 secret
docker secret ls列出 secrets
docker secret inspect查看 secret 详情
docker secret rm删除 secret
docker config create创建 config
docker config ls列出 configs
docker config inspect查看 config 详情
docker config rm删除 config

小结

Docker Swarm 的核心知识:

  • 核心概念:节点(Manager/Worker)、服务、任务、路由网格
  • 集群管理:初始化集群、加入节点、高可用配置
  • 服务管理:创建、更新、扩缩容、回滚操作
  • 调度策略:约束条件、放置偏好、资源限制
  • 网络配置:Overlay 网络、服务发现、路由网格
  • 配置管理:Secrets 存储敏感数据,Configs 存储配置
  • Stack 部署:使用 Compose 文件部署完整应用栈
  • 故障排除:任务状态检查、日志查看、网络诊断

练习

  1. 初始化一个单节点 Swarm 集群
  2. 创建一个 Nginx 服务,扩展到 3 个副本
  3. 配置滚动更新策略并触发更新
  4. 创建 Overlay 网络并部署多服务应用
  5. 使用 Stack 部署一个完整的 Web 应用栈

参考资源