1. 项目概述:从“parruda/swarm”看分布式系统编排的轻量化实践

最近在梳理团队内部的服务部署流程,发现随着微服务数量的增长,传统的单机部署和手动运维越来越力不从心。我们既不想立刻上马Kubernetes这样重量级的“全家桶”,又需要一种能统一管理多台服务器、具备基础服务发现和负载均衡能力的方案。这时,一个名为“parruda/swarm”的项目进入了我的视野。这并非Docker官方的Swarm Mode,而是一个独立的、轻量级的容器编排工具。它瞄准的正是我们这类场景:中小规模集群,追求极简的运维开销和快速上手,同时又能提供核心的编排能力。经过一段时间的实际部署和压测,我发现它确实在“简单”和“够用”之间找到了一个不错的平衡点。如果你也在为几台到几十台服务器的容器管理发愁,希望有一个比手动脚本更优雅、比K8s更轻便的选择,那么这次关于“parruda/swarm”的深度实践分享,或许能给你带来一些直接的参考价值。

2. 核心架构与设计哲学拆解

2.1 为何选择“另起炉灶”:与Docker Swarm的差异化定位

首先必须厘清一个关键概念: parruda/swarm 并非 Docker 官方 Swarm 的一个分支或改进版,而是一个从零开始、用 Go 语言重写的独立项目。其创始人 Cesar Parra 的初衷,是创建一个更符合 Unix 哲学——“只做好一件事”——的轻量级编排器。那么,它和 Docker Swarm Mode 的核心区别在哪里?

Docker Swarm Mode 是深度集成在 Docker Engine 中的原生集群模式,它提供了完整的容器编排生命周期管理,包括服务定义、滚动更新、密文管理、覆盖网络等。它的优势是“开箱即用”,与 Docker CLI 无缝集成。但这也带来了相对的“重”和“黑盒”。Swarm Mode 的架构相对复杂,其内部 Raft 共识、网络驱动(如 ingress overlay network)对于只想做简单服务编排的用户来说,可能存在不必要的认知负担和资源开销。

而 parruda/swarm 则走了另一条路。它的设计哲学可以概括为 “最小化编排” 。它不试图管理容器的整个生命周期,而是专注于解决两个最核心的问题: 如何在多台主机上维持期望数量的容器副本 ,以及 如何实现简单的服务发现和负载均衡 。它不内置复杂的覆盖网络,而是鼓励你使用主机网络(host network)或利用现有的如 Calico、Flannel 等 CNI 插件。它不直接处理镜像拉取和存储,而是依赖每台主机上预先安装好的 Docker Daemon。这种“职责分离”的设计,使得 parruda/swarm 本身非常小巧(二进制文件仅几MB),部署和运维的心智负担极低。

注意 :这种设计意味着你需要对底层基础设施(如网络、存储、Docker)有基本的掌控力。 parruda/swarm 更像一个智能的调度和协调层,而不是一个全栈平台。

2.2 核心组件与工作流程剖析

parruda/swarm 的架构极其简洁,主要包含两个组件:

  1. Swarm Manager(管理节点) :这是集群的大脑。你通过一个名为 swarm 的 CLI 工具与 Manager 交互,向其提交服务定义(一个 JSON 或 YAML 文件)。Manager 的核心职责是:

    • 维护期望状态 :它将你定义的服务期望状态(如镜像、副本数、端口映射)存储在其内置的 SQLite 数据库中。
    • 调度决策 :根据简单的策略(目前主要是随机调度)决定将容器副本分配到哪些可用的 Agent 节点上。
    • 状态协调 :持续监控集群的实际状态,并通过 gRPC 协议向 Agent 发出指令,驱动集群向期望状态收敛。
  2. Swarm Agent(代理节点) :运行在每台工作主机上的守护进程。它的职责很单纯:

    • 接收并执行指令 :监听来自 Manager 的 gRPC 指令,在本地通过 Docker API 执行容器的创建、启动、停止和删除操作。
    • 上报状态 :定期向 Manager 汇报本节点的状态(如容器运行状态、资源使用情况)。

整个工作流程是一个典型的“声明式API” + “调和循环(Reconciliation Loop)”模式:

  • 声明 :你向 Manager 声明“我需要 3 个副本的 Nginx 服务”。
  • 观察 :Manager 发现当前只有 1 个副本在运行。
  • 调和 :Manager 计算出需要创建 2 个新副本,并通过调度算法选择两个 Agent 节点,向其发送创建容器的指令。
  • 执行 :Agent 节点收到指令,调用本地 Docker 创建并启动容器。
  • 反馈 :Agent 将执行结果(成功或失败)上报给 Manager,Manager 更新其内部状态。

这个循环会持续运行,确保任何偏离期望状态的情况(如容器崩溃、节点宕机)都会被自动检测并修复。

2.3 内置服务发现与负载均衡机制

服务发现是分布式系统的基石。 parruda/swarm 实现了一套轻巧而有效的方案。它没有引入额外的如 etcd 或 Consul 作为服务注册中心,而是利用了 Manager 节点的中心化视角。

服务注册 :当 Agent 成功启动一个容器后,会将该容器的详细信息(服务名、容器ID、所在主机IP、映射端口等)上报给 Manager。Manager 将这些信息存储在自己的状态数据库中。

服务发现与负载均衡 : parruda/swarm 提供了一个内嵌的 DNS 服务器(通常运行在 Manager 节点上)。当集群内的容器(或主机)需要解析某个服务名(如 my-web-app )时,DNS 查询会被指向这个内嵌 DNS。该 DNS 服务器会查询 Manager 的状态数据库,获取当前该服务所有健康副本的 IP 地址列表,并以轮询(Round Robin)的方式返回给查询者。

关键点 :这种负载均衡是 客户端负载均衡 。DNS 返回多个 IP,由客户端(通常是应用的 HTTP 客户端或服务网格 sidecar)决定连接哪一个。它没有像 Docker Swarm 那样提供一个全局的、基于 VIP 的 4层负载均衡器(Ingress Load Balancer)。这对于许多基于 HTTP/1.1 或 gRPC 的现代应用来说已经足够,并且避免了集中式 LB 可能带来的单点瓶颈和配置复杂性。

实操心得 :这种模式要求你的应用容器能够处理 DNS 轮询。对于需要会话保持(Session Affinity)的场景,你需要在应用层实现,或者考虑在 parruda/swarm 之前放置一个支持会话保持的负载均衡器(如 HAProxy 或 Nginx)作为入口。

3. 从零开始部署与核心配置详解

3.1 环境准备与集群初始化

假设我们有三台 Ubuntu 22.04 服务器,IP 分别为 10.0.1.10 (Manager), 10.0.1.11 (Agent1), 10.0.1.12 (Agent2)。所有节点已安装 Docker Engine。

第一步:在所有节点下载 swarm 二进制文件 从项目的 GitHub Release 页面下载最新版本。这里以 v0.5.0 为例,通过命令行操作:

# 在 Manager 节点执行
curl -L -o swarm https://github.com/parruda/swarm/releases/download/v0.5.0/swarm-linux-amd64
chmod +x swarm
sudo mv swarm /usr/local/bin/

# 在 Agent 节点重复同样操作

确保 /usr/local/bin 在系统的 PATH 环境变量中。

第二步:启动 Manager 节点 Manager 需要持久化存储其状态。我们创建一个目录并启动服务:

# 在 10.0.1.10 上执行
sudo mkdir -p /etc/swarm
sudo swarm manager --data-dir /etc/swarm --listen-addr 10.0.1.10:7000 --advertise-addr 10.0.1.10:7000
  • --data-dir :指定 SQLite 数据库和状态文件的存储路径。
  • --listen-addr :Manager 监听的地址和端口,用于接收 Agent 连接和 CLI 命令。
  • --advertise-addr :告知 Agent 和 CLI 如何连接到 Manager。在单网卡环境下通常与 --listen-addr 相同。

为了让 Manager 在后台运行,建议使用 systemd 创建服务单元文件 /etc/systemd/system/swarm-manager.service :

[Unit]
Description=parruda/swarm Manager
After=docker.service network-online.target
Requires=docker.service network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/swarm manager --data-dir /etc/swarm --listen-addr 10.0.1.10:7000 --advertise-addr 10.0.1.10:7000
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

然后启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now swarm-manager
sudo systemctl status swarm-manager # 检查状态

第三步:启动 Agent 节点 在 Agent 节点上,我们需要将其加入到集群。首先获取加入令牌。在 Manager 节点上执行:

swarm --host 10.0.1.10:7000 token create --role agent

这会生成一个类似 SWARM-AGENT-xxxxxx 的令牌。然后在 Agent 节点 ( 10.0.1.11 ) 上启动 Agent:

sudo swarm agent --manager-addr 10.0.1.10:7000 --token SWARM-AGENT-xxxxxx --advertise-addr 10.0.1.11:7001
  • --manager-addr :指定 Manager 的地址。
  • --token :使用上一步生成的加入令牌。
  • --advertise-addr :Agent 向 Manager 注册自己的地址。确保该地址能被 Manager 访问。

同样,为 Agent 创建 systemd 服务文件 /etc/systemd/system/swarm-agent.service ,并启用启动。在另一台 Agent ( 10.0.1.12 ) 上重复此步骤。

第四步:验证集群状态 在 Manager 节点或任何安装了 swarm CLI 并能连接到 Manager 的机器上,执行:

swarm --host 10.0.1.10:7000 node ls

你应该能看到三个节点,一个角色为 manager ,两个角色为 agent ,状态均为 ready 。

3.2 服务定义文件深度解析

parruda/swarm 使用一个 JSON 或 YAML 文件来定义服务。我们以一个典型的 Web 应用为例,创建一个 webapp.yaml :

name: frontend-web
image: nginx:alpine
replicas: 3
ports:
  - 8080:80
environment:
  - NGINX_ENV=production
  - APP_VERSION=v1.2.0
healthcheck:
  cmd: ["curl", "-f", "http://localhost:80/health"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 40s
restart_policy:
  condition: on-failure
  max_attempts: 5
  delay: 10s

让我们拆解每个关键字段:

  • name :服务的唯一标识。也是集群内 DNS 解析使用的名称。
  • image :标准的 Docker 镜像地址。Agent 节点需要能访问该镜像仓库(默认是 Docker Hub)。
  • replicas :期望的容器副本数量。这是声明式编排的核心,Manager 会竭力维持这个数量。
  • ports :端口映射。格式为 主机端口:容器端口 。这里将容器的 80 端口映射到主机的 8080 端口。 重要 :由于 parruda/swarm 没有 ingress 网络,这个端口映射是在 每个运行该容器的 Agent 主机 上直接绑定的。这意味着如果你有 3 个副本运行在 3 台主机上,那么这 3 台主机的 8080 端口都会被占用。你需要一个外部的负载均衡器(如云负载均衡器或独立的 HAProxy)将流量分发到这三台主机的 :8080 上。
  • environment :注入容器的环境变量列表。
  • healthcheck :健康检查配置。这是确保服务可用性的关键。Manager 会根据此配置判断容器是否健康。只有健康的副本才会被纳入服务发现(DNS 记录)。配置项与 Docker 原生 healthcheck 类似。
  • restart_policy :重启策略。当容器非正常退出时,由运行该容器的 Agent 节点本地 根据此策略尝试重启。这与 Manager 驱动的副本数量维持是两层机制。

更复杂的示例:带依赖的服务 parruda/swarm 支持通过 links 或依赖服务名进行简单的服务间通信。例如,一个后端服务需要连接数据库:

name: backend-api
image: mycompany/api-server:latest
replicas: 2
environment:
  - DB_HOST=database # 这里直接使用服务名
  - DB_PORT=5432
# 不需要显式 links,集群内 DNS 可解析 ‘database’
---
name: database
image: postgres:15
replicas: 1 # 数据库通常单实例或主从,这里假设单实例
environment:
  - POSTGRES_PASSWORD=secret
ports:
  - 5432:5432 # 如果需要从集群外访问

后端服务容器内,代码直接连接 database:5432 ,集群的 DNS 会将其解析到运行 PostgreSQL 容器的具体 IP。

3.3 服务生命周期管理实操

部署、更新、回滚和扩缩容是日常运维的核心。

部署服务 :

swarm --host 10.0.1.10:7000 service create -f webapp.yaml

执行后,CLI 会返回一个服务 ID。你可以通过 swarm service ls 查看服务列表,通过 swarm service ps <service-name> 查看该服务所有副本的详细状态和分布节点。

更新服务 : parruda/swarm 目前 不支持 原地滚动更新。这是它与成熟编排器的一个显著区别。更新服务需要采用“蓝绿部署”或“重建”模式。

  1. 修改定义文件 :例如,将 image: nginx:alpine 改为 image: nginx:stable 。
  2. 删除旧服务 : swarm service rm frontend-web 。 注意 :这会立即停止并删除所有运行中的旧容器。
  3. 创建新服务 : swarm service create -f webapp.yaml 。

这种方式有停机时间。为了最小化影响,常见的做法是:

  • 先部署一个名为 frontend-web-v2 的新服务。
  • 将外部负载均衡器的后端从 frontend-web (旧)切换到 frontend-web-v2 (新)。
  • 验证无误后,再删除旧服务 frontend-web 。

扩缩容 :这是 parruda/swarm 的强项,非常简单。

# 扩容到5个副本
swarm --host 10.0.1.10:7000 service scale frontend-web=5
# 缩容到2个副本
swarm --host 10.0.1.10:7000 service scale frontend-web=2

Manager 会立即计算副本差值,并调度 Agent 创建或停止容器。

查看日志 :日志管理是分散的,你需要到运行容器的具体 Agent 主机上去查看 Docker 日志。

# 在运行目标容器的 Agent 节点上执行
docker logs <container-id>

或者,你可以使用 swarm service ps 先找到容器 ID 和所在节点,然后 SSH 到该节点查看日志。对于集中式日志,你需要像在普通 Docker 环境中一样,部署 EFK(Elasticsearch, Fluentd, Kibana)或 Loki 等日志收集栈。

4. 网络、存储与高可用性进阶探讨

4.1 网络模型选择与跨主机通信

如前所述, parruda/swarm 将网络复杂性交给了用户。你有以下几种选择:

  1. 主机网络(Host Network) :在服务定义中,不指定 ports 映射,或者使用 network_mode: host (如果镜像支持)。容器直接使用宿主机的网络命名空间。 优点 :性能最好,零开销。 缺点 :端口冲突管理麻烦,且容器需要以特权模式运行或具有 NET_ADMIN 能力,安全性降低。服务发现依赖集群 DNS 返回宿主机 IP。

  2. 桥接网络 + 主机端口映射 :即前面示例中的方式。这是最常用的模式。容器运行在默认的 bridge 网络或自定义的 Docker 桥上,通过端口映射暴露服务。跨主机通信时,流量需要经过宿主机的路由。 对于需要互相访问的服务 ,调用方应使用 被调用方的服务名 ,集群 DNS 会返回目标容器的宿主机 IP,流量会通过目标宿主机映射的端口进入容器。

  3. 第三方 CNI 网络插件 :这是生产环境更推荐的方式,尤其是需要 Pod 间直接通信(扁平网络)的场景。你可以在所有节点上安装 Calico 或 Flannel。

    • 以 Calico 为例 :安装后,Docker 会配置一个 calico 网络驱动。你可以创建一个 Calico 网络: docker network create --driver calico --ipam-driver calico-ipam my-calico-net 。
    • 在 parruda/swarm 的服务定义中,目前 CLI 可能不直接支持指定网络。但你可以通过 Docker 的元数据功能或在 Agent 上预配置,让服务容器在创建时加入 my-calico-net 。这样,所有容器将获得一个跨主机可达的 IP,服务间可以直接通过容器 IP 通信,无需端口映射,集群 DNS 返回的也是容器 IP,通信效率更高。

踩坑记录 :我们最初使用主机端口映射,当两个服务需要高频 RPC 调用时,流量总是需要绕行到宿主机端口,带来了不必要的性能损耗和 NAT 配置问题。后来切换到 Calico 网络,服务间直接通过容器 IP 通信,延迟显著降低,配置也清爽了许多。切换的关键是在所有 Agent 节点的 Docker 配置中,将 Calico 设为默认网络,或者修改 parruda/swarm Agent 的启动参数,使其创建的容器默认加入 Calico 网络(这可能需要修改 Agent 代码或配置模板)。

4.2 数据持久化与存储卷管理

parruda/swarm 本身不提供分布式存储卷的功能。数据持久化完全依赖 Docker 的卷机制和底层存储设施。

  • 本地卷(Local Volume) :如果容器只需要在 单个节点 上持久化数据(如某个节点的缓存),可以使用 Docker 本地卷。在服务定义中指定 volumes 映射。 切记 :你需要使用 调度约束 ,确保该服务的所有副本(或唯一副本)始终被调度到拥有该数据卷的特定节点上。 parruda/swarm 目前版本的调度标签功能可能比较简单,需要仔细规划。

  • 共享网络存储卷 :对于需要跨节点访问的数据(如用户上传的文件、共享配置文件),必须使用支持网络挂载的卷驱动,如 NFS、Ceph RBD、AWS EFS、Azure Files 等。

    • 以 NFS 为例 :首先在所有节点上安装 NFS 客户端,并确保能挂载远程 NFS 目录。
    • 创建一个 Docker 卷驱动为 local ,但使用 mount 选项指定 NFS 服务器:
      docker volume create --driver local \
        --opt type=nfs \
        --opt o=addr=<nfs-server-ip>,rw,noatime,soft,timeo=100,retrans=3 \
        --opt device=:/path/on/nfs \
        nfs_volume
      
    • 在服务定义中引用此卷: volumes: - nfs_volume:/app/data 。
    • 这样,无论容器被调度到哪个节点,都能访问到同一份数据。

关键策略 :对于有状态服务(数据库),强烈建议使用 固定节点+本地高性能存储 或 专业的云托管数据库服务 。不要依赖编排器在节点间迁移有状态容器, parruda/swarm 并非为此设计。

4.3 Manager 高可用(HA)部署方案

默认的单 Manager 架构存在单点故障风险。如果 Manager 节点宕机,你将无法通过 CLI 管理集群,但 正在运行的容器不会受影响 ,Agent 会继续维持现有容器的运行。然而,你无法进行任何变更操作(扩缩容、更新),并且健康检查失效可能导致服务发现异常。

为了实现 Manager 的高可用, parruda/swarm 支持基于 Raft 共识算法的多 Manager 部署。以下是部署步骤:

  1. 准备多个 Manager 节点 :例如,我们使用 10.0.1.10 , 10.0.1.20 , 10.0.1.30 三台服务器。
  2. 启动第一个 Manager(Leader) :在 10.0.1.10 上,使用 --cluster-addr 参数启动:
    swarm manager --data-dir /etc/swarm --listen-addr 10.0.1.10:7000 --advertise-addr 10.0.1.10:7000 --cluster-addr 10.0.1.10:7001
    
    --cluster-addr 用于 Manager 节点间的 Raft 协议通信。
  3. 获取 Manager 加入令牌 :在第一个 Manager 上执行 swarm --host 10.0.1.10:7000 token create --role manager 。
  4. 启动后续 Manager(Follower) :在 10.0.1.20 上执行:
    swarm manager --data-dir /etc/swarm --listen-addr 10.0.1.20:7000 --advertise-addr 10.0.1.20:7000 --cluster-addr 10.0.1.20:7001 --join 10.0.1.10:7000 --token <MANAGER-TOKEN>
    
    在 10.0.1.30 上重复类似命令, --join 地址可以是集群中任意已知 Manager。
  5. 验证集群状态 :连接任意 Manager,执行 swarm node ls 。应该能看到三个节点,角色都是 manager ,其中一个状态为 leader ,其余为 follower 。

高可用下的 CLI 操作 :配置高可用后,你的 CLI 命令可以指向任意一个 Manager 节点的 --advertise-addr (即 :7000 端口)。如果 Leader 宕机,Raft 协议会自动选举出新的 Leader,CLI 操作在短暂中断后可恢复。

重要提醒 :Manager 节点的数量必须是奇数(1,3,5...)以确保 Raft 集群能正常选举。生产环境推荐至少 3 个 Manager 节点,分布在不同的故障域。

5. 生产环境运维、监控与故障排查实录

5.1 监控体系搭建

监控是保障系统稳定的眼睛。对于 parruda/swarm 集群,我们需要监控以下几个层面:

  1. 主机监控 :使用经典的 Node Exporter + Prometheus + Grafana 组合。在每个节点(包括 Manager 和 Agent)上部署 Node Exporter,收集 CPU、内存、磁盘、网络等指标。
  2. 容器监控 :使用 cAdvisor。同样在每个节点部署 cAdvisor 容器,它会自动收集该节点上所有容器的资源使用情况。Prometheus 从 cAdvisor 拉取数据。
  3. parruda/swarm 组件监控 :这是关键。Manager 和 Agent 进程本身需要被监控。
    • Manager :通过 swarm CLI 的 system info 或 node ls 可以获取一些信息,但更适合集成到监控系统的是 暴露 metrics 端点 。你需要检查 parruda/swarm 是否支持(或通过修改代码支持)暴露 Prometheus 格式的指标,如服务数量、节点状态、调度队列长度等。如果官方不支持,一个变通方法是定期用 CLI 查询状态,并利用 Prometheus 的 Pushgateway 或自定义 Exporter 上报。
    • Agent :监控 Agent 进程是否存活,以及它与 Manager 的连接状态。可以通过在 Agent 节点上运行一个健康检查脚本来实现,该脚本尝试与本地 Agent 的 gRPC 端口通信,或检查 systemd 服务状态。
  4. 日志聚合 :如前所述,使用 Filebeat/Fluentd 收集各节点 Docker 容器日志,发送到 Elasticsearch 或 Loki。

一个简单的 Prometheus 配置片段,用于抓取 Node Exporter 和 cAdvisor:

scrape_configs:
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['10.0.1.10:9100', '10.0.1.11:9100', '10.0.1.12:9100']
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['10.0.1.10:8080', '10.0.1.11:8080', '10.0.1.12:8080']

5.2 常见故障场景与排查手册

在实际运行中,我们遇到了形形色色的问题。下面是一个速查表:

故障现象 可能原因 排查步骤与解决方案
服务副本数量不符 1. 资源不足(CPU/内存)。
2. 镜像拉取失败。
3. 健康检查持续失败。
4. Agent 节点失联。
1. swarm service ps <service> 查看副本事件和状态。 ERROR 状态会给出原因(如 no healthy node )。
2. 登录到调度失败的 Agent 节点,检查 docker ps -a 和 docker logs <container-id> 。
3. 检查节点资源: docker system df , free -h 。
4. 检查网络连通性,确保 Agent 能访问镜像仓库。
服务间网络不通 1. DNS 解析失败。
2. 防火墙规则阻止。
3. 使用了主机网络但端口冲突。
1. 进入客户端容器, nslookup <service-name> 看是否能解析出 IP。检查 Manager 内嵌 DNS 是否正常。
2. 检查集群内节点间的防火墙(如 ufw, iptables),确保相关端口(gRPC、容器映射端口、CNI网络端口)开放。
3. 确认服务定义中的端口映射是否合理,避免宿主机端口冲突。
swarm CLI 命令无响应 1. Manager 进程挂掉。
2. 网络问题导致 CLI 无法连接 Manager。
3. Raft 集群脑裂(多 Manager 场景)。
1. 在 Manager 节点检查 swarm-manager 服务状态: systemctl status swarm-manager 。
2. 使用 telnet <manager-ip> 7000 测试端口连通性。
3. 对于多 Manager,分别连接每个 Manager 执行 swarm node ls ,查看 Leader 状态和节点列表是否一致。可能需要重启状态异常的 Follower。
Agent 节点状态为 disconnected 1. Agent 进程停止。
2. 网络中断。
3. 令牌(token)失效或错误。
1. 在 Agent 节点检查 swarm-agent 服务状态和日志: journalctl -u swarm-agent 。
2. 从 Agent 节点 ping Manager 地址。
3. 检查 Agent 启动命令中的 --token 是否正确,必要时在 Manager 上重新生成令牌并重启 Agent。
容器频繁重启 1. 应用本身崩溃。
2. 健康检查配置过于严格。
3. 资源限制(OOM Killer)。
1. docker logs --tail 100 <container-id> 查看应用日志。
2. 调整健康检查的 interval , timeout , retries 参数,或修改检查命令使其更符合应用启动规律。
3. 检查系统日志 dmesg | grep -i kill 或容器内存限制。在服务定义中适当增加 mem_limit 。

5.3 安全加固建议

  1. 通信加密 :默认情况下,Manager 与 Agent 之间的 gRPC 通信是 明文的 。在生产环境,应启用 TLS 双向认证。

    • 你需要为集群生成 CA 证书,并为每个 Manager 和 Agent 节点生成各自的证书和密钥。
    • 在启动命令中添加 --tls , --tlscacert , --tlscert , --tlskey 等参数来指定证书路径。
    • 具体操作涉及 openssl 命令和证书管理,步骤较多,需参考项目文档或安全最佳实践进行配置。
  2. 最小权限原则 :

    • Manager 节点应部署在内网,不直接暴露给公网。
    • 用于运行 swarm 组件和 Docker 的系统用户,权限应被严格控制。
    • 服务定义中避免使用 privileged: true (特权模式),除非绝对必要。
  3. 镜像安全 :

    • 使用私有镜像仓库,并实施镜像漏洞扫描。
    • 尽量使用特定版本标签(如 myapp:v1.2.3 ),而非 latest 。
    • 镜像遵循最小化原则,使用 Alpine 等小型基础镜像。
  4. 定期备份 : 定期备份 Manager 节点的 --data-dir 目录 。这个 SQLite 数据库包含了集群所有的服务定义和状态。这是灾难恢复的关键。

6. 总结与选型思考

经过数月的实践, parruda/swarm 在我们拥有约 15 个节点、运行几十个微服务的开发测试环境中表现稳定。它的最大魅力在于 简洁和透明 。没有复杂的 CRD(自定义资源定义),没有数以千计的 YAML 行,出了问题,你很容易就能追溯到是 Docker 的问题、网络的问题,还是 swarm 调度器本身的问题。它的日志和状态清晰明了,学习曲线远低于 K8s。

然而,它的“简洁”也意味着“功能有限”。 它不适合的场景包括 :

  • 需要复杂的滚动更新、金丝雀发布 :你需要自己实现蓝绿部署或借助外部工具。
  • 庞大的集群规模(数百节点) :其中心化的 Manager 和简单的调度器可能成为瓶颈。
  • 需要丰富的生态系统和 Operator :如复杂的数据库集群管理、服务网格(Istio/Linkerd)深度集成等。
  • 完全自动化的 CI/CD 流水线 :需要与 GitOps 工具(如 ArgoCD, Flux)紧密集成时,K8s 的生态支持更好。

所以,究竟该不该选择 parruda/swarm ?

我的建议是: 将它视为一个“超级 Docker Compose 集群化工具” 。如果你熟悉 Docker Compose,并且需要将 Compose 描述的服务轻松扩展到多台机器,同时获得基本的服务发现和故障恢复能力,那么 parruda/swarm 是一个非常顺手的选择。它填补了 Docker Compose 和 Kubernetes 之间的空白。对于初创公司、中小型项目、边缘计算场景,或者作为团队内部一个轻量级的 PaaS 平台,它都能出色地完成任务。

最后,一个来自实战的小技巧:由于它的服务定义是简单的 YAML,你可以很容易地编写脚本,结合模板引擎(如 Jinja2)来动态生成不同环境(开发、测试、生产)的配置,实现简单的配置管理。这在一定程度上弥补了其缺乏原生配置管理功能的不足。

更多推荐