Docker Swarm 集群实战:节点管理、Service 部署与滚动更新
系统讲解 Docker Swarm 集群的完整运维体系:Swarm 初始化与节点加入、Manager/Worker 节点的职责与高可用(3/5 Manager 奇数原则)、Service 创建与约束(副本数/资源限制/放置策略)、Stack 部署多服务应用、滚动更新与回滚策略配置、健康检查联动自动替换故障容器,以及 Swarm 集群的日常监控与故障处理。
Docker Swarm 是 Docker 原生的集群编排方案,与 Kubernetes 相比配置更简单、运维门槛更低,适合中小规模(百容器级别)的生产部署。本文讲解 Swarm 从初始化到日常运维的完整体系。
Swarm vs Kubernetes
对比维度 Docker Swarm Kubernetes
────────────────────────────────────────────────────
学习曲线 低(复用 Docker 命令) 高(大量新概念)
集群规模 <1000 容器 万级容器
高可用 ✅ Manager HA ✅ Control Plane HA
自动伸缩 ❌ 需手动或外部工具 ✅ HPA/VPA
存储管理 基础 丰富(CSI 生态)
网络策略 基础 Calico/Cilium 等
适用场景 中小规模,快速上线 大规模,复杂需求
结论:已有 docker-compose 经验,快速上生产 → Swarm
长期规划,需要完整生态 → Kubernetes
一、集群初始化
高可用 Swarm 集群规划
生产 Swarm 集群节点规划:
Manager 节点(奇数个,推荐 3 或 5):
swarm-manager-01 192.168.1.10 主 Manager(Leader)
swarm-manager-02 192.168.1.11 备用 Manager
swarm-manager-03 192.168.1.12 备用 Manager
Worker 节点:
swarm-worker-01 192.168.1.20
swarm-worker-02 192.168.1.21
swarm-worker-03 192.168.1.22
Manager 数量与容错:
1 个 Manager → 容忍 0 个 Manager 故障(不推荐生产)
3 个 Manager → 容忍 1 个 Manager 故障(推荐)
5 个 Manager → 容忍 2 个 Manager 故障(大规模)
原理:Raft 共识协议,需要 (N+1)/2 个 Manager 存活才能工作
初始化流程
# 第一台 Manager 节点初始化
docker swarm init \
--advertise-addr 192.168.1.10 \ # 对外通告的 IP
--data-path-addr 192.168.1.10 # overlay 网络流量 IP(可与 advertise 相同)
# 输出:
# Swarm initialized: current node (xxx) is now a manager.
# To add a worker to this swarm, run the following command:
# docker swarm join --token SWMTKN-1-xxx-yyy 192.168.1.10:2377
# To add a manager to this swarm, run 'docker swarm join-token manager'
# 获取 Manager 加入 token
docker swarm join-token manager
# docker swarm join --token SWMTKN-1-xxx-zzz 192.168.1.10:2377
# 获取 Worker 加入 token
docker swarm join-token worker
# 第二、三台 Manager 节点加入
docker swarm join \
--token SWMTKN-1-xxx-zzz \
192.168.1.10:2377 # Manager 节点 IP:端口
# Worker 节点加入
docker swarm join \
--token SWMTKN-1-xxx-yyy \
192.168.1.10:2377
# 在任意 Manager 节点查看集群状态
docker node ls
# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
# abc123 * swarm-manager-01 Ready Active Leader 27.3.1
# def456 swarm-manager-02 Ready Active Reachable 27.3.1
# ghi789 swarm-manager-03 Ready Active Reachable 27.3.1
# jkl012 swarm-worker-01 Ready Active 27.3.1
节点管理
# 给节点打标签(用于服务放置约束)
docker node update --label-add role=web swarm-worker-01
docker node update --label-add role=db swarm-worker-02
docker node update --label-add zone=az1 swarm-worker-01
docker node update --label-add zone=az2 swarm-worker-02
# 临时下线节点(排空工作负载,维护时使用)
docker node update --availability drain swarm-worker-01
# 所有在 worker-01 上的 Task 会迁移到其他节点
# 维护结束,恢复节点
docker node update --availability active swarm-worker-01
# 将 Worker 提升为 Manager(增强 HA)
docker node promote swarm-worker-01
# 降级 Manager 为 Worker
docker node demote swarm-manager-03
# 移除节点(先在该节点离开 Swarm)
# 在要移除的节点上:
docker swarm leave
# 在 Manager 上:
docker node rm swarm-worker-01
二、Service — Swarm 的工作单元
Service 是 Swarm 中运行容器的声明:指定镜像、副本数、资源约束,Swarm 负责保持状态。
# 创建服务(最基础)
docker service create \
--name nginx-web \
--replicas 3 \
--publish published=80,target=80 \
nginx:1.25
# 查看服务
docker service ls
# ID NAME MODE REPLICAS IMAGE PORTS
# abc123 nginx-web replicated 3/3 nginx:1.25 *:80->80/tcp
# 查看服务详情(每个 Task 在哪个节点上)
docker service ps nginx-web
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# xxx nginx-web.1 nginx:1.25 swarm-worker-01 Running Running 2m
# yyy nginx-web.2 nginx:1.25 swarm-worker-02 Running Running 2m
# zzz nginx-web.3 nginx:1.25 swarm-worker-03 Running Running 2m
完整 Service 创建参数
docker service create \
--name api \
\
# 镜像
--image harbor.company.com/backend/api:v1.2.3 \
\
# 副本
--replicas 5 \
\
# 端口(Swarm 会在所有节点开监听,即使该节点没有 Task)
--publish published=8080,target=8080,mode=ingress \
# mode=ingress(默认):Routing Mesh,所有节点转发
# mode=host:只在运行 Task 的节点上开端口
\
# 资源限制
--limit-cpu 1.5 \
--limit-memory 512m \
--reserve-cpu 0.5 \ # 最低保证(调度时考虑)
--reserve-memory 256m \
\
# 重启策略
--restart-condition on-failure \
--restart-delay 5s \
--restart-max-attempts 3 \
\
# 放置约束(服务只调度到有指定标签的节点)
--constraint "node.labels.role==web" \
--constraint "node.role==worker" \ # 只调度到 Worker 节点
\
# 放置偏好(尽量分散到不同 zone)
--placement-pref "spread=node.labels.zone" \
\
# 网络
--network app-overlay \
\
# 环境变量
--env APP_ENV=production \
--env-file .env \
\
# 挂载
--mount type=volume,source=app_data,target=/data \
\
# 健康检查
--health-cmd "curl -f http://localhost:8080/health || exit 1" \
--health-interval 30s \
--health-retries 3
三、滚动更新
# 更新服务镜像(触发滚动更新)
docker service update \
--image harbor.company.com/backend/api:v1.3.0 \
--update-parallelism 2 \ # 每批更新 2 个副本
--update-delay 10s \ # 每批之间等待 10 秒
--update-failure-action rollback \ # 更新失败自动回滚
--update-monitor 30s \ # 每个副本更新后监控 30 秒
--update-max-failure-ratio 0.3 \ # 超过 30% 失败率则停止
api
# 查看更新进度
docker service ps api
# ID NAME IMAGE NODE DESIRED CURRENT
# aaa api.1 api:v1.3.0 node-1 Running Running 1m ← 已更新
# bbb api.2 api:v1.3.0 node-2 Running Running 30s ← 已更新
# ccc api.3 api:v1.2.3 node-3 Running Running 5m ← 待更新
# ddd \_ api.3 api:v1.2.3 node-3 Shutdown Shutdown 30s ← 旧版本已停
滚动更新参数详解
# 更新策略配置(在 service create 时就可以设置默认值)
docker service create \
--name api \
--update-parallelism 1 \ # 每次更新 1 个(最保守)
--update-delay 15s \ # 批间间隔 15 秒
--update-order start-first \ # start-first=先启动新容器再停旧容器(零停机)
# 默认 stop-first=先停旧容器再启动新容器
--update-failure-action rollback \
--update-monitor 60s \
--rollback-parallelism 2 \ # 回滚时每批 2 个
--rollback-delay 5s \
--rollback-order stop-first \
harbor.company.com/backend/api:v1.2.3
手动回滚
# 回滚到上一个版本
docker service rollback api
# 查看回滚进度
docker service ps api --filter desired-state=running
四、Stack — 多服务应用编排
Stack 是 Swarm 版本的 docker-compose,用 YAML 描述多服务集群应用。
# stack.yml
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- type: bind
source: /data/nginx/conf
target: /etc/nginx/conf.d
read_only: true
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
resources:
limits:
cpus: '0.5'
memory: 128M
placement:
constraints:
- node.role == worker
api:
image: harbor.company.com/backend/api:${APP_VERSION}
environment:
- DB_HOST=db
- REDIS_HOST=redis
- APP_ENV=production
secrets:
- db_password # Swarm Secret(加密存储)
networks:
- frontend
- backend
deploy:
replicas: 4
update_config:
parallelism: 2
delay: 15s
order: start-first
failure_action: rollback
monitor: 30s
max_failure_ratio: 0.2
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
placement:
preferences:
- spread: node.labels.zone # 跨 zone 分散
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
secrets:
- db_password
- db_root_password
volumes:
- db_data:/var/lib/mysql
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
- node.labels.role == db # 只在 db 节点运行
resources:
limits:
cpus: '4'
memory: 4G
redis:
image: redis:7.2-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
- node.labels.role == cache
networks:
frontend:
driver: overlay
backend:
driver: overlay
attachable: false # 禁止手动 attach(安全)
volumes:
db_data:
driver: local
redis_data:
driver: local
secrets:
db_password:
external: true # 提前创建的 Swarm Secret
db_root_password:
external: true
# 提前创建 Swarm Secret(加密存储在 Raft 中)
echo "db_secret_123" | docker secret create db_password -
echo "root_secret_456" | docker secret create db_root_password -
# 部署 Stack
APP_VERSION=v1.2.3 docker stack deploy \
-c stack.yml \
myapp
# 查看 Stack
docker stack ls
# NAME SERVICES ORCHESTRATOR
# myapp 4 Swarm
docker stack ps myapp
docker stack services myapp
# 更新(修改 stack.yml 后重新 deploy 即可,Swarm 自动处理差异)
APP_VERSION=v1.3.0 docker stack deploy -c stack.yml myapp
# 删除 Stack(删除所有 Service,但 Volume 保留)
docker stack rm myapp
五、Swarm Secret
# 创建 Secret
echo "SuperSecret123" | docker secret create api_key -
docker secret create ssl_cert /etc/ssl/cert.pem
docker secret create ssl_key /etc/ssl/private.key
# 在 Service 中使用
docker service create \
--name api \
--secret api_key \ # 挂载到 /run/secrets/api_key
--secret source=ssl_cert,target=/etc/nginx/cert.pem \
myapp:v1
# 容器内读取
cat /run/secrets/api_key # 读取 Secret 值
# SuperSecret123
# Secret 特点:
# - 加密存储在 Swarm Raft 数据库中
# - 只传输给需要它的 Task 所在节点
# - 传输时 TLS 加密
# - 只以 tmpfs 形式挂载到容器(不落盘)
# 轮换 Secret(先创建新 Secret,再更新 Service)
echo "NewSecret456" | docker secret create api_key_v2 -
docker service update \
--secret-rm api_key \
--secret-add api_key_v2 \
api
docker secret rm api_key
六、集群监控与故障处理
# 查看 Swarm 节点状态
docker node ls
# 查看节点详情(资源使用、标签等)
docker node inspect swarm-worker-01 --pretty
# 查看服务的所有 Task(包括历史)
docker service ps api --no-trunc
# 查看 Task 失败原因
docker service ps api --filter "desired-state=shutdown" \
--format "{{.Name}}: {{.Error}}"
# 查看 Swarm 事件
docker events --filter scope=swarm
# Leader 选举(当 Leader 故障时自动选举)
# 确认当前 Leader
docker node ls | grep Leader
# 强制新 Leader 选举(运维场景)
docker node demote swarm-manager-01 # 将当前 Leader 降级
# Swarm 自动选举新 Leader
# 扩缩容服务
docker service scale api=8 # 扩到 8 个副本
docker service scale api=2 worker=4 # 同时调整多个 Service
# 强制重新分配(解决 Task 分布不均)
docker service update --force api
小结
Docker Swarm 的核心概念链:Node(节点)→ Service(服务声明)→ Task(实际运行的容器)→ Stack(多 Service 集合)。
生产关键配置三点:Manager 节点奇数个(Raft 共识需要)、滚动更新用 start-first(先起新容器再停旧容器,零停机)、Secret 替代明文环境变量(加密存储,只传给需要的节点)。
Swarm 的主要局限:有状态服务(数据库)副本数只能设 1,存储不如 K8s 灵活,自动伸缩需要外部工具。对于这些需求,K8s 是更好的选择。
