Docker Swarm 集群管理
Docker Swarm 是 Docker 原生的容器编排工具,内置于 Docker Engine 中,可以将多个 Docker 主机组成一个虚拟的 Docker 主机集群。本章将详细介绍 Docker Swarm 的核心概念和使用方法。
什么是 Docker Swarm?
Docker Swarm 是 Docker 公司开发的容器编排工具,用于管理集群化的 Docker 主机。它将多个 Docker Engine 实例组合成一个虚拟的整体,让你可以像管理单个 Docker 主机一样管理整个集群。
Swarm 与其他编排工具对比
| 特性 | Docker Swarm | Kubernetes | Docker 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(大多数) | 容错能力 | 说明 |
|---|---|---|---|
| 1 | 1 | 0 | 单点故障,不推荐生产使用 |
| 2 | 2 | 0 | 无容错,不推荐 |
| 3 | 2 | 1 | 最小生产配置,可容忍 1 个节点故障 |
| 4 | 3 | 1 | 与 3 个 Manager 容错能力相同,不推荐 |
| 5 | 3 | 2 | 推荐配置,可容忍 2 个节点故障 |
| 6 | 4 | 2 | 与 5 个 Manager 容错能力相同,不推荐 |
| 7 | 4 | 3 | 大型集群推荐,可容忍 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-addr | Manager 节点的广播地址 |
--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 数量 |
|---|---|---|
| 3 | Zone-A, Zone-B, Zone-C | 1-1-1 |
| 5 | Zone-A, Zone-B, Zone-C | 2-2-1 |
| 7 | Zone-A, Zone-B, Zone-C | 3-2-2 |
| 9 | Zone-A, Zone-B, Zone-C | 3-3-3 |
部署原则:
- 至少 3 个可用区:确保单可用区故障时仍能保持 Quorum
- Manager 分布均匀:避免某个可用区 Manager 过于集中
- Worker 可按需分布:Worker 节点可根据业务需求灵活分布
- 网络延迟考虑: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 | 节点不可达或停止 |
| Unknown | Manager 无法获取节点状态 |
可用性说明:
| 可用性 | 说明 |
|---|---|
| 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-delay | 0s | 批次更新间隔 |
--update-parallelism | 1 | 每批更新数量 |
--update-failure-action | pause | 失败时行为:pause/continue/rollback |
--update-max-failure-ratio | 0 | 允许的失败比例 |
--update-monitor | 5s | 监控任务健康的时长 |
服务回滚
# 手动回滚到上一版本
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 调度器遵循以下原则:
- 声明式模型:你定义期望状态,Swarm 自动维护
- 最终一致性:调度器逐步达到期望状态,而非立即强制
- 最小干扰原则:避免不必要地移动运行中的任务
调度决策流程:
服务定义(期望状态)
│
▼
┌─────────────────────────────────┐
│ 调度器评估 │
│ ┌─────────┐ ┌─────────┐ │
│ │ 约束检查 │ │ 资源检查 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ └─────┬─────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 节点打分排序 │ │
│ └────────┬────────┘ │
└─────────────┼───────────────────┘
▼
选择最优节点分配任务
节点选择策略:
调度器会为每个可用节点打分,考虑因素包括:
- 是否满足约束条件
- 资源可用性(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 文件部署完整应用栈
- 故障排除:任务状态检查、日志查看、网络诊断
练习
- 初始化一个单节点 Swarm 集群
- 创建一个 Nginx 服务,扩展到 3 个副本
- 配置滚动更新策略并触发更新
- 创建 Overlay 网络并部署多服务应用
- 使用 Stack 部署一个完整的 Web 应用栈