Docker Swarm 深度实践:从集群搭建、服务编排到生产级运维

欢迎你来读这篇博客。

前面几篇文章分别讲了 Docker 基础、Docker Compose、Dockerfile、Docker Tools。这一篇专门讲 Docker Swarm,也就是 Docker
原生的集群编排能力。

本文核心内容包括:

  • Docker Swarm 是什么
  • Docker Swarm 和 Docker Compose、Kubernetes 的区别
  • Swarm 集群架构
  • Manager、Worker、Raft、Quorum
  • Service、Task、Container 的关系
  • 集群端口、防火墙和节点规划
  • 搭建 Docker Swarm 集群
  • 服务部署、扩缩容、滚动更新、回滚
  • Overlay 网络、Ingress 网络和 Routing Mesh
  • 服务发现、VIP、DNSRR
  • Secret、Config
  • Stack 部署
  • 数据卷和有状态服务的坑
  • 节点维护、Drain、Label、Placement
  • 高可用、备份、恢复、故障排查
  • 生产实践和常用脚本

Docker Swarm 的定位可以用一句话概括:

用 Docker 原生命令,把多台 Docker 主机变成一个可以调度服务的集群。

如果 Docker Compose 是“单机多容器编排”,那么 Docker Swarm 就是“多机服务编排”。
它没有 Kubernetes 那么庞大,也没有 Kubernetes 那么复杂。优点是简单、内置、Docker 原生;缺点是生态和扩展能力没有 Kubernetes 强。

Swarm 就像一把短刀:不花哨,但拔出来就能用。
Kubernetes 更像一套军械库:功能强,但你得先学会别把自己炸了。

一、Docker Swarm 是什么?

Docker Swarm 是 Docker Engine 内置的集群管理和服务编排能力。

它可以把多台安装了 Docker Engine 的机器组成一个集群,然后在集群上运行服务。

Docker Swarm 能做这些事情:

  • 创建多节点 Docker 集群
  • 管理 manager 和 worker 节点
  • 部署服务
  • 扩缩容服务副本
  • 服务发现
  • 负载均衡
  • 滚动更新
  • 服务回滚
  • 节点故障迁移
  • Overlay 跨主机网络
  • Secret 和 Config 管理
  • Stack 应用部署
  • 基于约束的服务调度

1. Docker Swarm 的基本结构

flowchart TB
    subgraph SwarmCluster[Docker Swarm Cluster]
        subgraph Managers[Manager Nodes]
            M1[manager-1 Leader]
            M2[manager-2 Reachable]
            M3[manager-3 Reachable]
        end

        subgraph Workers[Worker Nodes]
            W1[worker-1]
            W2[worker-2]
            W3[worker-3]
        end

        M1 <-->|Raft| M2
        M2 <-->|Raft| M3
        M1 <-->|Raft| M3
        M1 -->|Schedule Tasks| W1
        M1 -->|Schedule Tasks| W2
        M1 -->|Schedule Tasks| W3
    end

    USER[Docker CLI / API] --> M1

2. Swarm mode 不是 Classic Swarm

这里要特别说明:

  • Swarm mode 是 Docker Engine 内置的现代 Swarm 能力。
  • Classic Swarm 是早期项目,已经不是现在主流讨论的对象。

我们平时说 Docker Swarm,一般默认指 Docker Engine 的 Swarm mode。

3. Docker Swarm 适合什么场景?

适合:

  • 中小规模服务部署
  • 内部系统
  • 自托管服务集群
  • 简单微服务集群
  • 边缘节点集群
  • 已经熟悉 Docker CLI 的团队
  • 不想引入 Kubernetes 复杂度的场景
  • 需要比 Compose 更强的多机编排能力

不太适合:

  • 大规模云原生平台
  • 极复杂服务治理
  • 复杂 Operator 生态
  • 强依赖 Kubernetes 生态的公司
  • 多租户、强隔离、复杂权限场景
  • 超大规模弹性调度

可以这样理解:

工具 定位
Docker CLI 单容器操作
Docker Compose 单机多容器编排
Docker Swarm 多机 Docker 原生编排
Kubernetes 大规模云原生编排平台

二、Docker Swarm 核心概念

学习 Swarm 先不要急着敲命令。
先把几个对象之间的关系搞清楚,不然后面看到 Service、Task、Container 很容易乱。

1. Swarm

Swarm 是一个 Docker Engine 集群。

一个 Swarm 由多个 Node 组成,每个 Node 本质上都是一台运行 Docker Engine 的机器。

2. Node

Node 是 Swarm 集群中的一台主机,可以是物理机,也可以是虚拟机。

Node 分为两类:

  • Manager Node
  • Worker Node

3. Manager Node

Manager 节点负责集群管理。

职责包括:

  • 维护集群状态
  • 调度 Service
  • 分配 Task
  • 管理节点加入和退出
  • 保存 Raft 状态
  • 处理 Docker Swarm API 请求
  • 管理 Secret 和 Config
  • 参与 Leader 选举

Manager 节点也可以运行容器任务。
但生产环境通常建议把 Manager 设置为 Drain,让它只做管理,不跑业务。

4. Worker Node

Worker 节点负责执行任务。

它不参与 Raft,不做调度决策,也不维护全局集群状态。
它接收 Manager 分配的 Task,然后运行对应容器。

5. Service

Service 是 Swarm 里最核心的部署对象。

它描述的是“期望状态”,例如:

  • 用哪个镜像
  • 运行几个副本
  • 暴露哪个端口
  • 挂载哪些卷
  • 使用哪些网络
  • 使用哪些 Secret
  • 部署到哪些节点
  • 如何滚动更新
  • 失败后如何重启

例如:

1
2
3
4
5
docker service create \
--name web \
--replicas 3 \
--publish published=8080,target=80 \
nginx:latest

这表示:

我希望集群里运行一个名为 web 的服务,副本数为 3,对外发布 8080 端口,容器里监听 80 端口,镜像是 nginx。

6. Task

Task 是 Service 的一次具体调度执行。

一个 Service 有多个副本,每个副本对应一个 Task。
Task 再对应一个实际运行的 Container。

关系如下:

flowchart LR
    S[Service: web replicas=3] --> T1[Task web.1]
    S --> T2[Task web.2]
    S --> T3[Task web.3]
    T1 --> C1[Container on worker-1]
    T2 --> C2[Container on worker-2]
    T3 --> C3[Container on worker-3]

7. Container

Container 是最终运行的容器实例。

在 Swarm 中,不建议直接用 docker run 管理业务容器。
应该用 docker service createdocker stack deploy

因为:

  • docker run 是单机行为
  • docker service 是集群行为
  • Swarm 只会维护 Service 的期望状态,不会维护你手工 docker run 的容器

8. Desired State 期望状态

Swarm 的核心思想是 Desired State。

你告诉 Swarm:

1
我要 3 个 nginx 副本

Swarm 会尽力让真实状态保持为:

1
当前确实有 3 个 nginx task 运行

如果某个 worker 挂了,上面的 task 消失,Manager 会把 task 重新调度到其他可用节点。

这就是编排系统和普通容器启动命令最大的区别。


三、Docker Swarm 工作机制

1. 服务创建流程

sequenceDiagram
    participant User as 用户 / Docker CLI
    participant Manager as Manager Leader
    participant Raft as Raft 状态存储
    participant Scheduler as Scheduler
    participant Worker1 as Worker 1
    participant Worker2 as Worker 2
    User ->> Manager: docker service create
    Manager ->> Raft: 写入 Service 期望状态
    Manager ->> Scheduler: 根据副本数和约束调度 Task
    Scheduler ->> Worker1: 分配 Task web.1
    Scheduler ->> Worker2: 分配 Task web.2
    Worker1 ->> Worker1: 拉取镜像并启动容器
    Worker2 ->> Worker2: 拉取镜像并启动容器
    Worker1 -->> Manager: 上报 Task 状态
    Worker2 -->> Manager: 上报 Task 状态

2. 节点故障流程

假设 worker-1 挂了:

flowchart TB
    A[worker-1 故障] --> B[Manager 检测节点不可用]
    B --> C[worker-1 上的 Task 变为失败/不可达]
    C --> D[Manager 根据 Service 期望状态重新调度]
    D --> E[在 worker-2 或 worker-3 创建新 Task]
    E --> F[副本数恢复到期望状态]

这个过程体现了 Swarm 的编排能力。


四、Raft、Quorum 和 Manager 高可用

Swarm Manager 通过 Raft 维护一致的集群状态。

这部分非常重要。很多 Swarm 集群出问题,不是容器问题,而是 Manager 规划错了。

1. 为什么需要 Raft?

Swarm 集群的状态包括:

  • 有哪些节点
  • 有哪些服务
  • 服务副本数是多少
  • Task 分配到哪里
  • 网络、Secret、Config 等对象
  • 当前哪个节点是 Leader

这些状态必须在多个 Manager 中保持一致。
Raft 就是用来让多个 Manager 对集群状态达成一致。

2. Leader 和 Reachable

Manager 节点常见状态:

状态 含义
Leader 当前主 Manager,负责调度和写入决策
Reachable 可达 Manager,参与 Raft,同步状态
Unreachable 不可达 Manager,需要排查
Down 节点不可用

查看:

1
docker node ls

示例:

1
2
3
4
5
6
ID                            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS
abc123 * manager-1 Ready Active Leader
def456 manager-2 Ready Active Reachable
ghi789 manager-3 Ready Active Reachable
jkl111 worker-1 Ready Active
mno222 worker-2 Ready Active

3. Quorum 多数派

Raft 需要多数 Manager 可用才能处理管理操作。

如果 Manager 数量是 N,那么需要超过一半可用。

Manager 数量 多数派 Quorum 可容忍 Manager 故障数
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2
6 4 2
7 4 3

所以生产环境建议:

  • 1 个 Manager:只适合测试或极小环境
  • 3 个 Manager:常见生产起步
  • 5 个 Manager:更高可用
  • 7 个 Manager:官方建议的较高上限
  • 不建议 2、4、6 这种偶数 Manager,因为不会提升对应容错能力

4. 为什么不是 Manager 越多越好?

Manager 越多,需要达成一致的节点越多,写入性能反而会下降。

比如:

  • 创建服务
  • 更新服务
  • 节点加入
  • 节点删除
  • Secret 更新

这些都涉及 Raft 状态变更,需要 Manager 多数派确认。

所以 Manager 不是越多越强。
多了以后不是“高可用”,是“高沟通成本”。

5. Manager 是否应该跑业务?

默认情况下,Manager 也可以跑业务 Task。
小集群可以这样做,但生产建议 Manager 专职管理。

设置 Manager 不接收业务任务:

1
2
3
docker node update --availability drain manager-1
docker node update --availability drain manager-2
docker node update --availability drain manager-3

恢复可调度:

1
docker node update --availability active manager-1

五、Docker Swarm 网络端口规划

搭 Swarm 之前,必须先规划网络和防火墙。

1. Swarm 常用端口

端口 协议 用途 是否必须
2377 TCP 集群管理通信,节点加入 Manager Manager 需要
7946 TCP/UDP 节点间通信、服务发现、Gossip 所有节点需要
4789 UDP Overlay 网络 VXLAN 数据面 所有节点需要
发布端口 TCP/UDP 业务服务对外端口 按业务需要

2. 防火墙示例

假设集群内网网段:

1
10.0.0.0/24

UFW 示例:

1
2
3
4
5
6
7
8
9
10
11
# Manager 节点
sudo ufw allow from 10.0.0.0/24 to any port 2377 proto tcp

# 所有节点
sudo ufw allow from 10.0.0.0/24 to any port 7946 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 7946 proto udp
sudo ufw allow from 10.0.0.0/24 to any port 4789 proto udp

# 业务端口,例如 80 和 443
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

如果业务端口只给负载均衡器访问:

1
2
sudo ufw allow from 10.0.1.10 to any port 80 proto tcp
sudo ufw allow from 10.0.1.10 to any port 443 proto tcp

3. Manager 建议使用固定 IP

Manager 节点用于维护集群状态和节点通信,建议使用固定 IP。

初始化时:

1
docker swarm init --advertise-addr 10.0.0.11

如果 Manager IP 经常变化,其他节点可能找不到它,集群稳定性会受影响。

Worker 可以使用动态 IP,但生产环境也建议尽量稳定。


六、搭建 Docker Swarm 集群

下面以 3 台机器为例:

主机名 IP 角色
manager-1 10.0.0.11 Manager
worker-1 10.0.0.21 Worker
worker-2 10.0.0.22 Worker

所有机器都安装 Docker Engine。

1. 设置主机名

在 manager-1:

1
sudo hostnamectl set-hostname manager-1

在 worker-1:

1
sudo hostnamectl set-hostname worker-1

在 worker-2:

1
sudo hostnamectl set-hostname worker-2

可以配置 /etc/hosts

1
2
3
10.0.0.11 manager-1
10.0.0.21 worker-1
10.0.0.22 worker-2

2. 初始化 Swarm

在 manager-1 执行:

1
docker swarm init --advertise-addr 10.0.0.11

输出类似:

1
2
3
4
5
6
7
Swarm initialized: current node is now a manager.

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

docker swarm join \
--token SWMTKN-xxx \
10.0.0.11:2377

3. Worker 加入集群

在 worker-1 和 worker-2 执行上面输出的 join 命令:

1
2
3
docker swarm join \
--token SWMTKN-xxx \
10.0.0.11:2377

如果忘了 join 命令,在 Manager 上查看:

1
docker swarm join-token worker

查看 Manager 加入命令:

1
docker swarm join-token manager

4. 查看节点

在 manager-1 执行:

1
docker node ls

输出类似:

1
2
3
4
ID                            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS
abc123 * manager-1 Ready Active Leader
def456 worker-1 Ready Active
ghi789 worker-2 Ready Active

5. 新增 Manager 节点

如果后续要把 worker-1 提升为 Manager:

1
docker node promote worker-1

或者在新节点上使用 manager join token:

1
docker swarm join-token manager

6. 降级 Manager

1
docker node demote manager-2

注意:不要把 Manager 降到没有 quorum。别在生产上练胆量,集群不是练习簿。

7. 节点退出 Swarm

Worker 主动退出:

1
docker swarm leave

Manager 强制退出:

1
docker swarm leave --force

从 Manager 删除节点:

1
docker node rm worker-1

强制删除不可达节点:

1
docker node rm --force worker-1

七、部署第一个 Swarm Service

1. 创建 Nginx 服务

在 Manager 上执行:

1
2
3
4
5
docker service create \
--name web \
--replicas 3 \
--publish published=8080,target=80 \
nginx:latest

含义:

参数 说明
--name web 服务名
--replicas 3 运行 3 个副本
--publish published=8080,target=80 对外发布 8080,容器内 80
nginx:latest 镜像

2. 查看服务

1
docker service ls

查看具体 Task:

1
docker service ps web

输出类似:

1
2
3
4
ID        NAME      IMAGE          NODE       DESIRED STATE   CURRENT STATE
aaa web.1 nginx:latest worker-1 Running Running
bbb web.2 nginx:latest worker-2 Running Running
ccc web.3 nginx:latest manager-1 Running Running

3. 访问服务

在任意节点访问:

1
2
3
curl http://10.0.0.11:8080
curl http://10.0.0.21:8080
curl http://10.0.0.22:8080

如果使用 ingress routing mesh,只要服务发布了端口,任意节点都可以接收该端口流量,并转发给实际运行 Task 的节点。

4. 查看服务详情

1
docker service inspect web --pretty

5. 查看日志

1
2
3
docker service logs web
docker service logs -f web
docker service logs --tail 100 web

6. 删除服务

1
docker service rm web

八、服务扩缩容

1. 扩容

1
docker service scale web=5

查看:

1
docker service ps web

2. 缩容

1
docker service scale web=2

3. 多服务一起扩缩容

1
docker service scale web=3 api=5 worker=2

4. 扩缩容背后的机制

flowchart LR
    A[docker service scale web=5] --> B[Manager 更新期望状态 replicas=5]
    B --> C[Scheduler 计算需要新增 Task]
    C --> D[分配 Task 到合适节点]
    D --> E[Worker 拉镜像并启动容器]
    E --> F[服务副本数达到 5]

九、滚动更新与回滚

滚动更新是 Swarm 很实用的能力。

1. 创建带更新策略的服务

1
2
3
4
5
6
7
8
docker service create \
--name web \
--replicas 4 \
--publish published=8080,target=80 \
--update-parallelism 1 \
--update-delay 10s \
--update-failure-action rollback \
nginx:1.25

参数说明:

参数 说明
--update-parallelism 1 每次更新 1 个 Task
--update-delay 10s 每批更新之间等待 10 秒
--update-failure-action rollback 更新失败时自动回滚
--replicas 4 副本数为 4

2. 更新镜像

1
2
3
docker service update \
--image nginx:1.26 \
web

查看更新过程:

1
docker service ps web

3. 更新环境变量

1
2
3
docker service update \
--env-add APP_ENV=prod \
web

删除环境变量:

1
2
3
docker service update \
--env-rm APP_ENV \
web

4. 更新资源限制

1
2
3
4
docker service update \
--limit-cpu 0.5 \
--limit-memory 512M \
web

5. 强制重启服务

有时候镜像 tag 没变,但想重启全部 Task:

1
docker service update --force web

这也可以触发重新调度。

6. 回滚服务

1
docker service rollback web

查看:

1
docker service ps web

7. 更精细的更新策略

1
2
3
4
5
6
7
docker service update \
--update-parallelism 2 \
--update-delay 15s \
--update-monitor 30s \
--update-max-failure-ratio 0.2 \
--update-failure-action rollback \
web

说明:

参数 说明
--update-parallelism 同时更新几个 Task
--update-delay 每批更新时间间隔
--update-monitor 更新后观察多久判断是否失败
--update-max-failure-ratio 可接受失败比例
--update-failure-action 失败后 pause / continue / rollback

十、Routing Mesh:Swarm 的入口流量机制

Routing Mesh 是 Swarm 的核心特性之一。

当你发布一个服务端口时:

1
2
3
4
5
docker service create \
--name web \
--replicas 2 \
--publish published=8080,target=80 \
nginx

Swarm 会让集群中所有节点都监听 8080

即使某个节点没有运行 web 任务,访问这个节点的 8080 端口,也会被转发到真正运行 web task 的节点。

1. Routing Mesh 示意图

flowchart LR
    Client[客户端] -->|访问 worker - 2:8080| W2[worker-2]
    W2 -->|Routing Mesh| W1[worker-1]
    W1 --> C1[web task container]
    Client2[客户端] -->|访问 manager - 1:8080| M1[manager-1]
    M1 -->|Routing Mesh| W3[worker-3]
    W3 --> C2[web task container]

2. Routing Mesh 的优点

  • 任意节点都可以作为入口
  • 服务端口统一
  • 不需要知道容器实际在哪个节点
  • 外部负载均衡器可以把流量打到所有节点
  • 服务副本迁移后入口不变

3. Routing Mesh 的缺点

  • 多一层转发
  • 排查链路更复杂
  • 对某些需要源 IP 的场景不友好
  • 高性能场景可能希望绕过它
  • 需要所有节点开放发布端口

4. Ingress 模式发布端口

默认就是 ingress:

1
2
3
4
5
docker service create \
--name web \
--publish published=8080,target=80,mode=ingress \
--replicas 3 \
nginx

5. Host 模式发布端口

绕过 routing mesh,直接在运行 Task 的节点上发布端口:

1
2
3
4
5
docker service create \
--name web \
--publish published=8080,target=80,mode=host \
--replicas 3 \
nginx

注意:

如果 mode=host 且所有副本都要绑定同一个宿主机端口,那么同一个节点不能运行多个绑定同端口的 Task。

适合:

  • 高性能入口
  • 需要保留源 IP
  • 配合外部负载均衡器
  • 每个节点跑一个实例的 global 服务

十一、Overlay 网络

Docker 单机默认 bridge 网络只能在一台机器上工作。
Swarm 需要跨主机容器通信,所以使用 Overlay 网络。

1. 创建 Overlay 网络

1
2
3
docker network create \
--driver overlay \
app-net

查看:

1
docker network ls

2. 服务加入 Overlay 网络

1
2
3
4
5
6
7
8
9
10
docker service create \
--name redis \
--network app-net \
redis:7

docker service create \
--name app \
--network app-net \
--env REDIS_HOST=redis \
demo-app:1.0.0

在同一个 Overlay 网络中,服务可以通过服务名访问:

1
redis:6379

3. Overlay 网络通信图

flowchart TB
    subgraph Node1[worker-1]
        A[app task]
    end

    subgraph Node2[worker-2]
        R[redis task]
    end

    A -->|redis:6379| R

    subgraph Overlay[Overlay Network app-net]
        A
        R
    end

4. 创建可被普通容器连接的 Overlay 网络

默认 overlay 网络用于 swarm service。
如果普通容器也要连接,需要 --attachable

1
2
3
4
docker network create \
--driver overlay \
--attachable \
app-net

然后普通容器可以:

1
2
3
docker run -it --rm \
--network app-net \
alpine sh

5. 加密 Overlay 网络

可以开启 overlay 网络加密:

1
2
3
4
docker network create \
--driver overlay \
--opt encrypted \
secure-net

加密会增加 CPU 开销。
是否开启要看安全需求和性能要求。

6. Ingress 网络和自定义 Overlay 网络

网络 作用
ingress Swarm 内置,用于 routing mesh 和发布端口
自定义 overlay 业务服务之间通信
docker_gwbridge 容器连接宿主机网络的桥接网络

业务服务之间建议使用自定义 overlay 网络,不要直接依赖默认 ingress 网络。


十二、服务发现和负载均衡

Swarm 内置 DNS 服务发现。

1. 通过服务名访问

创建网络:

1
docker network create --driver overlay app-net

创建 Redis:

1
2
3
4
docker service create \
--name redis \
--network app-net \
redis:7

创建应用:

1
2
3
4
5
docker service create \
--name app \
--network app-net \
--env REDIS_HOST=redis \
demo-app:1.0.0

应用中可以配置:

1
2
spring.data.redis.host=redis
spring.data.redis.port=6379

2. VIP 模式

默认 endpoint mode 是 VIP。

服务名会解析到一个虚拟 IP,Swarm 内部负载均衡再转发到具体 Task。

1
docker service inspect redis --pretty

创建 VIP 模式服务:

1
2
3
4
5
6
docker service create \
--name web \
--endpoint-mode vip \
--network app-net \
--replicas 3 \
nginx

3. DNSRR 模式

DNSRR 会让 DNS 查询返回多个 Task IP,由客户端自己负载均衡。

1
2
3
4
5
6
docker service create \
--name web \
--endpoint-mode dnsrr \
--network app-net \
--replicas 3 \
nginx

适合:

  • 客户端自己做负载均衡
  • 某些服务发现框架
  • 不想使用 VIP 负载均衡

注意:DNSRR 和 ingress 发布端口并不是所有组合都适合,需要根据实际服务设计。


十三、Secret:敏感配置管理

不要把密码写进镜像,也不要直接写在命令里。

错误示例:

1
2
3
4
docker service create \
--name mysql \
--env MYSQL_ROOT_PASSWORD=123456 \
mysql:8

更推荐使用 Secret。

1. 创建 Secret

1
echo "StrongPassword123!" | docker secret create mysql_root_password -

查看:

1
docker secret ls

2. 服务使用 Secret

1
2
3
4
5
docker service create \
--name mysql \
--secret mysql_root_password \
--env MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password \
mysql:8

Secret 默认挂载在:

1
/run/secrets/<secret_name>

3. 在应用服务中读取 Secret

1
2
3
4
docker service create \
--name app \
--secret db_password \
demo-app:1.0.0

容器内:

1
cat /run/secrets/db_password

应用可以读取该文件。

4. Secret 轮换

Swarm Secret 不支持直接修改内容。
通常做法是创建新版本:

1
echo "NewPassword456!" | docker secret create mysql_root_password_v2 -

更新服务:

1
2
3
4
docker service update \
--secret-rm mysql_root_password \
--secret-add source=mysql_root_password_v2,target=mysql_root_password \
mysql

确认服务正常后删除旧 Secret:

1
docker secret rm mysql_root_password

5. Secret 使用建议

  • Secret 用于密码、Token、私钥等敏感信息
  • 不要把 Secret 写进镜像
  • 不要把 Secret 放进 Git
  • Secret 命名建议带版本
  • 轮换时使用新 Secret 替换旧 Secret
  • 服务不用的 Secret 要及时移除

十四、Config:非敏感配置管理

Config 类似 Secret,但用于非敏感配置。

适合:

  • Nginx 配置
  • 应用配置模板
  • Prometheus 配置
  • Fluent Bit 配置
  • 静态配置文件

1. 创建 Config

1
docker config create nginx_conf ./nginx.conf

查看:

1
docker config ls

2. 服务使用 Config

1
2
3
4
5
docker service create \
--name nginx \
--config source=nginx_conf,target=/etc/nginx/nginx.conf \
--publish published=80,target=80 \
nginx

3. Config 和 Secret 的区别

对比项 Config Secret
用途 非敏感配置 敏感信息
是否加密存储 不按 Secret 语义处理 加密存储在 Raft 日志
默认挂载位置 /<config_name> /run/secrets/<secret_name>
示例 nginx.conf db_password

4. Config 轮换

和 Secret 一样,不直接修改内容,而是创建新版本:

1
docker config create nginx_conf_v2 ./nginx.conf

更新服务:

1
2
3
4
docker service update \
--config-rm nginx_conf \
--config-add source=nginx_conf_v2,target=/etc/nginx/nginx.conf \
nginx

十五、使用 Stack 部署应用

虽然这篇文章不是讲 Compose,但 Swarm 的 docker stack deploy 会使用 Compose 文件格式来描述一组服务。

1. Stack 是什么?

Stack 是一组 Service、Network、Volume、Config、Secret 的集合。

如果 docker service create 是一个一个创建服务,docker stack deploy 就是一次部署一组服务。

2. 一个简单 Stack 示例

stack.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
version: "3.8"

services:
web:
image: nginx:1.26
ports:
- target: 80
published: 8080
protocol: tcp
mode: ingress
networks:
- app-net
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
rollback_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.50"
memory: 256M
reservations:
memory: 128M

networks:
app-net:
driver: overlay

部署:

1
docker stack deploy -c stack.yml demo

查看 Stack:

1
2
3
docker stack ls
docker stack services demo
docker stack ps demo

删除 Stack:

1
docker stack rm demo

3. 注意 stack deploy 和 Compose 的差异

docker stack deploy 使用的是 Swarm Stack 部署能力,它不是本地 docker compose up 的完全替代。

常见差异:

  • build 通常不适合 stack 部署,生产应提前构建并推送镜像到 Registry
  • container_name 在 Swarm 中不适合使用
  • depends_on 不提供 Compose 本地编排那种启动顺序语义
  • deploy 字段在 Swarm 中生效,而在很多本地 Compose 场景下行为不同
  • Stack 部署要在 Manager 节点执行
  • 镜像必须能被所有节点拉取

4. 为什么生产要用私有镜像仓库?

如果你在 manager-1 本地 build 了镜像:

1
docker build -t demo-app:1.0.0 .

然后部署:

1
docker service create --name app demo-app:1.0.0

Task 可能被调度到 worker-1。
但 worker-1 上没有这个镜像,就会拉取失败。

生产流程应该是:

flowchart LR
    A[构建镜像] --> B[推送到 Harbor / Registry]
    B --> C[Swarm 部署 Service]
    C --> D[各 Worker 从 Registry 拉取镜像]

示例:

1
2
3
docker build -t harbor.example.com/backend/demo-app:1.0.0 .
docker push harbor.example.com/backend/demo-app:1.0.0
docker service update --image harbor.example.com/backend/demo-app:1.0.0 app

十六、部署 Spring Boot 到 Swarm

1. Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM eclipse-temurin:17-jre

RUN groupadd -r app && useradd -r -g app app

WORKDIR /app

COPY --chown=app:app target/app.jar app.jar

USER app

EXPOSE 8080

ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"

ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar app.jar"]

构建并推送:

1
2
docker build -t harbor.example.com/backend/demo-app:1.0.0 .
docker push harbor.example.com/backend/demo-app:1.0.0

2. 创建 Overlay 网络

1
2
3
docker network create \
--driver overlay \
app-net

3. 创建 Secret

1
echo "db-password" | docker secret create db_password -

4. 部署服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker service create \
--name demo-app \
--replicas 3 \
--network app-net \
--publish published=8080,target=8080 \
--secret db_password \
--env SPRING_PROFILES_ACTIVE=prod \
--env DB_PASSWORD_FILE=/run/secrets/db_password \
--limit-memory 768M \
--limit-cpu 1 \
--update-parallelism 1 \
--update-delay 10s \
--update-failure-action rollback \
harbor.example.com/backend/demo-app:1.0.0

5. 更新服务

1
2
3
docker service update \
--image harbor.example.com/backend/demo-app:1.0.1 \
demo-app

6. 查看状态

1
2
3
docker service ls
docker service ps demo-app
docker service logs -f demo-app

7. 回滚

1
docker service rollback demo-app

十七、有状态服务和数据卷的坑

Swarm 跑无状态服务很舒服,但跑有状态服务要谨慎。

1. 本地 Volume 的问题

比如:

1
2
3
4
docker service create \
--name mysql \
--mount type=volume,source=mysql-data,target=/var/lib/mysql \
mysql:8

这个 mysql-data 是每个节点本地的 volume。

如果 MySQL task 从 worker-1 漂移到 worker-2:

1
2
worker-1: mysql-data 有数据
worker-2: mysql-data 是新的空卷

结果可能是灾难。

2. 有状态服务的几种方案

方案 说明
固定节点运行 使用 placement constraint,把服务固定到某个节点
外部数据库 数据库不跑在 Swarm 里,使用云数据库或独立集群
分布式存储 使用 NFS、Ceph、GlusterFS、Longhorn 等
专业中间件集群 MySQL 主从、Redis Cluster、Kafka 集群等单独规划
Docker Volume Plugin 使用支持多节点存储的插件

3. 使用节点标签固定 MySQL

给节点打标签:

1
2
3
docker node update \
--label-add storage=mysql \
worker-1

部署 MySQL:

1
2
3
4
5
6
7
docker service create \
--name mysql \
--constraint node.labels.storage==mysql \
--mount type=volume,source=mysql-data,target=/var/lib/mysql \
--env MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password \
--secret mysql_root_password \
mysql:8

这可以避免 MySQL 随意漂移到其他节点。

但它不等于高可用。
worker-1 挂了,MySQL 还是不可用。

4. 推荐实践

生产环境建议:

  • Web/API/Worker 这类无状态服务跑 Swarm
  • MySQL/PostgreSQL/Redis/Kafka 等状态服务单独规划
  • 小团队可以用固定节点 + 强备份
  • 关键业务使用云数据库或专门的数据库集群
  • 不要以为 Swarm 自动迁移就等于数据自动迁移

容器会漂移,数据不会自己长腿跟上。别给数据安排它做不到的事情。


十八、节点标签和调度约束

1. 给节点打标签

1
2
3
4
5
6
7
8
9
10
11
docker node update \
--label-add region=shanghai \
worker-1

docker node update \
--label-add disk=ssd \
worker-1

docker node update \
--label-add app=true \
worker-2

查看:

1
docker node inspect worker-1 --pretty

2. 使用约束部署服务

1
2
3
4
5
docker service create \
--name api \
--replicas 3 \
--constraint node.labels.app==true \
harbor.example.com/backend/api:1.0.0

3. 多约束

1
2
3
4
5
6
docker service create \
--name order-service \
--replicas 2 \
--constraint node.labels.region==shanghai \
--constraint node.labels.disk==ssd \
harbor.example.com/backend/order-service:1.0.0

4. 排除某些节点

1
2
3
4
docker service create \
--name test-service \
--constraint node.labels.env!=prod \
nginx

5. Global 服务

Global 服务会在每个符合条件的节点上运行一个 Task。

适合:

  • 日志采集 Agent
  • 监控 Agent
  • 节点 Exporter
  • 安全扫描 Agent

示例:

1
2
3
4
5
docker service create \
--name node-exporter \
--mode global \
--publish published=9100,target=9100,mode=host \
prom/node-exporter

6. Replicated vs Global

模式 说明 场景
replicated 指定副本数 Web/API/Worker
global 每个节点一个 Agent/Exporter/日志采集

十九、节点维护:Drain、Pause、Active

节点有可用性状态:

状态 说明
Active 可以接收新 Task
Pause 不接收新 Task,但已有 Task 继续运行
Drain 不接收新 Task,并迁移已有 Task

1. Drain 节点

维护 worker-1 前:

1
docker node update --availability drain worker-1

Swarm 会把 worker-1 上的 Task 调度到其他 Active 节点。

查看:

1
docker service ps web

2. 恢复节点

1
docker node update --availability active worker-1

注意:恢复 Active 后,Swarm 不一定立刻把 Task 迁回该节点。
Swarm 追求最小扰动,不会为了“看起来平均”随便迁移正在跑的任务。

如果需要强制重新分布:

1
docker service update --force web

3. Pause 节点

1
docker node update --availability pause worker-1

Pause 不会迁移已有 Task,只是不再接新 Task。

4. 维护流程建议

flowchart TB
    A[准备维护节点] --> B[设置 Drain]
    B --> C[等待 Task 迁移完成]
    C --> D[确认服务副本正常]
    D --> E[维护系统/升级 Docker/重启机器]
    E --> F[节点恢复]
    F --> G[设置 Active]
    G --> H[观察集群状态]

二十、Swarm 安全机制

1. 节点间 mTLS

Swarm 内置 PKI。
节点加入 Swarm 时,Manager 会给节点签发证书。节点之间使用双向 TLS 认证和加密通信。

这比很多人想象的要完整。
Swarm 简单,不代表它在安全上裸奔。

2. Join Token

Swarm 有两类 token:

1
2
docker swarm join-token worker
docker swarm join-token manager

Worker token 用于加入 Worker。
Manager token 用于加入 Manager。

Manager token 权限更高,必须保护好。

3. 轮换 Token

1
2
docker swarm join-token --rotate worker
docker swarm join-token --rotate manager

如果 token 泄露,立即轮换。

4. 轮换 CA

如果 CA 或 Manager 被怀疑泄露:

1
docker swarm ca --rotate

5. Auto-lock

Swarm 的 Raft 日志包含敏感数据,比如 Secret。
可以开启 auto-lock,保护 Manager 本地加密密钥。

初始化时开启:

1
2
3
docker swarm init \
--advertise-addr 10.0.0.11 \
--autolock

已有 Swarm 开启:

1
docker swarm update --autolock=true

查看 unlock key:

1
docker swarm unlock-key

Manager 重启后需要解锁:

1
docker swarm unlock

轮换 unlock key:

1
docker swarm unlock-key --rotate

注意:unlock key 必须安全保存。
别开启 auto-lock 后自己把钥匙丢了,那就是给自己集群上了一把精致的锁,然后把钥匙扔进了海里。


二十一、Swarm 备份与恢复

1. 需要备份什么?

Manager 节点的 Swarm 状态保存在:

1
/var/lib/docker/swarm

这里包含:

  • Raft 日志
  • 集群状态
  • 网络对象
  • 服务对象
  • Secret/Config 元数据
  • 加密相关 key

2. 备份 Swarm 状态

建议停止 Docker 后备份,避免状态变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env bash
set -e

BACKUP_DIR="/opt/backups/swarm"
DATE="$(date +%Y%m%d_%H%M%S)"

mkdir -p "${BACKUP_DIR}"

echo "Stopping Docker..."
sudo systemctl stop docker

echo "Backing up /var/lib/docker/swarm..."
sudo tar czf "${BACKUP_DIR}/swarm-${DATE}.tar.gz" -C /var/lib/docker swarm

echo "Starting Docker..."
sudo systemctl start docker

echo "Backup created: ${BACKUP_DIR}/swarm-${DATE}.tar.gz"

如果开启了 auto-lock,记得保存 unlock key:

1
docker swarm unlock-key

3. 恢复 Swarm

恢复到新 Manager:

1
2
3
4
5
6
sudo systemctl stop docker

sudo rm -rf /var/lib/docker/swarm
sudo tar xzf swarm-20260607_120000.tar.gz -C /var/lib/docker

sudo systemctl start docker

如果需要强制形成新集群:

1
docker swarm init --force-new-cluster --advertise-addr 10.0.0.11

4. 丢失 Quorum 怎么办?

如果 Manager 多数派丢失,集群不能执行管理操作:

  • 不能创建服务
  • 不能更新服务
  • 不能扩缩容
  • 不能添加节点
  • 不能删除节点

但已有 Worker 上的任务可能继续运行。

恢复优先级:

  1. 尽量恢复原 Manager 节点。
  2. 如果无法恢复,在保留完整状态的 Manager 上执行 --force-new-cluster
  3. 重新加入 Manager 和 Worker。
  4. 检查服务状态。
  5. 重新建立备份策略。

命令:

1
docker swarm init --force-new-cluster --advertise-addr 10.0.0.11

二十二、监控与日志

Swarm 原生可以查看服务状态,但生产还需要监控系统。

1. 常用原生命令

查看节点:

1
docker node ls

查看服务:

1
docker service ls

查看任务:

1
docker service ps <service>

查看日志:

1
docker service logs -f <service>

查看节点详情:

1
docker node inspect <node> --pretty

查看服务详情:

1
docker service inspect <service> --pretty

2. 监控建议

常用组合:

  • Prometheus
  • Grafana
  • cAdvisor
  • Node Exporter
  • Alertmanager

Node Exporter 适合 global 模式:

1
2
3
4
5
docker service create \
--name node-exporter \
--mode global \
--publish published=9100,target=9100,mode=host \
prom/node-exporter

cAdvisor 示例:

1
2
3
4
5
6
7
8
9
docker service create \
--name cadvisor \
--mode global \
--publish published=8081,target=8080,mode=host \
--mount type=bind,src=/,dst=/rootfs,ro \
--mount type=bind,src=/var/run,dst=/var/run,ro \
--mount type=bind,src=/sys,dst=/sys,ro \
--mount type=bind,src=/var/lib/docker,dst=/var/lib/docker,ro \
gcr.io/cadvisor/cadvisor

注意:这些监控容器通常要挂载宿主机目录,权限较高,要控制访问。

3. 日志建议

Swarm 里的服务日志可以用:

1
docker service logs

但生产更推荐集中日志:

  • Loki + Promtail
  • ELK / EFK
  • Fluent Bit
  • Vector
  • Graylog

日志采集 Agent 适合 global 模式,每个节点一个。


二十三、Swarm 常见故障排查

1. 节点 Not Ready

查看节点:

1
docker node ls

排查:

1
2
systemctl status docker
journalctl -u docker -f

检查端口:

1
2
nc -zv manager-1 2377
nc -zv worker-1 7946

检查 UDP 端口需要用专门工具,例如 nc -utcpdump

2. 服务一直 Pending

查看:

1
docker service ps <service> --no-trunc

常见原因:

  • 节点资源不足
  • placement constraint 无节点满足
  • 镜像拉取失败
  • 私有仓库未登录
  • 端口冲突
  • 网络创建失败
  • Secret/Config 不存在

3. 镜像拉取失败

如果使用私有仓库,所有节点都要能拉取镜像。

每个节点登录:

1
docker login harbor.example.com

或者使用:

1
2
3
4
docker service create \
--with-registry-auth \
--name app \
harbor.example.com/backend/app:1.0.0

更新服务:

1
2
3
4
docker service update \
--with-registry-auth \
--image harbor.example.com/backend/app:1.0.1 \
app

4. 服务端口访问不了

检查:

1
2
3
docker service ls
docker service ps web
docker service inspect web --pretty

检查端口发布:

1
docker service inspect web --format '{{json .Endpoint.Ports}}'

检查防火墙:

1
2
sudo ufw status
sudo iptables -L -n

5. Overlay 网络不通

检查端口:

  • 7946 TCP/UDP
  • 4789 UDP

查看网络:

1
2
docker network ls
docker network inspect app-net

抓包:

1
sudo tcpdump -i any port 4789

6. Manager Unreachable

查看:

1
docker node ls

查看 Docker 日志:

1
journalctl -u docker -f

检查 Manager 之间网络:

1
2
ping manager-2
nc -zv manager-2 2377

如果 Manager 长期不可恢复:

1
2
docker node demote manager-2
docker node rm manager-2

注意保持 quorum。

7. 服务没有重新均衡

Swarm 不会为了平均而随便迁移正在运行的任务。
如果新增节点后想重新分布:

1
docker service update --force web

或滚动更新服务。


二十四、生产部署架构建议

1. 推荐架构

flowchart TB
    LB[外部负载均衡器 Nginx/HAProxy/云LB] --> N1[manager-1 / worker]
    LB --> N2[worker-1]
    LB --> N3[worker-2]

    subgraph Swarm[Docker Swarm Cluster]
        M1[manager-1]
        M2[manager-2]
        M3[manager-3]
        W1[worker-1]
        W2[worker-2]
        W3[worker-3]
        M1 <-->|Raft| M2
        M2 <-->|Raft| M3
        M1 <-->|Raft| M3
        W1 --> APP1[app task]
        W2 --> APP2[app task]
        W3 --> APP3[app task]
    end

    REG[Harbor / Registry] --> Swarm
    DB[(外部数据库)]
    APP1 --> DB
    APP2 --> DB
    APP3 --> DB

2. 节点规划建议

环境 Manager Worker 说明
测试 1 0-2 可以单节点
小生产 3 2+ 起步推荐
中等生产 3 或 5 多个 Manager 分散部署
高可用 5 多个 跨可用区部署

3. Manager 分布建议

3 Manager:

1
2
3
AZ-A: manager-1
AZ-B: manager-2
AZ-C: manager-3

5 Manager:

1
2
3
AZ-A: manager-1, manager-2
AZ-B: manager-3, manager-4
AZ-C: manager-5

4. 服务部署建议

  • 无状态服务使用 replicated
  • Agent 类服务使用 global
  • 重要服务设置资源限制
  • 服务设置 update/rollback 策略
  • 使用 overlay 网络隔离业务
  • 使用 Secret 管理敏感信息
  • 使用 Config 管理配置文件
  • 使用 Harbor/Registry 分发镜像
  • 数据库优先外置
  • 生产入口优先使用外部 LB + Swarm 服务

5. 命名规范建议

Service:

1
2
3
4
业务-模块-环境
order-api-prod
pay-worker-prod
gateway-prod

Network:

1
2
3
prod_backend_net
prod_frontend_net
monitor_net

Secret:

1
2
prod_mysql_password_v20260607
prod_redis_password_v20260607

Config:

1
2
nginx_conf_v20260607
prometheus_conf_v20260607

二十五、完整实战:部署一个 API 集群

假设我们有:

  • Spring Boot API
  • 镜像:harbor.example.com/backend/demo-api:1.0.0
  • 对外端口:8080
  • 副本数:3
  • 使用 Secret 读取数据库密码
  • 使用 Overlay 网络
  • 使用滚动更新和失败回滚

1. 创建网络

1
2
3
4
docker network create \
--driver overlay \
--attachable \
prod-net

2. 创建 Secret

1
echo "StrongDbPassword!" | docker secret create prod_db_password_v1 -

3. 部署服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker service create \
--name demo-api \
--replicas 3 \
--network prod-net \
--publish published=8080,target=8080,mode=ingress \
--secret source=prod_db_password_v1,target=db_password \
--env SPRING_PROFILES_ACTIVE=prod \
--env DB_PASSWORD_FILE=/run/secrets/db_password \
--limit-cpu 1 \
--limit-memory 768M \
--reserve-memory 256M \
--update-parallelism 1 \
--update-delay 15s \
--update-monitor 30s \
--update-failure-action rollback \
--restart-condition on-failure \
harbor.example.com/backend/demo-api:1.0.0

4. 查看状态

1
2
3
docker service ls
docker service ps demo-api
docker service logs -f demo-api

5. 扩容

1
docker service scale demo-api=5

6. 发布新版本

1
2
3
4
docker service update \
--with-registry-auth \
--image harbor.example.com/backend/demo-api:1.0.1 \
demo-api

7. 回滚

1
docker service rollback demo-api

8. 更新 Secret

1
echo "NewStrongDbPassword!" | docker secret create prod_db_password_v2 -
1
2
3
4
docker service update \
--secret-rm prod_db_password_v1 \
--secret-add source=prod_db_password_v2,target=db_password \
demo-api

确认正常后:

1
docker secret rm prod_db_password_v1

二十六、使用 Stack 部署完整应用

demo-stack.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
version: "3.8"

services:
api:
image: harbor.example.com/backend/demo-api:1.0.0
networks:
- prod-net
ports:
- target: 8080
published: 8080
protocol: tcp
mode: ingress
environment:
SPRING_PROFILES_ACTIVE: prod
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- source: db_password
target: db_password
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 15s
monitor: 30s
failure_action: rollback
rollback_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: "1.0"
memory: 768M
reservations:
memory: 256M
placement:
constraints:
- node.labels.app == true

worker:
image: harbor.example.com/backend/demo-worker:1.0.0
networks:
- prod-net
environment:
SPRING_PROFILES_ACTIVE: prod
deploy:
replicas: 2
restart_policy:
condition: on-failure
placement:
constraints:
- node.labels.worker == true

networks:
prod-net:
driver: overlay

secrets:
db_password:
external: true

创建 Secret:

1
echo "StrongDbPassword!" | docker secret create db_password -

给节点打标签:

1
2
3
docker node update --label-add app=true worker-1
docker node update --label-add app=true worker-2
docker node update --label-add worker=true worker-3

部署:

1
2
3
4
docker stack deploy \
--with-registry-auth \
-c demo-stack.yml \
demo

查看:

1
2
3
docker stack ls
docker stack services demo
docker stack ps demo

删除:

1
docker stack rm demo

二十七、Docker Swarm 常用命令清单

1. Swarm 集群

1
2
3
4
5
6
7
8
9
docker swarm init --advertise-addr <MANAGER-IP>
docker swarm join-token worker
docker swarm join-token manager
docker swarm join --token <TOKEN> <MANAGER-IP>:2377
docker swarm leave
docker swarm leave --force
docker swarm update --autolock=true
docker swarm unlock-key
docker swarm unlock

2. 节点管理

1
2
3
4
5
6
7
8
9
10
docker node ls
docker node inspect <node> --pretty
docker node promote <node>
docker node demote <node>
docker node update --availability drain <node>
docker node update --availability active <node>
docker node update --label-add key=value <node>
docker node update --label-rm key <node>
docker node rm <node>
docker node rm --force <node>

3. 服务管理

1
2
3
4
5
6
7
8
9
10
docker service create --name web --replicas 3 nginx
docker service ls
docker service ps web
docker service inspect web --pretty
docker service logs -f web
docker service scale web=5
docker service update --image nginx:1.26 web
docker service update --force web
docker service rollback web
docker service rm web

4. 网络管理

1
2
3
4
5
6
docker network create --driver overlay app-net
docker network create --driver overlay --attachable app-net
docker network create --driver overlay --opt encrypted secure-net
docker network ls
docker network inspect app-net
docker network rm app-net

5. Secret 管理

1
2
3
4
echo "password" | docker secret create db_password -
docker secret ls
docker secret inspect db_password
docker secret rm db_password

6. Config 管理

1
2
3
4
docker config create nginx_conf ./nginx.conf
docker config ls
docker config inspect nginx_conf
docker config rm nginx_conf

7. Stack 管理

1
2
3
4
5
6
docker stack deploy -c stack.yml demo
docker stack deploy --with-registry-auth -c stack.yml demo
docker stack ls
docker stack services demo
docker stack ps demo
docker stack rm demo

二十八、自动化脚本合集

1. 初始化 Manager

init-manager.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env bash
set -e

MANAGER_IP="$1"

if [ -z "$MANAGER_IP" ]; then
echo "Usage: ./init-manager.sh <manager-ip>"
exit 1
fi

docker swarm init --advertise-addr "$MANAGER_IP"

echo "Worker join command:"
docker swarm join-token worker

echo "Manager join command:"
docker swarm join-token manager

2. 打印 Join Token

show-tokens.sh

1
2
3
4
5
6
7
8
9
#!/usr/bin/env bash
set -e

echo "== Worker token =="
docker swarm join-token worker

echo
echo "== Manager token =="
docker swarm join-token manager

3. 节点巡检

swarm-check.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env bash
set -e

echo "== Docker info =="
docker info --format 'Swarm: {{.Swarm.LocalNodeState}}'

echo
echo "== Nodes =="
docker node ls

echo
echo "== Services =="
docker service ls

echo
echo "== Failed tasks =="
docker service ls --format '{{.Name}}' | while read svc; do
echo "-- $svc"
docker service ps "$svc" --filter desired-state=shutdown --no-trunc || true
done

4. 服务发布脚本

deploy-service.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env bash
set -e

SERVICE_NAME="$1"
IMAGE="$2"
REPLICAS="${3:-3}"
PUBLISHED_PORT="$4"
TARGET_PORT="$5"

if [ -z "$SERVICE_NAME" ] || [ -z "$IMAGE" ] || [ -z "$PUBLISHED_PORT" ] || [ -z "$TARGET_PORT" ]; then
echo "Usage: ./deploy-service.sh <service-name> <image> <replicas> <published-port> <target-port>"
exit 1
fi

if docker service inspect "$SERVICE_NAME" >/dev/null 2>&1; then
echo "Updating service ${SERVICE_NAME}..."
docker service update \
--with-registry-auth \
--image "$IMAGE" \
"$SERVICE_NAME"
else
echo "Creating service ${SERVICE_NAME}..."
docker service create \
--name "$SERVICE_NAME" \
--replicas "$REPLICAS" \
--publish published="$PUBLISHED_PORT",target="$TARGET_PORT",mode=ingress \
--update-parallelism 1 \
--update-delay 10s \
--update-failure-action rollback \
"$IMAGE"
fi

docker service ps "$SERVICE_NAME"

5. Swarm 备份脚本

backup-swarm.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env bash
set -e

BACKUP_DIR="/opt/backups/swarm"
DATE="$(date +%Y%m%d_%H%M%S)"
BACKUP_FILE="${BACKUP_DIR}/swarm-${DATE}.tar.gz"

mkdir -p "$BACKUP_DIR"

echo "Warning: Docker will be stopped during backup."
sleep 3

sudo systemctl stop docker
sudo tar czf "$BACKUP_FILE" -C /var/lib/docker swarm
sudo systemctl start docker

echo "Backup created: $BACKUP_FILE"

6. 节点维护脚本

drain-node.sh

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash
set -e

NODE="$1"

if [ -z "$NODE" ]; then
echo "Usage: ./drain-node.sh <node>"
exit 1
fi

docker node update --availability drain "$NODE"
docker node inspect "$NODE" --pretty

active-node.sh

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash
set -e

NODE="$1"

if [ -z "$NODE" ]; then
echo "Usage: ./active-node.sh <node>"
exit 1
fi

docker node update --availability active "$NODE"
docker node inspect "$NODE" --pretty

二十九、Docker Swarm 和 Kubernetes 对比

1. 对比表

对比项 Docker Swarm Kubernetes
上手难度
与 Docker CLI 集成 原生 间接
单机到集群迁移 简单 复杂一些
网络模型 Overlay + Routing Mesh CNI
服务发现 内置 DNS/VIP Service/CoreDNS
配置管理 Config/Secret ConfigMap/Secret
滚动更新 支持 支持且更强
自动伸缩
生态 较小 极强
Operator 基本没有 丰富
多租户治理
学习成本
适合场景 中小集群、简单编排 大规模云原生平台

2. 什么时候选 Swarm?

选择 Swarm,如果你:

  • 已经熟悉 Docker
  • 服务规模不大
  • 希望快速搭一个可用集群
  • 不想引入 K8s 学习和维护成本
  • 主要跑无状态服务
  • 团队没有专职平台工程能力
  • 更重视简单、直接、够用

3. 什么时候选 Kubernetes?

选择 Kubernetes,如果你:

  • 有复杂微服务治理需求
  • 有大规模集群
  • 要使用云原生生态
  • 需要 HPA、Operator、CRD
  • 需要复杂网络和存储插件
  • 需要完善多租户和策略治理
  • 团队具备 K8s 运维能力

4. 一句实话

很多团队不是需要 Kubernetes,而是想显得自己需要 Kubernetes。
如果只是几个 Spring Boot 服务、一个 Redis、一个 MySQL、一个 Nginx,上来就 K8s,可能还没服务出问题,集群自己先开始教育你。

Swarm 的价值在于:

它把多机容器编排做到足够简单。

但如果你的系统已经复杂到 Swarm 不够用,就不要硬撑,该上 Kubernetes 就上 Kubernetes。


三十、生产实践检查清单

1. 集群规划

  • Manager 使用固定 IP
  • Manager 数量为 3 或 5
  • Manager 分布在不同可用区或不同物理机
  • Worker 资源足够
  • 防火墙开放 2377、7946、4789
  • 业务端口按需开放
  • 节点时间同步正常

2. 安全

  • 保护 manager join token
  • 定期轮换 join token
  • 使用 Secret 管理敏感数据
  • 不把密码写进镜像和环境变量
  • 关键集群开启 auto-lock
  • 私有 Registry 使用 HTTPS
  • 所有节点使用可信镜像仓库
  • 限制 Docker Socket 访问

3. 服务部署

  • 使用私有镜像仓库
  • 镜像 tag 明确,不滥用 latest
  • 配置 update 和 rollback 策略
  • 配置资源限制
  • 无状态服务多副本
  • 有状态服务固定节点或外置
  • 使用节点 label 管理调度
  • 使用 overlay 网络隔离服务

4. 运维

  • 配置集中日志
  • 配置监控和告警
  • 定期备份 /var/lib/docker/swarm
  • 保存 auto-lock unlock key
  • 定期演练恢复
  • 节点维护前先 Drain
  • 变更前确认 quorum
  • 记录服务部署配置

5. 排错

  • 会使用 docker node ls
  • 会使用 docker service ps --no-trunc
  • 会使用 docker service logs
  • 会检查 overlay 网络端口
  • 会处理镜像拉取失败
  • 会排查 placement constraint
  • 会恢复 quorum

总结

Docker Swarm 是 Docker 原生的集群编排能力。

它的核心不是“多台机器跑容器”这么简单,而是围绕 Service 的期望状态进行调度和维护:

  • 你声明服务应该是什么状态
  • Manager 把状态写入 Raft
  • Scheduler 把 Task 分配到节点
  • Worker 运行容器
  • 节点故障后重新调度
  • 服务更新时滚动替换
  • 入口流量通过 Routing Mesh 转发
  • 服务之间通过 Overlay 网络通信
  • Secret 和 Config 由 Swarm 管理

Swarm 的优点很明确:

  1. 简单。
  2. Docker 原生。
  3. CLI 统一。
  4. 上手快。
  5. 适合中小规模。
  6. 比 Compose 更适合多机。
  7. 比 Kubernetes 更轻量。

Swarm 的短板也要诚实面对:

  1. 生态没有 Kubernetes 丰富。
  2. 自动伸缩能力弱。
  3. 存储编排能力有限。
  4. 复杂服务治理能力有限。
  5. 大规模平台化不如 Kubernetes。

所以最合理的态度是:

小规模别强上 Kubernetes,大规模别硬撑 Swarm。

如果你要的是简单、稳定、Docker 原生、多机编排,Docker Swarm 依然是一个很实用的选择。
如果你已经需要复杂云原生生态、Operator、多租户、自动伸缩和精细化策略,那么 Kubernetes 才是更长期的路线。

最后记住三句话:

  1. Manager 保持奇数,Quorum 是 Swarm 的命根子。
  2. 无状态服务适合 Swarm,有状态服务要认真规划存储。
  3. Swarm 很简单,但生产环境的网络、安全、备份、监控一点都不能简单。

参考资料


Docker Swarm 深度实践:从集群搭建、服务编排到生产级运维
https://allendericdalexander.github.io/2026/06/07/devops/management/docker-swarm-cluster-deep-dive-blog/
作者
AtLuoFu
发布于
2026年6月7日
许可协议