☰
Docker Swarm架构核心:分清引擎Mode与服务部署Mode
2026/10/2 9:12:12 网站建设 项目流程

聊到Docker Swarm,很多人第一反应是docker service、docker stack这些命令,但真正想把Swarm架构理顺,绕不开的其实是Mode这个词。Mode在Swarm体系里同时肩负着两个层面的含义:引擎层面的运行模式,和服务层面的部署模式。不把它们掰清楚,后面排查问题时很容易被报错带偏。这篇是Swarm架构系列的第一篇,先把Mode这块地基打牢。文章面向正在把业务从单机Docker迁往集群、又不想一上来就上Kubernetes的团队,也适合刚接触容器编排、被各种术语绕晕的新手。我会从两个Mode的区别、架构设计的动机、实际部署操作,到常见坑位排查,一次讲透。

1. Mode到底是什么:两个容易混淆的层面

1.1 引擎层面的Swarm Mode

从Docker 1.12开始,Docker引擎把完整的Swarm编排能力直接内置进了dockerd进程。这里的“Mode”指的是Docker引擎当前处于哪一种运行状态:是单机模式,还是集群模式。

启动集群模式只需要一条命令:

docker swarm init

执行之后,当前节点会被初始化为管理节点,引擎开始监听2377端口用于集群管理通信,同时创建两个新对象:swarm集群本身,以及一系列集群级别的网络和服务发现机制。想看当前引擎是否处于Swarm模式,可以用:

docker info | grep -i swarm

输出里会出现Swarm: active或Swarm: inactive。这就是引擎层面的Mode开关。想退出的话:

docker swarm leave --force

注意--force参数不能省,管理节点退出时必须加,worker节点不加也能退。

打个比方,这就像手机上的飞行模式。开关本身不决定你用什么App,但决定你能不能接入基站网络。Swarm Mode的开关决定的是你这台Docker引擎能不能参与集群调度、能不能加入覆盖网络,而不是决定你部署的服务长什么样。

1.2 服务部署层面的Deployment Mode

服务部署层面的Mode,指的是docker service create时的--mode参数,它只有两个取值:replicated和global。

replicated模式是你指定期望的副本数(--replicas N),调度器负责在集群里挑N个节点各跑一个任务。哪个节点跑、不跑,由调度算法决定。任务挂了,控制器会自动在其他可用节点上补充,直到达到期望副本数。

global模式则完全不一样,它忽略副本数,调度器会保证集群里每个可用节点上都运行且只运行一个任务。节点数变了,任务数自动跟着变。新节点加入集群,控制器会自动在该节点上启动一个global任务,不需要任何人工干预。

这两个模式在命令上的体现如下:

# replicated模式,期望3个副本 docker service create --name web --mode replicated --replicas 3 nginx:1.27 # global模式,每个节点一个任务 docker service create --name node-exporter --mode global prom/node-exporter

很多人第一次踩坑就在这里:global模式下的服务执行docker service scale扩容,会直接报错。这让我想起一个真实的判断失误,有同事把global理解成“全局生效的固定副本数”,在3个节点的集群上用global模式部署了一个需要5个实例的服务,结果怎么扩都只有3个任务,查了半天才发现是Mode理解错了。

对比维度replicated 模式global 模式
副本控制通过--replicas指定期望副本数忽略副本数,每节点固定一个
伸缩方式docker service scale动态伸缩随节点数自动伸缩,不可手动scale
适用场景无状态Web服务、API网关等业务负载监控采集、日志代理、节点探针
调度行为调度器选节点,可能落在同一节点强制在所有可用节点分布
典型代表nginx、tomcat、业务APInode-exporter、promtail、datadog-agent

1.3 为什么先搞清楚Mode再上手

这个“1.3”部分要放在前面说,是因为我见过太多人把两个Mode搞混,最后在排查问题时走弯路。

引擎层的Swarm Mode解决的是“这台机器是否参与集群”的问题。比如你在测试环境用Docker Desktop把集群初始化好了,但到了生产环境的服务器上,忘了执行docker swarm init就直接跑docker service create,会得到错误提示This node is not a swarm manager。错误信息倒是直白,但很多人第一反应是“我是不是网络有问题”,而不是“原来Swarm Mode没开启”。

部署层的Mode解决的是“这个服务在集群里怎么分布”的问题,跟引擎是否开启集群模式是两回事。你可以在一台机器上开启Swarm Mode,然后用global模式部署一个服务,效果就是这个节点上有且仅有一个任务;也可以用replicated模式配上多个副本,效果是多个任务抢占同一个节点。两者组合方式是自由的。

简而言之:引擎Mode解决“能不能用集群”,部署Mode解决“服务怎么放上去”。建议你上手前先建立这个最基础的心智模型,后续所有Swarm概念都是在这个框架上展开的。

2. 架构视角:Swarm Mode解决了什么问题

2.1 从单机Docker到集群编排

单机Docker的痛点很典型。一个业务容器暴露在宿主机上,进程挂了?容器会自动重启,但宿主机挂了怎么办。流量涨了想加实例?你得手动在另一台机器上跑同样的容器,再在网关里手动加一条转发规则。业务发布版本?一台一台滚,发布期间服务不中断已经算是精细操作了。

这些痛点的本质是:单机Docker只有“进程管理”能力,没有“集群管理”能力。Swarm Mode的架构动机,就是把调度、状态一致性、服务发现、负载均衡这几件事全部下沉到引擎里。

Swarm集群由两类节点构成:管理节点(Manager)和工作节点(Worker)。管理节点负责维护整个集群的状态,使用Raft协议在管理节点之间同步数据,保证任何时刻所有管理节点对集群状态的认知一致。工作节点则负责实际运行任务,它们只接收来自管理节点的指令。

Raft这个协议可以类比成一个班级的班长群体。有好几个班长同时管理班级,但只有被选出的那个“主班长”能拍板。主班长挂了,其他班长会重新选举,继续维持班级运转。Swarm里的管理节点就是干这件事的,它保证了集群在任何时刻都不会因为某个管理节点宕机而“失去大脑”。

2.2 为什么是“内置”而不是像Kubernetes那样拆分安装

这也是Swarm架构设计里一个很鲜明的取舍点。Kubernetes把组件拆得很细:API Server、Scheduler、Controller Manager、kubelet、kube-proxy,每个模块负责一块独立职责。要部署一套可用的K8s集群,至少得先把这些组件的证书、配置、启动顺序理清楚,这对小团队来说是一道不低的门槛。

Swarm的取向完全不同:编排能力直接塞进dockerd这个二进制里,不需要额外的控制平面进程。docker swarm init之后,一个Docker引擎就同时具备管理节点、调度器、服务发现、DNS解析、负载均衡的能力。管理节点之间通过Raft协议共享状态,状态存储也是引擎内部处理的。

这种“内置”带来的直接优势是运维成本极低。你不需要单独维护一个etcd,不需要担心K8s证书过期,不需要理解一堆抽象概念才能上手。它的劣势也很明显:扩展性和生态远比Kubernetes弱,复杂场景下Swarm能做的事情有限。但如果你只是需要把一个多机集群跑起来、把服务稳定编排起来,Swarm这个架构是“最小的能解决问题的方案”。

2.3 Replicated与Global的决策逻辑

理解了架构动机,再回头看部署层的两个Mode,决策逻辑就清晰多了。

replicated模式针对的是“无状态业务负载”。Web服务、API网关、消息消费者这类可以随意水平扩展的应用,用replicated模式。它的核心价值在于:你可以定义期望副本数,调度器会努力去维持这个数字。负载高了你docker service scale web=10,低峰期缩回3,弹性在一条命令里完成。

global模式针对的是“每个节点都必须有”的基础设施组件。最典型的是监控采集器:你希望集群里每台机器都被采集到指标,那就用global模式部署node-exporter。它自动随节点数伸缩,完全不需要人工干预。日志采集、安全Agent、网络探针,也都是这个逻辑。

选择准则归纳成一句话:业务流量要伸缩,用replicated;基础设施要铺满全节点,用global。

有一个常见误区值得单独提醒:有状态服务,比如MySQL、Redis,放Swarm里要格外谨慎。有人用replicated模式部署MySQL,期望2个副本,以为Swarm会自动做主从复制,结果两个MySQL实例同时往同一个数据卷写数据,直接数据错乱。Swarm不负责数据库级别的数据一致性,它只管调度和网络。数据库这类服务,要么用外置存储+单副本约束,要么干脆跑在虚拟机或物理机上,别让Swarm替你决定它该在哪。

3. 实操:在一个双节点集群里把两种Mode跑通

3.1 初始化Manager节点并加入Worker节点

先把实验环境的假设说清楚:我准备了两台Linux主机,IP分别是192.168.1.100和192.168.1.101,都装了Docker引擎,版本23.0以上。生产环境建议至少3个管理节点,这里为了演示双节点也够用。

在第一台机器上初始化集群:

docker swarm init --advertise-addr 192.168.1.100

--advertise-addr参数很关键,它告诉其他节点来这里通信。如果你有多块网卡,漏掉这个参数可能导致节点之间互相找不到对方。

初始化成功后,会输出一条join命令,类似这样:

docker swarm join --token SWMTKN-1-xxxxx 192.168.1.100:2377

Token是集群的准入凭证,分管理节点Token和工作节点Token。在第二台机器上执行上面的命令,第二台机器就以Worker身份加入集群。

查看集群成员状态:

docker node ls

输出里会显示每个节点的ID、主机名、角色、状态。正常情况下,一台显示Leader或Reachable,另一台显示Ready,角色分别为Manager和Worker。

如果实验做完想清掉集群,在第二台上执行docker swarm leave,在第一台上执行docker swarm leave --force,集群就解散了。

3.2 用--mode replicated部署一个可伸缩服务

集群建好了,先跑一个replicated模式的服务看看效果。

docker network create -d overlay demo-net

覆盖网络(overlay)是Swarm集群跨主机通信的基础网络模式,它把多台宿主机上不同容器之间的二层网络打通,让它们像在同一台机器上一样互相通信。如果不创建自定义overlay网络,服务默认加入ingress网络,也能用,但自定义网络更方便测试服务发现。

然后创建服务:

docker service create --name web \ --network demo-net \ --publish 8080:80 \ --mode replicated \ --replicas 2 \ nginx:1.27

重点解释--publish 8080:80。在Swarm模式下,这个端口发布不是简单地把宿主机端口映射到容器,而是通过Ingress网络实现的:任意节点访问该节点的8080端口,流量都会被路由到某个运行web服务的容器上。这意味着你不需要关心容器实际运行在哪台机器。

查看服务状态:

docker service ls # 查看服务列表和副本状态 docker service ps web # 查看每个副本落在哪个节点

扩容一把,验证replicated模式的核心能力:

docker service scale web=5

再执行docker service ps web,会看到5个任务均匀或不均匀地分布到两台节点上。调度器会尽量避免把任务堆在同一台机器上,但这不是强保证。

继续验证滚动更新:

docker service update --image nginx:1.28 web

Swarm会逐个替换旧版本容器,期间服务不会中断。更新过程里docker service ps web能看到一个任务状态是Running,另一个是Shutdown。这个就是replicated模式在发布场景下的核心价值:用最小代价更新整个服务。

3.3 用--mode global部署一个每节点采集组件

再验证global模式。我在集群里部署一个node-exporter,作为监控采集器的示例:

docker service create --name node-exporter \ --mode global \ --publish 9100:9100 \ prom/node-exporter

执行docker service ps node-exporter,你会看到两个任务,一个运行在Manager节点,一个运行在Worker节点。这就是global模式的定义:每个节点一个,不偏不倚。

试着对它执行scale:

docker service scale node-exporter=5

系统会拒绝,并抛出一段错误提示:service node-exporter can't be scaled。这就是在强制纠偏你之前对Mode的误解。

验证全局采集效果,可以用curl http://192.168.1.100:9100/metrics和curl http://192.168.1.101:9100/metrics,两台机器的指标都通过同一个服务名暴露出来了。

global模式在实际生产中最常用的场景,就是这种“铺满全节点”的采集组件。还有人用它来跑日志转发器、安全Agent、节点健康探针。因为它自动跟随节点数量变化,新扩容的机器不用手动部署任何东西,采集能力就具备。相比之下,如果用replicated模式跑这些组件,你得自己保证“每个节点都有且仅有一个”,调度器并不理解你的诉求,很折腾。

4. 常见问题排查与避坑实录

4.1 环境准备:Docker Desktop的虚拟化报错怎么处理

很多开发机是Windows或macOS,跑Docker Desktop时启动就失败,常见报错是类似Docker Desktop failed to start because virtualisation support wasn't detected。Windows下通常是BIOS里虚拟化没开,或者Windows的Hyper-V功能没启用。排查操作是:进入BIOS确认Intel VT-x/AMD-V打开,控制面板里勾选“Windows Hypervisor Platform”和“适用于Linux的Windows子系统”,两者都生效后再重启Docker Desktop。

另外一个容易忽略的点:Docker Desktop本质上是在你的开发机里跑了一个单节点的Docker引擎,它支持docker swarm init,但不要拿它当多节点生产集群来测试。你在Docker Desktop上初始化Swarm,只能看到一个管理节点,没有Worker节点可以加入。想要真实的多节点体验,还是得准备几台Linux主机,或者用虚拟机模拟跨主机网络。

4.2 global模式服务无法扩容

这节标题就是问题本身。当你对global模式服务执行docker service scale时,Docker会报错拒绝。这个限制是设计使然,不是BUG。如果你的业务确实需要5个实例分布在3台机器上,正确的做法是使用replicated模式并指定--replicas 5。如果你的诉求是“每台都要有”,那你就不应该扩容,而应该扩节点。

排查这个错误时,先docker service ls看看Mode列显示的是global还是replicated,再做下一步操作。很多人因为没看这一列,误以为是Docker版本问题或集群状态异常。

4.3 Swarm集群跨主机网络不通怎么查

这是Swarm集群部署里最折磨人的问题。集群创建成功,服务也部署了,但A机器上的容器访问不到B机器上的容器,或者容器访问不了某个服务的VIP地址。

网络不通的排查顺序,我把常用命令和可能原因列成一张表:

现象可能原因排查命令
节点之间无法加入集群防火墙挡了2377/TCPtelnet目标IP 2377
容器间通信超时7946/TCP+UDP被拦截nc -uvz 目标IP 7946
覆盖网络数据不通4789/UDP被拦截tcpdump -i eth0 udp port 4789
服务名解析失败服务发现异常或overlay网络未关联docker exec 容器ID nslookup 服务名
跨节点访问发布端口失败ingress网络未正常工作docker network inspect ingress

Swarm集群之间有两条核心通信链路:管理节点之间走2377/TCP做Raft同步;节点之间的gossip协议走7946/TCP和UDP;跨主机覆盖网络VXLAN隧道走4789/UDP。云环境里这三组端口在安全组也要放行,本地虚拟化环境则要检查防火墙。

还有一个经验之谈:调试网络问题前,先用docker network ls确认服务是否挂在正确的overlay网络上。我曾经遇到过一个跨主机访问失败的问题,查了半天端口,最后发现是服务创建时没有指定--network demo-net,它被默认挂到了ingress网络,ingress网络虽然负责端口发布,却不参与普通的跨主机容器通信,结果服务名解析不出来。这个坑在网上的教程里几乎不会写。

最后再说两句实在话

两个Mode弄清楚之后,Swarm的主干脉络就很清晰了。我自己的建议是,首次接触Swarm时别急着上生产,先在本地或测试环境里把replicated和global各跑一遍,观察任务在节点上的分布变化,故意执行一次错误的scale命令看看报错,再执行一次docker swarm leave --force看看节点状态变化。这些操作总共也就二十分钟,但带来的掌控感比看任何文章都强。

有个小技巧可以分享一下:排查任何Swarm相关问题时,先执行docker service inspect --pretty 服务名,把服务的完整配置拉出来看一遍。Mode、端口、网络、更新配置全在里面,比你凭记忆猜要靠谱得多。这个命令我几乎每次排查都会用,建议你养成习惯。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询