Docker Swarm 集群实战:两台 VPS 搭滚动更新与自动故障转移,overlay 网络与有状态服务取舍

单机 Docker 跑得好好的,为什么要上 Swarm

大多数个人站长一开始都是单机 Docker:一台 VPS,一个 compose 文件,把所有服务塞进去。这套方案简单、够用,直到遇到下面几种情况:

  • 单点故障:这台机器宕机或机房抖动,网站全部下线。你开始想「要是有第二台能接管就好了」。
  • 资源瓶颈:某台机器 CPU 或内存吃满,而你手里还有一台闲置的小鸡,想把负载分过去。
  • 滚动更新:手动 compose up -d 更新时有几十秒 downtime,你想要「先起新的再停旧的」。

一提到多机容器编排,很多人直接跳向 Kubernetes。但 K8s 对一个只跑几个网站的站长来说是过度工程——它自己的控制平面就要吃掉一个节点的资源,学习曲线陡到离谱。Docker Swarm(现在正式叫 Swarm mode)是那个被严重低估的中间答案:它是 Docker 内置的集群能力,命令风格和单机几乎一致,学习成本极低,资源占用小,足够支撑个人站点的多机高可用需求。

Swarm 的三个核心概念,用人话讲

先建立心智模型,不然光敲命令会晕:

  • Node(节点):加入集群的一台机器。分两种角色——Manager(管理节点,负责调度和决策)和 Worker(工作节点,只负责跑容器)。
  • Service(服务):你在 Swarm 里发布的单位。它描述「我要 3 个副本,用这个镜像,映射这个端口」。Swarm 自动把副本分布到各节点。
  • Task(任务):副本的实例。一个 service 有 N 个副本,就有 N 个 task,每个 task 落在某个节点上跑成容器。

和单机的区别一句话总结:单机你管的是「容器」,Swarm 你管的是「服务」,Swarm 负责把服务铺到节点上,并在某个节点挂了之后自动把副本挪到健康节点重启。这就是高可用的来源。

前置准备:两台机器的基础条件

假设你有两台同机房的 VPS,能内网互通(这点非常重要,跨机房延迟会让 Raft 共识不稳)。检查清单:

  • Docker 版本一致(28.x 及以上,避开老版本已知 bug);
  • 各节点时间同步(chrony 或 systemd-timesyncd 已跑起来,时间漂移会导致节点被踢);
  • 防火墙放通集群通信端口:2377/tcp(集群管理)、7946/tcp+udp(节点通信)、4789/udp(overlay 网络 VXLAN);
  • 宿主机 hostname 唯一,别两台都叫 localhost。
# 每台机器上确认时间同步
timedatectl | grep "System clock"
# 确认端口没有被别的服务占用
ss -tlnp | grep -E '2377|7946'

第一步:初始化集群

在第一台机器(将成为 manager)上执行:

docker swarm init --advertise-addr 内网IP

--advertise-addr 一定要显式指定内网 IP。如果不指定,Docker 可能选到公网 IP,导致集群通信走公网(慢且不安全)。成功后它会打印一段 docker swarm join --token ... 的命令。

如果机器有多张网卡,命令会报「could not choose an IP address to advertise」,这时候必须显式加这个参数指定。

保存好那条 join 命令(token 可以随时用 docker swarm join-token worker 重新查看)。

第二步:把工作节点加进来

在第二台机器上执行 manager 打印的那条命令:

docker swarm join --token SWMTKN-1-xxxxx... 内网IP:2377

看到 This node joined a swarm as a worker. 就成功了。回到 manager 上验证:

docker node ls

你应该看到两个节点,manager 那行有个 Leader 标记,worker 那行 STATUS 是 Ready、AVAILABILITY 是 Active。

生产建议:manager 至少 3 台(Raft 需要多数派,1 台挂了就失去调度能力)。但个人站两台的情况下,把 worker 也能升成 manager 意义不大——2 节点 Raft 只要挂一台就失去多数派。所以更现实的做法是接受「单 manager、多 worker」的降级,或者干脆 3 台起步。这点要清醒,别被「集群就一定高可用」误导。

第三步:发布你的第一个服务

Swarm 里发布服务用 docker service create。以 Nginx 为例:

docker service create \
  --name web \
  --replicas 2 \
  --publish published=80,target=80 \
  nginx:1.27-alpine

逐项说明:

  • --replicas 2:要两个副本。Swarm 会尽量把它们分到不同节点上。
  • --publish published=80,target=80:这是 ingress 模式的端口发布,Swarm 会在每个节点的 80 端口上都监听,然后做负载均衡——无论你访问哪台节点的 80,请求都会被转发到某个副本。

查看服务状态:

docker service ls
docker service ps web

service ps 会显示每个副本落在哪个节点、是否运行中。

用 Stack 管理完整应用(推荐姿势)

一条条 service create 太原始。Swarm 支持 docker stack deploy,它读的是和 compose 几乎一样的 YAML,但注意——Swarm 的 compose 规范是 v3,很多现代 compose 特性(如 depends_on 的条件启动)不支持。这是最容易踩的坑之一。

示例 stack.yml:

version: "3.8"
services:
  web:
    image: typecho/typecho:latest
    ports:
      - "80:80"
    deploy:
      replicas: 2
      restart_policy:
        condition: on-failure
        max_attempts: 5
      update_config:
        order: start-first
        parallelism: 1
        delay: 10s
    volumes:
      - web_data:/var/www/html
    networks:
      - webnet

  db:
    image: mariadb:11.4
    environment:
      MARIADB_ROOT_PASSWORD: 强密码
      MARIADB_DATABASE: typecho
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - webnet

volumes:
  web_data:
  db_data:

networks:
  webnet:
    driver: overlay

部署:

docker stack deploy -c stack.yml mystack

几个关键点:

  • deploy.update_config.order: start-first:先起新副本、确认健康后再停旧的,实现零停机更新。默认是 stop-first(先停后起,有 downtime)。
  • db 用 placement.constraints 固定到 manager 节点。数据库这种有状态服务必须固定节点 + 挂持久卷,绝不能让 Swarm 随便调度。
  • 服务之间通过 overlay 网络互访,用服务名当主机名(web 里连数据库就写 db:3306)。

滚动更新与回滚

更新镜像版本:

docker service update --image typecho/typecho:v2 web
# 或 stack 场景
docker stack deploy -c stack.yml mystack

Swarm 会按 update_config 的节奏逐个替换副本。观察过程:

docker service ps web --no-trunc

如果新版有问题要回滚:

docker service rollback web

这就是 Swarm 相比手工更新最大的价值——内置的滚动更新和回滚能力,不用自己写脚本。

高可用验证:亲手杀掉一个节点

集群搭好了,别急着信它「高可用」。做一次实战演练:找个跑着副本的 worker,把它的 Docker 停掉模拟宕机:

# 在该节点执行
systemctl stop docker

然后在 manager 上看:

docker node ls
docker service ps web

你会看到该节点变成 Down,而落在它上面的副本会显示 Shutdown,Swarm 随后在健康的节点上重新拉起副本。网站应该只有极短的抖动甚至无感。这个演练非常重要——它能暴露你从未想到的坑(比如固定到 manager 的数据库副本无法迁移,或者卷数据在两个节点上不同步)。

四个必踩的坑

坑一:overlay 网络跨机不通

最常见的原因是防火墙没放通 4789/udp(VXLAN)和 7946。表现是服务起得来,但容器之间 ping 不通、连不上数据库。用 docker network inspect 看有没有节点掉出去。

坑二:有状态服务被调度到错误节点,数据丢了

如果数据库没有固定节点,Swarm 某次调度把它挪到另一台,而卷是本地卷,新节点上是个空目录——数据「消失」了。这是新手最容易丢数据的地方。解决办法:有状态服务固定节点,或用共享存储(NFS/GlusterFS)。

坑三:把最新版 compose 文件直接扔给 stack deploy

你在单机用的 compose 文件可能含 depends_on、profiles 等 Swarm 不认的字段,docker stack deploy 会直接报错或悄悄忽略。要么用带 deploy: 段的 v3 写法,要么接受 Swarm 的能力边界。

坑四:manager 单点,挂了一台就失去调度

前面强调过——manager 靠 Raft 共识,1 台 manager 挂了就直接失去管理能力(虽然已跑的服务还在跑),3 台 manager 才能容忍挂 1 台。个人站要理性评估:要么接受这个风险,要么凑 3 台。

Swarm vs 单机:我最后的选择

坦白说,对绝大多数个人站长,单机 Docker + 完善的备份 + 一台冷备机(出问题手动切 DNS)是性价比更高的方案。Swarm 适合的是这样的你:

  • 有 2 台以上同机房机器,且愿意维护;
  • 需要滚动更新/零停机发布,且不想上 K8s;
  • 服务基本无状态,或能接受有状态服务固定节点;
  • 愿意花一个周末搞懂 Raft、overlay 网络这些概念。

如果你符合这些,Swarm 会用极低的学习成本,给你一套够用的多机高可用。如果不符合,把精力花在单机的备份和监控上,回报更实在。

小结

Docker Swarm 被 K8s 的光芒盖住了,但它在「轻量多机编排」这个生态位上依然没有对手。它复用了你已经会的 Docker 命令,加上节点、服务、overlay 网络三个新概念,就能把单机 Docker 升级成能扛住单点故障的集群。记住四条底线:内网互通、时间同步、有状态服务固定节点、manager 数量按 Raft 多数派规划。做到这几点,你就能用最小代价换来「一台机器挂了网站还在」的踏实感。

Last modification:October 5th, 2026 at 10:26 pm

Leave a Comment