news 2026/10/2 9:19:45

Docker Swarm全生命周期管理:10个关键实践范例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker Swarm全生命周期管理:10个关键实践范例

大家刚开始接触 Docker Swarm 时,多半会围着docker service create和docker service scale这两个命令打转,觉得“能起服务、能扩副本”就算会用了。但做了一段时间运维以后你会发现,命令只是表面,真正决定集群生死的是更外围那些事:初始化时节点怎么选、端口怎么开、证书 token 怎么管,更新发布能不能在出问题时快速回滚,节点要维护了怎么优雅下线,集群不想要了怎么安全拆除。这篇文章我就按“全生命周期管理”这条线,把我实际用过的 10 个 Docker Swarm 精要实践范例整理出来。从docker swarm init开始,一直到docker swarm leave,覆盖服务部署、扩缩容、滚动更新、数据持久化和节点维护这些核心场景,适合已经跑熟单机 Docker、正打算往多节点集群走的开发者,也适合正在维护小规模生产集群的运维朋友。

1. 集群奠基:初始化前把端口、角色和 advertise-addr 一次想清楚

1.1 范例1:初始化前先规划节点角色与通信端口,别在三台机器上直接敲 init

我见过不少朋友把服务器买回来之后,直接就是一句docker swarm init,然后发现 manager 之间经常互相找不到,或者工作节点加了半天加不进来。问题多半出在初始化前没有把节点角色和端口规划好。

先说节点角色。Docker Swarm 把节点分成 manager 和 worker 两类,manager 负责集群状态维护、服务调度和 API 接入,worker 主要跑业务容器。生产环境我习惯用 3 个或 5 个 manager,取奇数是为了 raft 选主时不容易平票;如果只是本地实验或测试学习,1 个 manager 就够,不要为了“看起来专业”硬凑 3 台机器,毕竟多一个节点就多一份维护成本。worker 节点数量根据业务规模来,可以先从 2 个起步,后面需要再加。

然后是端口。以下这三个端口必须提前在安全组或防火墙里放行,否则节点之间没法通信:

端口协议用途
2377tcp集群管理消息、节点加入、raft 通信
7946tcp/udp节点之间的发现与心跳
4789udpoverlay 网络 VXLAN 数据面

这里有个很容易被忽略的细节:如果你既跑了 Swarm 又跑了 Docker 默认的docker0桥接网络,节点之间跨主机容器通信走的是 4789 端口。云服务商的安全组如果只放行 tcp 不放开 udp,经常出现“服务能创建但跨节点访问不通”的怪问题。我踩过一次,排查了半天,最后发现是安全组把 4789/udp 漏了。

初始化命令是这样的:

docker swarm init --advertise-addr 10.0.0.11

--advertise-addr我强烈建议手动指定,尤其在有多个网卡的机器上。如果不指定,Docker 可能会选一个带有默认路由的地址,有时候会选到内网口,有时候会选到公网口,导致其他节点通过这个地址连不上你。判断标准很简单:这个地址必须是你从其他节点能直接访问到的地址。我的习惯是先在每台机器上执行ip addr看一遍网卡,确认好固定的内网 IP 再做初始化。

初始化完成后,立刻执行docker node ls看看自己节点的状态,确认当前节点角色是MANAGER,Hostname 和 Availability 都对。这一步花不了十秒钟,但能帮你避免后面一堆“群龙无首”的问题。

1.2 范例2:用 join-token 把工作节点带进集群,记得定期轮换 token

第一个节点初始化完之后,往集群里加节点就是标准操作了。worker 节点加入的命令不是手写的,而是从 manager 上获取:

docker swarm join-token worker

这条命令会输出完整的一行docker swarm join --token xxx命令,你复制到工作节点执行就行。manager 节点加入则用docker swarm join-token manager。这里有个我认为很关键的习惯:每次加完节点,如果 token 打印在屏幕上被别人看到过,或者你需要批量添加一批临时节点,批量加完以后最好轮换一次 token:

docker swarm join-token --rotate worker

token 轮换不会影响已在集群中的节点,只会让旧的 token 失效。这样即使之前有人悄悄记录过 worker token,也没办法再往你集群里混入新节点了。

我在批量加入节点时还有个习惯:加完节点立刻给节点打上标签。标签不是必须的,但它对后面做服务调度约束非常有用。比如有三个节点是负责跑数据库的,可以这样打标签:

docker node update --label-add role=db node-b

等以后创建服务的时候,--constraint node.labels.role==db就能把数据库类服务精确地调度到这些节点上。这个动作如果你在集群早期就做,后面维护成本会低很多;如果等到服务已经满天飞了再回头补标签,那就只能慢慢调整了。

2. 服务上线:部署思维要从 docker run 切换到 docker service

2.1 范例3:副本数、健康检查与 ingress 网络三件套共同决定服务是否“真正能用”

把服务跑起来的命令很简单:

docker service create \ --name web \ --replicas 3 \ --publish published=8080,target=80 \ nginx:1.26-alpine

这条命令的本质和docker run不一样:你不用自己决定容器在哪台机器上跑,Swarm 在集群里帮你调度。副本数 3 意味着它会尽量把 3 个容器分布到不同的节点上,这本身就是一层高可用。

--publish published=8080,target=80里,published=8080是对集群对外暴露的端口,target=80是容器内的端口。当你在有多个节点的 Swarm 集群里访问任意一个节点的 8080 端口时,请求都会通过 ingress 网络被转发到某个健康副本上,这就是 Swarm 路由网格的基本逻辑。如果你不想走路由网格,而是希望每个节点只暴露本节点的副本,可以用--publish mode=host,这个我一般只在特殊场景用,普通 Web 服务还是默认的 ingress 模式更省心。

服务创建之后,必须尽快加上健康检查。没有健康检查的服务,Docker 只能根据容器进程是否存活来判断它好不好;进程还活着但业务已经 500 了,Swarm 是不知道的。健康检查参数可以直接写在service create里:

docker service create \ --name web \ --replicas 3 \ --health-cmd "wget -qO- http://localhost/ || exit 1" \ --health-interval 10s \ --health-timeout 5s \ --health-retries 3 \ --publish published=8080,target=80 \ nginx:1.26-alpine

注意这里我用的是wget,因为很多精简镜像是没有 curl 的。健康检查命令必须选镜像里真实存在的工具,否则状态会一直显示starting,等于没配。健康检查的意义和普通进程存活还不一样:它是给 Swarm 做“任务是否健康”判断用的。健康检查失败到一定次数,服务会被重新调度,这在后面的更新和自愈场景里非常关键。

另外一个容易忽略的点是:如果服务需要多个容器之间互相通信,记得把它们放到同一个 overlay 网络里。自定义网络可以这样建:

docker network create -d overlay --attachable mynet

之后创建服务时加一个--network mynet,服务之间直接通过服务名互相访问即可,这样不用关心容器 IP 会变。记住,默认的 ingress 网络只是给外部流量做路由用的,内部服务之间的调用还是建议单独分一个 overlay 网络,逻辑更清晰。

2.2 范例4:扩缩容不只是一条 scale 命令,placement 约束和资源预留才是关键

要扩容,最简单的是:

docker service scale web=5

或者用docker service update --replicas 5 web,两者效果基本一样。但如果你的集群节点配置差异很大,盲目扩缩容很容易出问题。比如 3 台机器中有一台是 2C4G 的老机器,其他两台是 8C16G,你把一个很吃内存的服务扩到 10 副本,调度器可能优先把任务堆到配置差的机器上,最后大家都不稳。

解决这个问题的两个工具是 placement 约束和资源预留。

placement 约束用来指定“这个服务可以跑在哪些节点上”,格式是:

docker service update \ --constraint-add node.labels.role==api \ --constraint-add node.role!=manager \ web

node.role!=manager是我特别喜欢用的一个约束,意思是业务容器不落在 manager 节点上。manager 节点本身承担了 raft 通信和集群调度任务,再把重业务压上去,集群一抖动 manager 也会跟着抖。生产环境我一般都会给业务服务加上这个约束。

资源预留通过在创建或更新服务时指定这些参数:

docker service update \ --reserve-cpu 0.25 \ --reserve-memory 256m \ --limit-cpu 1.5 \ --limit-memory 1g \ web

其中reserve是调度器在决定把任务放到哪个节点时参考的预留值,limit是容器运行时真正受到限制的 cgroup 上限。很多人以为设置了 limit 就够了,其实如果没有 reserve,调度器可能把一堆 limit 加起来远超节点的真实容量,最后节点 load 爆表;设置了 reserve 后,调度器在放任务时会主动避开那些已经被预留得差不多的节点。一个相对安全的做法是:先根据监控数据估算单个副本的正常占用,reserve 设为正常值,limit 设为正常值的 1.5 到 2 倍。

还有一类服务希望每个节点都跑一个副本,比如日志采集器或监控 exporter,用--mode global更合适:

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

这种模式下不需要指定--replicas,Swarm 会自动保证每个可用的节点上都跑一个任务,新节点加入集群时也会自动补齐。理解了副本模式和 global 模式的区别,扩缩容的时候思路就会清晰很多。

3. 版本发布:滚动更新要有节奏,回滚要有完整排查链路

3.1 范例5:滚动更新的核心不是换镜像,而是 update-parallelism、delay 和 order

Swarm 的滚动更新机制我觉得设计得挺巧妙的,它更新的不是整个服务,而是服务里的一个个任务。你要发布新版本,只需要:

docker service update \ --image nginx:1.27-alpine \ --update-parallelism 1 \ --update-delay 30s \ --update-order start-first \ --update-failure-action pause \ web

逐项解释一下这些参数的实际意义:

--update-parallelism 1表示每次只更新一个副本。如果设置成 2 或 3,则每一批同时更新多个副本。对生产环境来说,从 1 开始最稳妥,等确认没问题再考虑调大。

--update-delay 30s表示每更新完一个副本,等 30 秒再更新下一个。这个时间窗口给了你观察日志和监控的余地。如果副本健康检查需要 10 秒,delay 至少要比健康检查周期长,否则上一个还没确认健康,下一个已经开始更新了。

--update-order start-first表示先启动新副本,等新副本进入运行状态后,再停掉旧副本。这个模式能最大限度降低服务中断时间,代价是更新期间节点上会同时存在新旧两个容器,对资源的要求会翻倍。如果集群资源本来就很紧张,可以改用默认的stop-first,先停旧的再起新的,但会有短暂的服务不可用窗口。默认值其实是stop-first,这一点很多人记反了,我特别强调一下。

--update-failure-action pause表示如果某一批任务更新失败,Swarm 会自动暂停更新,不会继续往下滚。这个我强烈建议保留,不要图省事改成 continue,否则一个坏镜像能被滚到所有副本上。

更新过程中观察状态,用一条命令就够:

docker service ps web

你会看到每个任务当前处于什么状态,是Running、Starting还是Failed,同时右侧会显示上一次的更新状态。如果某一行的状态里出现UpdateState: paused,说明更新被暂停了,需要赶紧去查日志。

3.2 范例6:发布翻车后的快速回滚链路,不要一上来就 rollback

回滚是发布动作的安全网。标准命令是:

docker service update --rollback web

这条命令会把你服务镜像等一系列配置回滚到上一次发布前的状态,而且会按照服务配置里的更新策略分批执行。如果 rollback 能顺利触发,Swarm 会自动把任务恢复到旧版本,整个过程不需要你手动去改 image。

但我的经验是:回滚之前先做三轮排查,不要一看到服务有问题就直接 rollback。原因是有些问题不是新版本本身引入的,而是节点资源不足、网络不通、或者配置被改坏了,直接回滚解决不了根因。

我的一般排查链路是这样的:

第一步,看任务状态。执行docker service ps web,看哪些任务卡在Starting或者Failed,这些任务集中在哪些节点上。如果所有失败都集中在一个节点,那大概率是那个节点本身有问题,而不是镜像问题。

第二步,看服务日志。执行:

docker service logs --since 30m web

重点看报错是拉取镜像失败、端口占用,还是容器启动后进程崩溃。镜像失败通常是私有仓库的问题,端口占用往往是因为节点上有一个残留容器占了同一个端口。

第三步,用docker inspect看单个任务的详细信息。先通过docker service ps web --no-trunc拿到失败任务的容器 ID,然后:

docker inspect <container-id>

看State段里的ExitCode和Error。ExitCode 1多半是应用启动逻辑问题,ExitCode 137则很可能是内存不足被杀。

排查完之后再决定:如果确认是新版本代码问题,执行docker service update --rollback web;如果只是某个节点有问题,先处理节点,比如docker node update --availability drain 出问题节点,再考虑要不要回滚。我遇到过一个很典型的场景:新镜像没问题,但其中一个 worker 节点的磁盘被日志写满了,所有任务都拉不下镜像,这时候去 rollback 只会把旧的也推不上来,正确操作是先把节点 drain 掉,再手动清理磁盘。

回滚完成后,记得再执行一遍docker service ps web,确认所有任务都回到 Running 状态。如果回滚也失败,说明你必须手动指定旧镜像重来一遍了:先docker service ps web查到上一个可用镜像的版本,然后:

docker service update --image nginx:1.26-alpine web

这种情况下我建议把--update-failure-action pause保留,至少失败时能暂停,留给你一个稳定的现场。

4. 数据与配置:容器可以随时漂移,但状态不能跟着丢

4.1 范例7:本地卷不会随容器“漂移”,跨节点共享要提前想清楚

Swarm 里的任务是分布在不同节点上的,这带来一个很现实的问题:容器可以随时被调度到另一台机器上,但容器里的数据怎么搬家?

先明确一个基本点:如果创建服务时没有挂载任何卷,容器内的所有写入都会在容器删除时一并消失。Swarm 里为了高可用是会重新调度任务的,一旦任务换到别的节点,原来的容器被删掉,数据就没了。所以上生产环境,必须给有状态的服务挂载持久化存储。

最简单的挂载方式:

docker service create \ --name app \ --mount type=volume,source=appdata,target=/data \ --replicas 1 \ your-image

这里创建了一个名为appdata的 Docker volume,挂载到容器/data。它的内容包括在 manager 中以 raft 方式同步的 volume metadata,但实际数据卷本体仍然只存在创建它的那个节点上。这一点非常关键:这并不代表数据会在集群内自动复制或跟随服务迁移。

如果你只有一个副本,并且这个副本被重新调度到另一个节点,数据不会跟着走。所以更可靠的做法是:给有状态服务加上 placement 约束,把它固定在有数据的节点上:

docker service update \ --constraint-add node.labels.role==db \ app

同时在该节点上打好role=db的标签。这样即便任务需要重启,它也只会在这个节点上重启,不会漂到别的机器上去。

那如果想要一个真正能在多节点间共享的存储呢?我的建议是不要自己去造分布式的轮子,直接用外部的 NFS 或云厂商的共享文件存储。创建服务时用绑定挂载方式指向 NFS 目录:

docker service create \ --mount type=bind,source=/mnt/sharedstorage/target=/data \ --replicas 3 \ app

但要注意一点:多个副本同时写同一个共享目录,应用本身必须支持并发写。如果不支持,共享存储反而会引入数据损坏问题。我见过有人把 MySQL 数据目录放在 NFS 上,还开了 3 个副本,结果数据还没跑几天就坏了。数据库这类应用更适合采用“单副本加持久化节点约束”的模式,不要把多副本写到共享存储上。

4.2 范例8:Secret 和 Config 是 Swarm 里的“配置单”,不要把它们写进镜像

配置管理和数据持久化同样重要。现在很多团队还习惯把数据库密码、API key 直接塞进镜像环境变量里,镜像一旦推送到仓库就等于把秘密公开了。Swarm 原生提供的方案是 secret 和 config。

创建 secret 很简单:

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

创建服务时挂载进去:

docker service create \ --name app \ --secret source=db_password,target=/run/secrets/db_password,mode=0400 \ your-image

挂载后,密码会以文件形式出现在容器里的/run/secrets/db_password,应用读取这个文件即可。secret 文件在容器内是以内存文件系统挂载的,不会写入容器可写层。应用的配置文件如果是一些非敏感内容,比如 nginx 配置、应用环境变量模板,可以用 config 来管理:

docker config create app_config nginx.conf docker service create \ --name web \ --config source=app_config,target=/etc/nginx/nginx.conf \ nginx:1.26-alpine

这比把配置文件拷贝进镜像或者通过 bind mount 维护要干净得多。配置文件的任何修改,都可以通过docker config create生成新版本,然后更新服务引用。

更新 secret 或 config 时,Swarm 不会自动把新版本应用到已经运行的任务。你需要主动触发一次服务更新:

docker service update \ --secret-rm db_password \ --secret-add source=db_password_new,target=/run/secrets/db_password,mode=0400 \ app

这里有个非常重要的坑:secret 一旦更新,Swarm 会通过滚动更新重启任务,把新 secret 文件挂载进去。所以更新 secret 本身也是一次发布动作,必须按前面说的更新节奏来,不要以为只是改个配置就偷偷摸摸地做。

5. 运维收尾:节点维护、集群升级与下线清理

5.1 范例9:drain 是节点维护的唯一优雅方式,别直接去 stop 容器

当你需要重启某个节点、升级内核、或者更换硬件时,千万不要直接去节点上停容器,正确做法是先把节点标记为 drain:

docker node update --availability drain node-b

执行之后,docker node ls里这个节点的 Availability 会变成Drain。Swarm 会把该节点上的所有服务任务主动停止,并在其他可用节点上重新创建。这个过程不是瞬间完成的,新任务从拉镜像到健康检查通过需要时间,所以你在维护窗口前要预估好这个迁移耗时。

维护完成后,把节点重新打开:

docker node update --availability active node-b

但有一个点很多人不知道:节点恢复 active 后,之前被挪走的任务并不会自动搬回来。Swarm 只在任务失败或需要重新调度时才会把任务放到这个节点上,不会主动做“重新平衡”。如果你希望恢复后把服务的一部分任务移回该节点,可以手动触发一次服务更新:

docker service update --force web

这会按照滚动更新策略把所有任务重新创建一遍,调度器在放置时就会考虑这个刚恢复的节点。注意--force会把所有任务都重建,生产环境要选在低峰期操作。

如果某个节点已经彻底宕机了,比如硬件损坏、系统无法引导,那就不需要 drain 了。你可以直接:

docker node ls docker node rm <node-id>

把该节点从集群中移除。docker node rm需要先确认节点状态是Down,如果还是Ready,会提示你没办法删除。这时要看情况:如果它真的还活着,先把它改成 drain 再来删;如果它已经不可达,等状态变成 Down 再删。

5.2 范例10:集群升级要分批次滚动,彻底下线要清理干净

Swarm 集群的升级,本质上是 Docker Engine 的升级,但集群节点因为角色不同,升级顺序有讲究。我的建议是:先升级 worker 节点,再升级 manager 节点;升级每个节点之前,先 drain,升级完成后重新 active,再等它完全恢复正常,再动下一个节点。如果集群里没有多余的 worker 节点来做漂移,至少保证升级过程中不会有两个 manager 同时不可用,否则可能影响 raft 选主。

每次升级完成后,用这些命令做常规确认:

docker node ls docker service ls docker service ps --format "table {{.Name}}\t{{.CurrentState}}\t{{.Error}}" web docker node ps

docker node ps是我很常用的命令,它能快速列出某个节点上所有任务的状态,可以帮你确认节点升级完成后负载是否正常。日志方面,单服务日志用:

docker service logs --tail 100 --follow web

节点自身的 Docker 日志则看系统日志,比如用journalctl -u docker -n 100。如果业务日志量很大,建议把 Docker daemon 的 log driver 配置成集中式日志方案,否则节点磁盘很快会被日志打满,这也是很多集群“莫名其妙”任务失败的幕后黑手。

最后说集群下线清理。如果你确定整个 Swarm 不再需要了,不要只在一台机器上操作。步骤是这样:先移除服务,让容器都停止:

docker service rm $(docker service ls -q)

然后在每个 worker 节点上执行:

docker swarm leave

在 manager 节点上执行:

docker swarm leave --force

--force是必须的,因为 manager 节点离开集群时不带这个参数会被拒绝。最后一个 manager 离开后,Swarm 集群状态就没了。但此时节点上可能还残留着/var/lib/docker/swarm目录,里面有机密数据和 raft 日志。如果这台机器将来还要重新加入其他集群,建议清掉:

sudo systemctl stop docker sudo rm -rf /var/lib/docker/swarm sudo systemctl start docker

不清这个目录的话,下次docker swarm init可能会因为状态残留而报错,甚至会出现“看起来初始化成功了但节点列表怪怪的”的问题。

我自己的习惯是:每次在集群里增加或删除节点后,都顺手执行一次docker swarm join-token --rotate worker,把旧的加入凭证作废。这个动作既简单又能避免历史 token 在离职人员或测试脚本里留存带来的隐患。

整体看下来,Docker Swarm 的整套生命周期操作其实不复杂,难的是一旦出事你能不能快速定位。我的心得是:把docker service ps、docker node ls和docker service logs这三条命令练成肌肉记忆,每次变更完先看状态,再看日志,最后再做决策。上面这 10 个范例基本覆盖了从初始化到拆集群的常见节点,照着一步步走,至少能保证你在集群的每个阶段都知道自己该做什么、不该做什么。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 9:19:24

Paperclip:本地AI工作流胶合层,React+Node.js直连Claude与OpenClaw

1. 项目概述&#xff1a;Paperclip 是什么&#xff0c;它解决的到底是什么问题&#xff1f; Paperclip 这个名字乍一听容易让人联想到办公用品——回形针。但放在当前技术语境下&#xff0c;尤其结合你提供的热搜词组合&#xff08;Node.js、React、OpenClaw、Claude&#xff0…

作者头像 李华
网站建设 2026/10/2 9:19:05

Docker入门到实战:镜像容器、端口映射、数据卷与常见坑

装过Docker的人都知道&#xff0c;第一次把 docker run hello-world 跑起来&#xff0c;屏幕上打出一段"Hello from Docker!"的时候&#xff0c;心里那点成就感是真的。但等你回过神来&#xff0c;往往是一连串问号&#xff1a;镜像和容器到底啥关系&#xff1f;为…

作者头像 李华
网站建设 2026/10/2 9:18:09

IEEE33节点配电网仿真:模型、潮流计算与无功优化实战指南

简介&#xff1a;这份资源面向电力系统方向的学生、教师与科研人员&#xff0c;提供IEEE33节点配电网的标准测试案例&#xff0c;可用于潮流计算、电压分布分析、故障模拟与保护策略验证等教学与科研场景。压缩包共2个文件&#xff0c;包含1个m脚本与1个slx模型&#xff0c;整体…

作者头像 李华
网站建设 2026/10/2 9:18:08

Flutter contacts包鸿蒙化实践:通讯录插件适配全解析

做 Flutter 鸿蒙化的团队&#xff0c;迟早会撞上一面墙&#xff1a;pub.dev 上那批成熟的 Flutter 三方库&#xff0c;绝大多数只维护了 Android 和 iOS 两个平台的实现&#xff0c;ohos这个平台标签在官方支持列表里根本不存在。今天要聊的contacts包就是这面墙上的典型一块砖…

作者头像 李华
网站建设 2026/10/2 9:16:38

OpenHarmony上的Flutter跨端开发:井盖地图批量导入与原生通信

先交代背景&#xff1a;这个项目是在 OpenHarmony 设备上做一张城市井盖的数字化管理地图&#xff0c;客户端用 Flutter 跨端方案&#xff0c;服务端给一批 CSV/Excel 的井盖台账数据&#xff0c;要在 App 里批量导入并落到地图上&#xff0c;形成可点、可查、可筛选的资产图层…

作者头像 李华
网站建设 2026/10/2 9:15:42

三类别电动车头盔检测数据集:YOLOv5训练与调参实战指南

简介&#xff1a;面向目标检测初学者与头盔佩戴自动识别需求&#xff0c;提供一套按YOLOV5目录结构整理的道路电动车头盔检测数据集。共3个类别&#xff1a;戴头盔、没戴头盔&#xff0c;以及行人整体标注&#xff0c;图片为19201080分辨率的RGB道路场景&#xff0c;适合直接接…

作者头像 李华