1. 为什么要写这篇 Swarm 详解说实话在云原生时代聊容器编排很多人第一反应就是 Kubernetes。但我在实际工作中发现有相当大一部分场景根本用不着上 K8s——维护成本高、学习曲线陡、基础设施要求也不低。一个三五台机器的项目、一个内部系统、或者一个刚起步的团队需要的往往是一个能把 Docker 容器管起来、能多机部署、能滚动更新、最好还不用额外装一堆组件的东西。这就是 Docker Swarm 的定位。Docker Swarm 是 Docker 原生自带的容器编排工具把自己所在的 Docker 引擎集群变成一个统一调度平台。你不需要额外安装二进制、不需要搞一整套证书体系、更不需要维护 etcd 集群只要服务器上装好了 Docker一条命令就能初始化一个集群另一条命令就能把别的机器拉进来。这种零额外依赖的特性让它在中小规模场景下显得特别轻巧。这篇详解文档面向谁呢一类是刚接触容器编排、只在单机玩过 docker run 和 docker-compose想跨到多机部署的开发者另一类是已经在用 Swarm 但只停留在能用层面想搞清楚内部原理、遇到问题能自己排查的运维同学。我会从核心概念讲到架构原理再花大篇幅讲实操步骤和排错经验最后聊聊它和 Kubernetes 的选型边界。这不是官方文档的翻译而是我在真实项目里跑了一两年 Swarm 之后沉淀下来的理解尽量把为什么这样做和踩过哪些坑都说清楚。2. 核心概念和设计思路拆解2.1 节点、服务、任务这三个词到底是什么意思理解 Swarm 的关键是先建立一套自己的心智模型。官方文档里反复出现 node、service、task很多人看一遍就忘因为这几个词太抽象了。我换个说法。节点Node就是一台加入了 Swarm 集群的 Docker 主机。节点分两种角色管理节点Manager和工作节点Worker。管理节点负责集群的管理和控制比如下发指令、调度任务、维护集群状态工作节点负责实际运行容器。你可以在初始化集群时让一台机器同时当管理节点和工作节点也可以只让它当管理节点。服务Service是一个服务的期望状态定义。你说我需要三个 Nginx 副本这就定义了一个服务。Swarm 会保证集群里始终有三个 Nginx 容器在运行任何一个挂了它都会自动拉起新的。任务Task是服务的实际执行单元。一个任务对应一个容器Swarm 调度器把任务分配到具体的节点上运行。你可以把服务理解成一张需求清单任务是这张清单被逐项执行后产生的具体工作项。打个比方服务是餐厅菜单上的宫保鸡丁任务则是后厨实际炒出来的那一盘。你点了三份副本数3厨房就需要炒三盘每盘对应一个任务。某个厨师工作节点做砸了一盘餐厅不会让你干等着而是立刻让另一个厨师重新炒一盘保证端上桌的始终是三道菜。2.2 为什么 Swarm 适合中轻量场景而不是大型平台说到容器编排绕不开的问题是为什么不用 K8s。我从实际运维体验出发说说 Swarm 适合什么、不适合什么。Swarm 最核心的优势在于简单直接。初始化集群只要docker swarm init一行命令加节点只要docker swarm join一行命令部署服务用docker service create更新用docker service update。没有额外的组件、没有复杂的证书配置、没有一堆 CRD 要学习。如果你的基础设施只有三四台机器跑十几个到几十个容器Swarm 完全可以胜任而且维护成本极低。但它的劣势也很明显。Swarm 没有像 K8s 那样完整的生态没有内置的 HPAHorizontal Pod Autoscaler做基于 CPU 的自动伸缩没有 Namespace 级别的隔离没有完善的 RBAC 权限模型也没有 StatefulSet 这类专门处理有状态应用的抽象。虽然可以通过外部工具补齐一部分能力但如果你需要在这套编排系统上构建一个大平台K8s 才是更合适的选择。我的使用经验是先判断业务规模和团队维护能力再决定用不用 Swarm而不是一上来就为了主流上 K8s。如果团队只有一两个人需要快速把服务跑起来Swarm 是性价比极高的方案。等业务真的到了几十个节点、上百个服务的规模再迁移到 K8s 也不迟——因为服务编排的抽象逻辑是相通的迁移成本可控。2.3 声明式模型和自愈能力的底层逻辑Swarm 坚持的是声明式编排理念这跟 K8s 一脉相承。你不需要告诉 Swarm把那个容器停掉再启动一个新的你只需要声明我要三个副本剩下的事情它自己做。这个逻辑的背后是期望状态和实际状态的持续调和。Swarm 管理节点会持续监控集群中所有服务的实际状态和目标状态做对比。发现差异比如一个容器崩了、一个节点掉线了就触发协调动作通过调度器重新分配任务让实际状态向期望状态收敛。我在项目里最直观的体验是一个后端服务因为内存溢出被内核杀了Swarm 在几秒钟内就自动在一个健康节点上重新拉起了任务容器。整个过程我什么都没做只看到日志里有一条task re-scheduled之类的记录。这个自愈能力正是编排工具比单纯写脚本强的地方。你用 crontab 或者 systemd 也能做守护但做不了跨节点的调度和状态收敛而 Swarm 把这些变成了内置能力。3. 关键机制原理解读3.1 Raft 共识算法如何保证管理节点高可用Swarm 集群的大脑是管理节点管理节点之间靠 Raft 共识算法来保证数据一致性和高可用。这一点很多文档一笔带过但我觉得值得展开。Raft 是分布式系统里常用的共识算法核心目标是让多个节点在同一个数据状态上达成一致。Swarm 用 Raft 来存储集群状态包括服务的定义、节点的状态、任务分配信息等等。当一个管理节点收到一条指令比如创建服务它不会直接执行而是先把这条指令作为一个日志条目写入本地然后广播给其他管理节点。只有超过半数quorum的管理节点确认写入了这条日志它才会正式生效。这里有一个非常关键的实操含义如果管理节点数量少于半数存活集群会进入只读状态无法再接受变更操作。比如你有三个管理节点其中两个宕机了剩一个也没法和别人达成共识集群没法创建新服务、没法更新服务、没法增加节点。这也是为什么官方建议生产环境至少部署三个管理节点而且最好是奇数——三个的话能容忍坏一个五个的话能容忍坏两个四个反而和三个一样只能容忍坏一个多一个节点没有带来额外可用性还多了同步开销。提示管理节点数量没必要太多。超过七个管理节点并不会显著增强可用性反而会增大 Raft 同步开销和故障域。一般三到五个管理节点就足够了其余机器都用工作节点角色。3.2 Overlay 网络和数据平面的通信路径Swarm 集群里的跨主机通信靠的是 Overlay 网络。每个服务可以接入一个或多个 Overlay 网络容器在不同主机上运行时通过 Overlay 网络就像在同一个二层网络里一样互通。这个能力由 Docker 原生的网络驱动来实现。Overlay 网络的实现原理基于 VXLAN 隧道技术。当容器 A在主机 X 上要访问容器 B在主机 Y 上时数据包先进入 A 所在的 Overlay 网络Docker 引擎把原始数据包封装成一个新的 UDP 包通过主机之间的物理网络送到主机 Y再由主机 Y 的 Docker 引擎解封装还原原始数据包送到容器 B。这个封装和解封装的过程对容器来说是完全透明的。我在实际部署中体会到最有用的一点是服务可以通过服务名直接访问而不需要关心容器 IP。Swarm 内置了基于 DNS 的服务发现你创建了一个叫api的服务网络内的其他容器直接访问api:8080就能路由到对应的任务。这个能力极大简化了微服务之间的连接配置不用维护一堆环境变量来拼 IP。Overlay 网络还有内建的负载均衡功能。一个服务有三个副本别的服务通过服务名访问时内置 DNS 会返回多个任务 IP同时 VIP虚拟 IP机制会做四层负载均衡把请求分散到各个任务上。对应用来说看到的始终是一个固定的服务 IP 和端口。3.3 安全传输层和节点加解密机制Swarm 集群有一个内置的 CA证书颁发机构。每个节点加入集群时都会申请一份由集群 CA 签发的客户端证书用于节点之间通信的双向 TLS 认证。管理节点之间同步 Raft 日志用的是加密通道工作节点和调度器之间的控制通信也是加密的。这些证书还有自动轮换机制到期前会自动续期。整个过程中你不需要做任何证书管理操作。唯一需要手动保护的是初始化集群时那串 join token它相当于集群的邀请码。拿到管理节点 token 的人可以把机器加入集群变成管理节点这是安全上的第一道闸门。所以我一直建议把 token 妥善保管不要随便发给别人也不要写进公开的脚本仓库里。注意如果有节点证书过期或者节点被重新加入集群时出现 TLS 握手错误最常见的原因是时钟不同步。容器编排对时间同步的要求非常高Raft 日志时间戳、TLS 证书有效期都依赖正确的时间。给所有节点配置好 NTP 时间同步是基本功。4. 从零部署一套 Swarm 集群4.1 环境准备和初始化这里我以三台 Linux 服务器为例演示怎么搭一个最小高可用集群。三台都装好 Docker Engine版本尽量一致我测试时用的版本是 24.x。第一步在三台机器上都开启 Docker 服务并设置开机自启systemctl enable docker --now第二步挑一台机器作为第一个管理节点执行初始化命令。这里最关键的一个参数是--advertise-addr要填其他节点能访问到的本机 IP。如果服务器有多个网卡不指定这个参数可能导致集群通信失败。docker swarm init --advertise-addr 192.168.1.10执行成功后终端会输出两条 join 命令——一条是加入管理节点的一条是加入工作节点的。这两条命令里包含了 join token要保存好。第三步在另外两台机器上执行管理节点 join 命令docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377加入后回到第一台机器查看节点状态docker node ls正常情况下可以看到三个节点的 STATUS 都是 ReadyMANAGER STATUS 那一列第一台是 Leader另外两台是 Reachable。到这里一个三管理节点的高可用 Swarm 集群就建好了。4.2 创建服务和调整副本数集群建好之后先做一个最简单的测试创建一个 Nginx 服务docker service create --name web --replicas 2 --publish 80:80 nginx:latest这条命令的意思是创建一个名叫 web 的服务副本数为 2把宿主机 80 端口映射到容器 80 端口。执行后可以用docker service ls查看服务状态用docker service ps web查看每个任务的分配情况能看到任务分别被调度到了哪些节点上。如果下面想测试自愈能力可以找到运行 web 任务的那个节点手动把这个容器停掉docker stop $(docker ps -q --filter nameweb)Swarm 会在十几秒内自动重新拉起一个任务保证副本数恢复到 2。这是你第一次直观感受到声明式编排的力量。扩容和缩容同样简单不需要重新创建服务直接更新副本数即可docker service scale web5 docker service scale web2扩容时 Swarm 会尽量把新任务分散到不同节点避免所有副本挤在一台机器上。通过docker service ps web能看到任务的节点分布情况。4.3 服务更新和回滚Swarm 支持滚动更新这是发布新版本时我用到最多的功能之一。比如要把 Nginx 镜像从 1.26 升级到 1.27执行docker service update --image nginx:1.27 --update-parallelism 1 --update-delay 10s web--update-parallelism 1表示同时只更新一个副本--update-delay 10s表示每个副本更新之间间隔 10 秒。这种滚一个停一个的策略能最大程度减少发布对线上流量的影响。如果新版本有问题回滚命令非常直接docker service rollback webSwarm 会把服务恢复到上一次变更前的配置包括镜像版本、环境变量、挂载卷等全部参数。这个能力在紧急事故处理时效果极佳——不需要手动改配置再重新部署一条命令全部还原。实操心得我上线一个新版本前一定会先执行一次docker service update --env-add DEBUGfalse web这种空变更来确认当前服务定义无误再去改镜像版本。因为 Swarm 对服务定义中的配置项是非常敏感的改动一个环境变量就可能触发全部任务滚动重启提前确认能避免误操作。5. 网络、存储和服务发现细节5.1 创建自定义 Overlay 网络默认的 ingress 网络用于实现服务发布时的外部访问。但如果你需要在服务之间做内部通信建议创建一个自定义 Overlay 网络docker network create -d overlay --attachable backend创建后在启动服务时指定加入这个网络docker service create --name api --network backend --replicas 2 api:v1 docker service create --name db --network backend db:v1这样 api 和 db 之间就能通过服务名互相访问了比如 api 里直接访问http://db。之所以推荐自定义网络一是便于隔离不同的服务组二是在多网络场景下可以精细控制哪些服务之间能通信。--attachable这个参数值得说一下。默认 Overlay 网络只允许 swarm 服务接入不允许普通容器比如临时调试用的docker run容器接入。加了--attachable之后普通容器也能连上这个网络调试联调非常方便。5.2 数据持久化的正确打开方式Swarm 任务重建时容器会被销毁容器内写的数据自然也会丢失。要持久化数据有几种方案。最常用的是bind mount把宿主机目录直接挂进容器docker service create \ --name app \ --mount typebind,source/data/app,target/app/data \ app:v1这种方式简单直接但有一个隐患如果副本数多于 1多个容器会同时读写同一个宿主机目录可能产生数据竞争。所以 bind mount 一般配合单副本服务使用或者配合 global mode每个节点固定跑一个副本。另一种方案是volume。Docker 的本地 volume 在单节点上能正常保存数据但 Swarm 默认不会把 volume 数据复制到其他节点。如果任务被调度到了另一台机器数据就不在了。这个场景需要引入外部存储插件比如 NFS 或者云厂商的块存储。我只在实际项目里用过 NFS 方案把 NFS 共享目录 mount 到每台节点上再以 bind mount 的方式挂进容器效果很稳定。注意对有状态应用比如数据库Swarm 不是理想的选择。Swarm 的任务调度本质上假设容器可以随时在其他节点重建这对无状态服务非常友好对有状态服务则意味着数据必须全部走外部存储架构上要提前设计好。如果不知道如何设计我建议这类应用还是单独部署Swarm 只跑无状态的中间层。5.3 Routing Mesh外部流量如何进入集群Swarm 的 Routing Mesh 机制让外部流量可以到达集群中的任意节点即使该节点上并没有运行目标任务。你发布服务时指定了--publish 80:80外部请求打到集群里任何一台机器的 80 端口都会被转发到实际运行该服务的任务节点上。这带来了两个好处。第一你不需要在集群前面单独部署负载均衡器每台节点的 80 端口都对外可用。第二即使某个运行任务副本的节点宕机了请求打到其他节点依然能路由过去。Routing Mesh 的底层还是依托 ingress Overlay 网络来做的。Swarm 为每个发布的服务在 ingress 网络上创建一个 VIP外部流量进入节点后由 IPVS 规则把流量转发给对应服务的后端任务。6. Swarm 和 Kubernetes 怎么选6.1 从运维成本角度做对比先说结论如果你的需求是管理二三十个容器、五六台机器、团队没有专职运维Swarm 是更务实的方案。K8s 的部署和维护门槛明显更高。即使现在有各种发行版帮你省略了很多步骤但你要处理的问题依然比 Swarm 多得多节点组件的更新、证书轮换、CNI 网络插件选型、监控体系搭建、RBAC 配置、存储类管理……每一样都需要学习成本和维护成本。而 Swarm 把这套复杂度全藏在 Docker 引擎里你用到的只是几条 CLI 命令。我把两者用一张表做个对比方便快速决策对比维度Docker SwarmKubernetes安装部署Docker 内置一条命令初始化需要额外安装整套组件学习成本低懂 Docker 基础即可上手高概念多、配置复杂自动伸缩手动 scale无内置 HPA支持基于指标的 HPA有状态应用支持程度有限数据外置StatefulSet PV/PVC 更成熟RBAC 权限基本没有完善生态扩展较少极丰富Operator、CRD 等升级维护跟随 Docker 引擎升级组件多升级链路复杂故障排查简单Docker 原生日志排查链路长组件多6.2 什么业务场景该用 Swarm从我接触过的项目看以下几种场景用 Swarm 非常合适。一是中小规模的内部系统和工具平台。比如公司内部的 Wiki、任务管理系统、日志查询平台流量不大但需要高可用和方便部署。Swarm 完全能承担。二是边缘端的容器管理。边缘场景通常机器数量有限、带宽不稳定、网络环境复杂K8s 太重Swarm 自带 store/raft 的轻量逻辑反而很契合。三是CI/CD 流水线中的临时执行环境。我在项目里用 Swarm 跑了一批构建任务容器动态创建一个服务执行构建完成后把服务删掉。这个过程如果用 K8s 做要配置 Job、配置 RBAC、配置存储性价比明显不高。四是作为学习和入门容器编排的跳板。先通过 Swarm 理解什么是节点、什么是调度、什么是滚动更新再去学 K8s概念迁移成本会低很多。很多人一上来啃 K8s 被概念砸晕其实先玩半个月 Swarm 再开始会顺畅很多。6.3 什么时候该迁到 K8sSwarm 有两个明显的天花板一是大规模服务治理当服务数量到几百上千Swarm 的信息查询和调度能力就显得粗糙二是需要精细的资源控制和自动伸缩如果业务流量有明显波峰波谷需要基于 CPU 或自定义指标来自动扩缩容K8s 的 HPA 是标配能力Swarm 需要额外写脚本非常别扭。另外如果公司已经在用 Prometheus Grafana 做监控很多现成的 K8s 监控方案能用但 Swarm 的监控指标通常更薄需要自己拼装。团队如果打算长期做云原生基础设施K8s 显然更有长期价值。我的建议是不要为了迁移而迁移。一个跑得好好的 Swarm 集群如果当前业务没有遇到明确的瓶颈继续用它没有任何问题。但如果你明确感知到以下信号——服务数量大、需要细粒度权限隔离、需要自动伸缩、需要对接云平台生态就可以着手规划迁移。迁移前先小规模试点一个非核心服务把服务编排定义迁移到 K8s 的 Deployment跑通了再逐步扩大。7. 常见问题排查速查表7.1 单点异常和节点状态不是说改就改现象节点显示 Down先确认网络连通性用ping检查节点间的物理网络。再检查 Docker 服务是否在运行systemctl status docker。最后确认时钟同步。这几个检查项按顺序排查大部分 Down 状态都能定位。现象服务副本数一直小于期望值用docker service ps 服务名看任务的最细粒度状态。常见原因是镜像拉取失败比如镜像私有仓库认证过期或者资源不足内存或 CPU 不满足服务预留值。这两种问题都会持续卡在 Precondition 或者 Pending 状态。现象更新服务后所有副本都被重启Swarm 把服务定义的任何字段变化都视为一次更新。如果你改了环境变量、挂载卷、资源限制等字段都会触发滚动更新。如果你只是想加一个标签而不想重启服务考虑用docker service update --label-add的标签字段区分一下——但说实话标签也是会触发排重的。所以这个现象不是 bug是设计如此。7.2 网络和负载均衡问题现象A 服务访问不到 B 服务先确认两个服务在同一个 Overlay 网络里。这个检查很基础但经常被忽略。再确认 B 服务的端口监听是否正常然后在 A 服务内部用 DNS 工具解析 B 的服务名确认返回的 VIP 地址是否正常。现象外部访问服务不通排查顺序是先检查服务是否发布了正确的端口docker service inspect 服务名看 PublishedPort再确认请求打到的节点 80 端口是否被防火墙挡了最后用 Routing Mesh 的特性换另一台节点试一下排除单节点异常。现象发布端口和目标端口搞混--publish 8080:80的前后顺序非常关键。前面是宿主机端口后面是容器端口。我在项目里踩过这个坑——写反了之后外部访问 80 端口映射到容器 8080结果服务直接不可达排查了很长时间才发现是端口映射写反了。7.3 数据和安全问题现象容器数据丢失先确认你挂载的是 bind mount 还是 volume。bind mount 只要宿主机目录还在数据还在volume 数据是 Docker 管理的如果任务被调度到了另一个节点新节点上没有数据。解决方案就是提前规划好共享存储。现象join token 丢了可以用docker swarm join-token manager或者docker swarm join-token worker重新获取。token 会重新生成注意旧 token 会失效所以重新获取后要更新你的自动化脚本。我建议把 token 存到一个安全的密码管理工具里不要放在团队共享文档的正文里。7.4 我的排查方法论总结在 Swarm 上排查问题我一般遵循这样的顺序先看集群状态再看服务定义然后看任务状态最后看容器日志。集群状态查docker node ls服务定义查docker service inspect任务状态查docker service ps容器日志查docker service logs。很多问题其实在服务定义里就能发现线索。比如资源限制写得太低导致容器频繁 OOM 被重启这时docker service ps会显示task repeated on node之类的记录但docker inspect是不容易看到的。所以别跳过逐层排查的路径很多时候快速定位问题的关键是收集足够多的信息而不是猜。8. 实际项目中的使用经验和建议我在一个模拟的中小型项目中用了 Swarm 做整套业务后端的编排。大概五台机器的集群跑着十几个服务包括网关、用户服务、订单服务、消息队列消费端外加定时任务。整体跑了一年多稳定性让我印象很深最长时间无故障运行超过 200 天。中间经历了多次滚动发布、节点下线维护、单机故障自愈真正需要我人工干预的次数非常少。有几个经验想特别分享。第一把服务定义做成声明式配置文件而不是靠命令行一点一点敲。虽然docker service create能完成所有配置但我更推荐用 Compose 文件来定义服务然后通过docker stack deploy部署。好处是配置可版本化、可追溯团队其他人看代码就能知道服务跑在什么状态下。官方虽然一直鼓励 service 命令但在多服务场景下docker stack 的效率高得多。第二监控一定要提前搭。Swarm 本身没有内置监控面板但可以非常轻量地用 cAdvisor Prometheus Grafana 这套组合来采集节点和容器的指标。监控的意义在于提前发现问题而不是事后排查。我在没有监控的早期阶段经常是用户反馈服务出问题了才反应过来搭好监控之后内存持续上涨的趋势在爆炸前就能看到提前扩容就能避免故障。第三日志集中化是刚需。Swarm 容器的日志默认是分散在各节点上的排查问题要一台一台机器去找日志效率很低。我后来部署了轻量的日志采集组件所有容器的标准输出统一收集到中央存储里配合关键字检索排查问题的时间从小时级降到了分钟级。如果团队预算紧张至少也要给每台节点装一个简单的日志采集脚本用docker service logs --since配合节点筛选也能临时用一用。第四不要把所有节点都设成管理节点。我见过有人图方便把五台全设成管理节点想着反正都能管。但实际上管理节点承载了 Raft 共识的通信压力管理节点过多会拖慢所有配置变更的速度。正确的做法是管理节点保证奇数且够用就好剩下的全部设成工作节点把管理和执行的角色分开。关于 Swarm 的未来说实话容器编排领域 K8s 已经是事实标准但这并不影响 Swarm 在特定场景下的价值。如果你面对的是一套不需要极致扩展、团队也养不起 K8s 专职运维人员的基础设施Swarm 这个原生的、极简的编排方案依然是很好的选择。技术选型从来没有最好的只有最合适的。用合适的技术解决合适的问题比追逐主流更重要。