欢迎光临
我们一直在努力

Docker - Docker Swarm初识,搭建容器集群

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Docker这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • Docker Swarm初识:搭建容器集群 🐳✨
    • 一、为什么是 Swarm?不是 Kubernetes?🤔
    • 二、Swarm 核心概念:节点、服务、任务、网络 🧩
      • 1. 节点(Node)—— 集群的物理/虚拟单元
      • 2. 服务(Service)—— 用户定义的可扩展应用单元
      • 3. 任务(Task)—— Service 的最小调度单位
      • 4. 网络(Network)—— 内置覆盖网络(Overlay Network)
    • 三、动手实践:搭建三节点 Swarm 集群 🛠️
      • 步骤 1:初始化 Manager 节点
      • 步骤 2:添加两个 Worker 节点
    • 四、Java 示例:Spring Boot 微服务集群化 🌱
      • 4.1 项目结构概览
      • 4.2 核心 Java 代码详解
        • ✅ `UserController.java` —— 提供 `/users` 接口
        • ✅ `User.java` —— JPA 实体(带 Lombok 简化)
        • ✅ `application.yml` —— 关键配置(适配 Swarm 环境)
      • 4.3 Dockerfile —— 构建可移植镜像
    • 五、Swarm Stack 部署:`docker-compose.yml` 详解 🧱
      • 部署 Stack
    • 六、服务验证与流量观察 🌐
      • 6.1 访问应用 API(通过 Swarm Ingress)
      • 6.2 观察服务发现:DNS 解析验证
      • 6.3 查看健康状态与日志
    • 七、高级能力实战:滚动更新与故障自愈 🔄
      • 场景 1:无缝滚动更新(Rolling Update)
      • 场景 2:模拟节点宕机,验证自愈能力
    • 八、安全与生产就绪要点 🔐
      • 8.1 TLS 加密通信(默认启用)
      • 8.2 Secrets 管理(替代环境变量)
      • 8.3 资源限制与隔离
      • 8.4 日志集中收集(对接 ELK / Loki)
    • 九、Swarm vs Kubernetes:何时切换?⚖️
    • 十、常见问题与排错指南 ❓
      • Q1:`docker service ps` 显示 `Rejected` 或 `Failed`,如何排查?
      • Q2:服务启动后无法通过 `localhost:8080` 访问?
      • Q3:如何让 Swarm 服务被外部域名访问(如 `api.myapp.com`)?
      • Q4:`docker stack deploy` 报错 `invalid reference format`?
    • 十一、结语:拥抱简单,始于 Swarm 🌈

Docker Swarm初识:搭建容器集群 🐳✨

在现代云原生应用开发与运维实践中,单机容器部署已远远不能满足高可用、弹性伸缩与故障自愈的需求。当业务规模扩大、服务模块增多、用户请求激增时,我们需要一种轻量级、原生集成、生产就绪的容器编排方案——Docker Swarm 正是为此而生的官方答案 🌟。

它不是 Kubernetes 那般庞杂的“操作系统级”平台,而是以极简哲学构建的分布式容器调度引擎:无需额外安装复杂组件,仅靠 docker swarm init 一条命令即可启动集群;所有操作通过熟悉的 docker CLI 完成;天然兼容 Docker Compose v3+ 格式;且深度嵌入 Docker Engine,零外部依赖,开箱即用 🚀。

本文将带你从零开始,系统性地认识 Docker Swarm 的核心概念、工作原理与实战能力。我们将亲手搭建一个三节点 Swarm 集群(1 manager + 2 worker),部署一个具备 REST API、数据库连接、健康检查与滚动更新能力的 Spring Boot 微服务,并通过可视化方式理解服务发现、负载均衡、任务调度等关键机制。全程使用纯 Docker 原生命令,不引入 Helm、Traefik 或 Consul 等第三方工具,确保你掌握的是最本质、最可控的编排能力 💡。

✅ 阅读前提:你已安装 Docker Desktop(macOS/Windows)或 Docker Engine(Linux),并能正常运行 docker –version 和 docker info。无须 Kubernetes 经验,但了解基本容器概念(镜像、容器、端口映射、网络)将更顺畅。


一、为什么是 Swarm?不是 Kubernetes?🤔

这是一个高频问题。我们先不急于编码,而是厘清定位:

维度Docker SwarmKubernetes
学习曲线 ⭐⭐☆(极低|CLI 语义直白,docker service create 即部署) ⭐⭐⭐⭐⭐(陡峭|需掌握 Pod/Deployment/Service/Ingress/CRD 等数十种抽象)
资源开销 ⚡ 极轻量(manager 节点内存占用 <50MB) 🐘 较高(etcd + kube-apiserver + controller-manager + scheduler 等常驻进程)
部署复杂度 ✅ 单命令初始化集群(docker swarm init) ❌ 需 kubeadm/kops 或托管服务(EKS/AKS/GKE)
生态成熟度 📦 官方维护,与 Docker Desktop 深度集成,CI/CD 流水线友好 🌐 生态庞大,但配置碎片化严重(CNI/CSI/Ingress Controller 选型多)
适用场景 ✔️ 中小团队快速上线微服务✔️ CI/CD 测试环境集群✔️ 边缘计算轻量节点编排✔️ Docker Compose 迁移平滑升级 ✔️ 大型企业超大规模混合云编排✔️ 需要细粒度策略控制(如 PodSecurityPolicy)✔️ 已有大量 K8s 运维资产

🔗 延伸阅读:Docker 官方对 Swarm 的定位说明可参考 Docker Swarm Overview —— 这里明确指出:“Swarm mode is designed for production use and provides built-in orchestration capabilities.”(Swarm 模式专为生产环境设计,并提供内置编排能力)

如果你正在构建一个内部管理后台、IoT 设备数据聚合服务、或是高校课程实验平台,Swarm 是比 Kubernetes 更务实、更高效的选择。它不追求“大而全”,而是专注把容器调度这件事做到可靠、简洁、可预测 ✅。


二、Swarm 核心概念:节点、服务、任务、网络 🧩

在动手前,必须理解四个基石概念。它们共同构成 Swarm 的运行模型:

1. 节点(Node)—— 集群的物理/虚拟单元

  • Manager Node(管理节点):负责集群状态维护、任务调度、API 响应。支持 Raft 共识算法实现高可用(≥3 个 manager 可容忍 1 个宕机)。
  • Worker Node(工作节点):仅执行 manager 分配的任务(即运行容器),不参与集群决策。

💡 提示:单节点也可作为 manager 运行 Swarm(开发测试常用),但生产环境强烈建议 ≥3 manager 实现容错。

2. 服务(Service)—— 用户定义的可扩展应用单元

  • Service 是 Swarm 的核心抽象,代表一组相同容器的声明式定义(类似 Kubernetes 的 Deployment)。
  • 支持两种模式:
    • replicated(默认):指定副本数(如 –replicas 3),Swarm 自动在可用 worker 上均匀分布;
    • global:每个 worker 节点上运行恰好一个该服务的实例(适合日志收集器、监控 agent)。

3. 任务(Task)—— Service 的最小调度单位

  • 每个 Task 是一个容器实例(Container),由 Swarm 创建并监控生命周期。
  • Task ID 全局唯一,状态包括:assigned → pending → running → complete → failed。
  • 当 Task 异常退出,Swarm 会自动在同节点或其它节点拉起新 Task 替代(自愈能力)✅。

4. 网络(Network)—— 内置覆盖网络(Overlay Network)

  • Swarm 自动创建 ingress 网络(用于服务间负载均衡)和 docker_gwbridge(worker 与外部通信)。
  • 用户可创建自定义 overlay 网络(docker network create -d overlay mynet),实现跨主机容器通信,且默认加密(IPSec)🔐。
  • 所有 Service 默认接入 ingress 网络,可通过服务名直接 DNS 解析(如 curl http://user-service:8080/health)。

下面这张 Mermaid 图表清晰展示了三节点 Swarm 中 Service、Task 与 Network 的关系:

#mermaid-svg-CaaDFPFRZkHpP4Il{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CaaDFPFRZkHpP4Il .error-icon{fill:#552222;}#mermaid-svg-CaaDFPFRZkHpP4Il .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CaaDFPFRZkHpP4Il .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CaaDFPFRZkHpP4Il .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CaaDFPFRZkHpP4Il .marker.cross{stroke:#333333;}#mermaid-svg-CaaDFPFRZkHpP4Il svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CaaDFPFRZkHpP4Il p{margin:0;}#mermaid-svg-CaaDFPFRZkHpP4Il .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster-label text{fill:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster-label span{color:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster-label span p{background-color:transparent;}#mermaid-svg-CaaDFPFRZkHpP4Il .label text,#mermaid-svg-CaaDFPFRZkHpP4Il span{fill:#333;color:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il .node rect,#mermaid-svg-CaaDFPFRZkHpP4Il .node circle,#mermaid-svg-CaaDFPFRZkHpP4Il .node ellipse,#mermaid-svg-CaaDFPFRZkHpP4Il .node polygon,#mermaid-svg-CaaDFPFRZkHpP4Il .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CaaDFPFRZkHpP4Il .rough-node .label text,#mermaid-svg-CaaDFPFRZkHpP4Il .node .label text,#mermaid-svg-CaaDFPFRZkHpP4Il .image-shape .label,#mermaid-svg-CaaDFPFRZkHpP4Il .icon-shape .label{text-anchor:middle;}#mermaid-svg-CaaDFPFRZkHpP4Il .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CaaDFPFRZkHpP4Il .rough-node .label,#mermaid-svg-CaaDFPFRZkHpP4Il .node .label,#mermaid-svg-CaaDFPFRZkHpP4Il .image-shape .label,#mermaid-svg-CaaDFPFRZkHpP4Il .icon-shape .label{text-align:center;}#mermaid-svg-CaaDFPFRZkHpP4Il .node.clickable{cursor:pointer;}#mermaid-svg-CaaDFPFRZkHpP4Il .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CaaDFPFRZkHpP4Il .arrowheadPath{fill:#333333;}#mermaid-svg-CaaDFPFRZkHpP4Il .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CaaDFPFRZkHpP4Il .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CaaDFPFRZkHpP4Il .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CaaDFPFRZkHpP4Il .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CaaDFPFRZkHpP4Il .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CaaDFPFRZkHpP4Il .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster text{fill:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il .cluster span{color:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CaaDFPFRZkHpP4Il .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CaaDFPFRZkHpP4Il rect.text{fill:none;stroke-width:0;}#mermaid-svg-CaaDFPFRZkHpP4Il .icon-shape,#mermaid-svg-CaaDFPFRZkHpP4Il .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CaaDFPFRZkHpP4Il .icon-shape p,#mermaid-svg-CaaDFPFRZkHpP4Il .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CaaDFPFRZkHpP4Il .icon-shape .label rect,#mermaid-svg-CaaDFPFRZkHpP4Il .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CaaDFPFRZkHpP4Il .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CaaDFPFRZkHpP4Il .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CaaDFPFRZkHpP4Il :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Worker Node 2

Worker Node 1

Manager Node

Schedules Tasks

Schedules Tasks

Schedules Tasks

Schedules Tasks

Overlay Networkvia ingress

Overlay Networkvia ingress

Swarm Manager• Raft Store• API Server• Scheduler

Task: user-service.1Container ID: abc123

Task: order-service.1Container ID: def456

Task: user-service.2Container ID: ghi789

Task: payment-service.1Container ID: jkl012

✅ 图中可见:Manager 不运行业务容器,只做决策;Worker 运行 Task(容器);所有 Task 通过内置 ingress overlay 网络互通,无需暴露端口或配置 host 网络。


三、动手实践:搭建三节点 Swarm 集群 🛠️

我们将在一台 Linux 主机(或 Docker Desktop 的 Linux 子系统)上模拟三节点集群。实际生产中,各节点为独立云服务器或物理机。

⚠️ 注意:以下命令均在 同一台机器的三个终端窗口中执行,通过不同监听端口区分节点(避免端口冲突)。真实部署时,请替换为各节点真实 IP。

步骤 1:初始化 Manager 节点

打开终端 1,执行:

# 初始化 Swarm,指定监听地址为本机所有接口,端口 2377(Swarm 默认通信端口)
docker swarm init –advertise-addr 127.0.0.1:2377

# 输出示例:
# Swarm initialized: current node (xxxxx) is now a manager.
# To add a worker to this swarm, run the following command:
# docker swarm join –token SWMTKN-1-abc…xyz 127.0.0.1:2377
# To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

记下 docker swarm join … 命令(含 token),稍后用于添加 worker。

验证集群状态:

docker node ls
# 输出应显示:
# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
# xxxxxxxx * moby Ready Active Leader 24.0.7

* 表示当前节点,Leader 表示它是主管理节点。

步骤 2:添加两个 Worker 节点

打开终端 2,执行(注意:需在 同一台机器,但使用不同 Docker 守护进程端口模拟独立节点):

# 启动第二个 Docker 守护进程(绑定到 2376 端口)
dockerd -H tcp://127.0.0.1:2376 -H unix:///var/run/docker.sock2 –data-root /var/lib/docker2 &

# 切换 Docker CLI 到新守护进程
export DOCKER_HOST=tcp://127.0.0.1:2376

# 加入 Swarm(粘贴上一步获得的 join 命令,去掉 –advertise-addr)
docker swarm join –token SWMTKN-1-abc...xyz 127.0.0.1:2377

终端 3 同理(使用端口 2375):

# 启动第三个守护进程
dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock3 –data-root /var/lib/docker3 &

# 切换 CLI
export DOCKER_HOST=tcp://127.0.0.1:2375

# 加入 Swarm
docker swarm join –token SWMTKN-1-abc...xyz 127.0.0.1:2377

💡 真实环境中,你只需在每台目标服务器上运行 docker swarm join … 即可,无需手动启停 dockerd。

回到终端 1(manager),查看完整节点列表:

docker node ls

你将看到类似输出:

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxx123456 * node-1 Ready Active Leader 24.0.7
yyy789012 node-2 Ready Active 24.0.7
zzz345678 node-3 Ready Active 24.0.7

✅ 三节点 Swarm 集群已就绪!接下来,我们部署一个真实的 Java 应用。


四、Java 示例:Spring Boot 微服务集群化 🌱

我们将构建一个极简但完整的微服务:user-service,它提供用户 CRUD 接口,并连接 PostgreSQL 数据库。整个栈将通过 Swarm 编排,体现服务发现、滚动更新、健康检查等能力。

4.1 项目结构概览

user-service/
├── src/main/java/com/example/userservice/
│ ├── UserServiceApplication.java # Spring Boot 启动类
│ ├── controller/UserController.java # REST API
│ ├── entity/User.java # JPA 实体
│ └── repository/UserRepository.java # Spring Data JPA
├── src/main/resources/application.yml # 配置文件(含数据库连接)
├── Dockerfile # 构建镜像
└── docker-compose.yml # Swarm 部署描述(v3.8)

4.2 核心 Java 代码详解

✅ UserController.java —— 提供 /users 接口

package com.example.userservice.controller;

import com.example.userservice.entity.User;
import com.example.userservice.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private UserRepository userRepository;

// GET /users —— 查询所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userRepository.findAll());
}

// POST /users —— 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userRepository.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}

// GET /users/{id} —— 根据 ID 查询
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}

✅ User.java —— JPA 实体(带 Lombok 简化)

package com.example.userservice.entity;

import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

@Column(unique = true, nullable = false)
private String email;
}

✅ application.yml —— 关键配置(适配 Swarm 环境)

spring:
application:
name: user–service

datasource:
url: jdbc:postgresql://postgres:5432/userdb # 👈 关键!使用服务名 postgres,Swarm DNS 自动解析
username: ${POSTGRES_USER:postgres}
password: ${POSTGRES_PASSWORD:password}
driver-class-name: org.postgresql.Driver

jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true

server:
port: 8080

# 启用 Actuator 健康端点(Swarm 用作健康检查)
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always

🔍 重点说明:jdbc:postgresql://postgres:5432/… 中的 postgres 是数据库服务名,不是 IP 地址!Swarm 内置 DNS 会将该名称解析为 postgres 服务当前运行的任意 Task 的 IP。这意味着:即使数据库容器重启迁移,Java 应用完全无感,连接自动重试 ✅。

4.3 Dockerfile —— 构建可移植镜像

# 使用官方 Spring Boot 运行时镜像(轻量、安全)
FROM springio/spring-boot-jre17:3.1.0

# 设置工作目录
WORKDIR /app

# 复制打包好的 JAR(假设 mvn clean package 后生成 target/user-service-0.0.1-SNAPSHOT.jar)
COPY target/user-service-0.0.1-SNAPSHOT.jar app.jar

# 暴露端口(仅声明,Swarm 会处理实际映射)
EXPOSE 8080

# 启动应用
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/app.jar"]

构建镜像(在项目根目录执行):

mvn clean package -DskipTests
docker build -t user-service:1.0 .

✅ 镜像构建完成,下一步用 docker-compose.yml 描述整个 Stack。


五、Swarm Stack 部署:docker-compose.yml 详解 🧱

Swarm 使用 docker stack deploy 命令部署 Compose v3 文件。以下 docker-compose.yml 定义了完整的 user-service 栈,包含数据库、应用服务、网络与健康检查:

version: '3.8'

services:

# PostgreSQL 数据库服务(global 模式,仅需 1 实例)
postgres:
image: postgres:15–alpine
environment:
POSTGRES_DB: userdb
POSTGRES_USER: ${POSTGRES_USER:–postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:–password}
volumes:
– postgres_data:/var/lib/postgresql/data
networks:
– user_net
deploy:
placement:
constraints:
– node.role == manager # 约束:仅在 manager 节点运行(可选,增强安全性)
resources:
limits:
memory: 512M
cpus: '0.5'

# user-service 应用(replicated 模式,3 个副本)
user-service:
image: user–service:1.0
ports:
– "8080:8080" # 将宿主机 8080 映射到服务 ingress 端口
environment:
POSTGRES_USER: ${POSTGRES_USER:–postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:–password}
networks:
– user_net
depends_on:
– postgres
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
monitor: 30s
restart_policy:
condition: on–failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 512M
cpus: '0.5'
# 健康检查:Swarm 用此判断 Task 是否存活
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

volumes:
postgres_data:

networks:
user_net:
driver: overlay
attachable: true # 允许非 Swarm 容器(如调试容器)加入

✅ 关键特性说明:

  • depends_on: 声明启动顺序(PostgreSQL 先于 user-service);
  • deploy.replicas: 3: 创建 3 个 user-service 容器,Swarm 自动分发到 worker 节点;
  • update_config: 定义滚动更新策略(每次更新 1 个,间隔 10 秒,失败则回滚);
  • healthcheck: 每 30 秒调用 /actuator/health,连续 3 次失败则标记 Task 为 unhealthy 并重启;
  • networks.user_net.driver: overlay: 创建跨主机通信网络,服务间通过服务名互访。

部署 Stack

在 Manager 节点终端执行:

# 设置环境变量(可选,用于覆盖 compose 中的默认值)
export POSTGRES_USER=admin
export POSTGRES_PASSWORD=secure123

# 部署名为 user-stack 的 Stack
docker stack deploy -c docker-compose.yml user-stack

查看服务状态:

docker service ls
# 输出:
# ID NAME MODE REPLICAS IMAGE
# abc123… user-stack_postgres replicated 1/1 postgres:15-alpine
# def456… user-stack_user-service replicated 3/3 user-service:1.0

查看 Task(容器实例)分布:

docker service ps user-stack_user-service
# 输出(示意):
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# ghi789… user-stack_user-service.1 user-service:1.0 node-2 Running Running 2 minutes ago
# jkl012… user-stack_user-service.2 user-service:1.0 node-3 Running Running 2 minutes ago
# mno345… user-stack_user-service.3 user-service:1.0 node-1 Running Running 2 minutes ago

✅ 3 个副本已均匀分布在三个节点上!现在,我们验证服务是否真正可用。


六、服务验证与流量观察 🌐

6.1 访问应用 API(通过 Swarm Ingress)

Swarm 为每个发布端口的服务自动配置 ingress 负载均衡器。你无需关心哪个 Task 在哪个节点,只需访问任意 manager 或 worker 的 IP + 端口:

# 本地测试(因我们在单机模拟,直接 curl)
curl http://localhost:8080/users
# 返回:[] (空数组,因数据库刚初始化)

# 创建一个用户
curl -X POST http://localhost:8080/users \\
-H "Content-Type: application/json" \\
-d '{"name":"Alice","email":"alice@example.com"}'

# 再次查询
curl http://localhost:8080/users
# 返回:[{"id":1,"name":"Alice","email":"alice@example.com"}]

✅ 服务正常响应!Swarm 的 ingress 网络将请求自动路由到任一健康的 user-service Task。

6.2 观察服务发现:DNS 解析验证

进入某个 user-service 容器内部,验证能否通过服务名访问 PostgreSQL:

# 获取一个 user-service 容器 ID
docker ps –filter "name=user-stack_user-service" –format "{{.ID}}" | head -1

# 进入容器执行 nslookup
docker exec -it <CONTAINER_ID> sh -c "nslookup postgres"

输出应类似:

Server: 127.0.0.11
Address 1: 127.0.0.11

Name: postgres
Address 1: 10.0.1.3 postgres.user-stack_user_net

✅ 10.0.1.3 是 postgres 服务当前 Task 的 overlay 网络 IP。Swarm 内置 DNS(127.0.0.11)实时解析服务名,无需修改 /etc/hosts 或硬编码 IP。

6.3 查看健康状态与日志

# 查看 user-service 服务整体健康状态
docker service inspect user-stack_user-service –format='{{json .Endpoint.Spec.Ports}}'

# 查看某 Task 日志(实时)
docker service logs -f user-stack_user-service.1

# 查看所有 Task 健康详情
docker service ps –no-trunc user-stack_user-service

你将看到 CURRENT STATE 列显示 Running 5 minutes ago 或 Health check passed,证明健康检查机制生效。


七、高级能力实战:滚动更新与故障自愈 🔄

Swarm 最强大的价值在于其声明式、自动化的运维能力。我们来演示两个关键场景。

场景 1:无缝滚动更新(Rolling Update)

假设我们要发布 user-service v1.1(修复一个 bug),只需构建新镜像并更新服务:

# 1. 修改 Java 代码(例如在 UserController 中加一行日志)
# 2. 重新打包
mvn clean package -DskipTests

# 3. 构建新镜像
docker build -t user-service:1.1 .

# 4. 更新服务(Swarm 自动执行滚动更新)
docker service update \\
–image user-service:1.1 \\
–update-parallelism 1 \\
–update-delay 10s \\
user-stack_user-service

Swarm 将按如下步骤执行:

  • 停止 user-stack_user-service.1(第一个 Task);
  • 拉取 user-service:1.1 镜像并启动新 Task;
  • 等待其健康检查通过(/actuator/health 返回 200);
  • 重复步骤 1-3,直至所有 3 个副本更新完毕。
  • 🔗 原理深入:整个过程由 Swarm Manager 的 scheduler 和 orchestrator 组件协同完成,状态变更通过 Raft 日志同步到所有 manager 节点,保证强一致性。详细机制可参考 Docker Swarm Architecture。

    场景 2:模拟节点宕机,验证自愈能力

    在终端中,手动停止一个 worker 节点的 Docker 守护进程(模拟服务器宕机):

    # 在运行 node-2 的终端中(假设是终端 2)
    pkill -f "dockerd.*2376"

    回到 Manager 终端,观察:

    docker node ls
    # node-2 状态变为 Down

    docker service ps user-stack_user-service
    # 你会看到:user-stack_user-service.1 的 CURRENT STATE 变为 "Failed" 或 "Rejected"
    # 几秒后,Swarm 自动在剩余健康节点(node-1 或 node-3)上创建新 Task 替代它!
    # 状态变为:Running 10 seconds ago

    ✅ 服务始终维持 3/3 副本,用户请求无感知中断。这就是生产环境必需的**故障自愈(Self-healing)**能力。


    八、安全与生产就绪要点 🔐

    Swarm 开箱即用,但生产部署需关注以下安全实践:

    8.1 TLS 加密通信(默认启用)

    Swarm 节点间通信(2377 端口)默认启用 TLS,证书由 manager 自动生成并分发。你无需手动配置,但可验证:

    docker node inspect self –format='{{.Status.Addr}}' # 查看节点 IP
    # 所有节点间通信自动加密,防止中间人攻击。

    8.2 Secrets 管理(替代环境变量)

    避免将密码明文写入 docker-compose.yml。使用 Swarm Secrets 安全注入:

    # 创建 secret(内容来自文件或 stdin)
    echo "my-super-secret-password" | docker secret create db_password –

    # 在 compose 中引用
    services:
    user-service:
    secrets:
    – db_password
    secrets:
    db_password:
    external: true

    应用内通过挂载路径 /run/secrets/db_password 读取,文件权限为 0400,仅容器内 root 可读。

    8.3 资源限制与隔离

    deploy.resources.limits 已在前文 docker-compose.yml 中配置。务必设置,防止某个服务耗尽节点资源影响全局:

    resources:
    limits:
    memory: 512M
    cpus: '0.5'
    reservations:
    memory: 256M
    cpus: '0.25'

    reservations 保证最低资源,limits 设定硬上限,双保险。

    8.4 日志集中收集(对接 ELK / Loki)

    Swarm 支持多种日志驱动。推荐使用 fluentd 或 loki:

    deploy:
    logging:
    driver: "fluentd"
    options:
    fluentd-address: "localhost:24224"
    tag: "user-service"

    🔗 最佳实践参考:Docker 官方《Production Best Practices》指南提供了全面的安全加固清单,详见 Docker Security Documentation。


    九、Swarm vs Kubernetes:何时切换?⚖️

    我们不鼓吹“银弹”,而是理性分析演进路径:

    场景推荐方案理由
    团队 < 5 人,服务 < 10 个,QPS < 1k ✅ Swarm 运维成本趋近于零,CI/CD 流水线简洁,扩容只需 docker service scale
    需要 GPU 加速训练任务 ❌ Swarm ➜ ✅ Kubernetes Swarm 不原生支持 Device Plugin,K8s 有成熟 NVIDIA GPU Operator
    多租户隔离、RBAC 精细控制 ❌ Swarm ➜ ✅ Kubernetes Swarm RBAC 功能有限(仅 manager/worker 角色),K8s 提供 namespace + RoleBinding + ServiceAccount 全链路控制
    已有大量 Helm Charts ❌ Swarm ➜ ✅ Kubernetes Helm 是 K8s 生态标准,Swarm 无对应包管理器(虽有第三方 port,但非官方)
    边缘设备(树莓派、Jetson)集群 ✅ Swarm 资源占用低,ARM 支持完善,docker swarm join 一行命令入网

    💡 务实建议:从 Swarm 启步,当业务增长触及它的边界时,再平滑迁移到 Kubernetes。两者都遵循 OCI 标准,容器镜像、Dockerfile、网络模型高度一致,迁移成本远低于从传统 VM 迁移。


    十、常见问题与排错指南 ❓

    Q1:docker service ps 显示 Rejected 或 Failed,如何排查?

    ✅ 四步法:

  • docker service logs <SERVICE_NAME> —— 查看错误日志(如数据库连接超时);
  • docker service inspect <SERVICE_NAME> —— 检查 Endpoint, UpdateStatus, Healthcheck 配置;
  • docker node ls —— 确认节点状态为 Ready;
  • 进入容器 docker exec -it <TASK_CONTAINER> sh,手动测试依赖服务连通性(如 ping postgres, telnet postgres 5432)。
  • Q2:服务启动后无法通过 localhost:8080 访问?

    ✅ 检查:

    • docker service ls 中 PORTS 列是否显示 *:8080->8080/tcp;
    • 防火墙是否放行 2377(Swarm)、7946(节点发现)、4789(overlay 网络)端口;
    • 若在云服务器,安全组需开放 8080 入站。

    Q3:如何让 Swarm 服务被外部域名访问(如 api.myapp.com)?

    ✅ Swarm 本身不提供七层路由。你需要:

    • 方案 A:在 Swarm 前部署 Nginx / HAProxy,配置反向代理到 localhost:8080;
    • 方案 B:使用 Traefik(官方推荐的 Swarm 友好型反向代理),通过标签自动发现服务;

      🔗 Traefik 官方 Swarm 文档:Traefik & Docker Swarm

    Q4:docker stack deploy 报错 invalid reference format?

    ✅ 大概率是镜像名含非法字符(如 _)或未打 tag。确保:

    • 镜像名全小写、仅含 – 和 .;
    • docker images 中存在该镜像及 tag;
    • 若用私有仓库,提前 docker login。

    十一、结语:拥抱简单,始于 Swarm 🌈

    Docker Swarm 不是一个过时的技术,而是一种清醒的工程选择。它拒绝过度设计,在“足够好”与“面面俱到”之间划出一条优雅的界限。当你看到 docker swarm init 启动集群、docker service create 部署服务、docker service scale 瞬间扩容——那种掌控感与确定性,正是工程师最珍视的体验 🛠️。

    本文从概念到代码,从单节点到三节点,从静态部署到滚动更新,为你铺就了一条通往生产级容器编排的坚实路径。你已掌握:

    • Swarm 的核心抽象(Node/Service/Task/Network);
    • Spring Boot 应用容器化与集群化改造;
    • 声明式部署(docker-compose.yml)与运维(scale/update/logs);
    • 健康检查、自愈、安全(Secrets/TLS)等生产必备能力。

    下一步,你可以:

    • 将本文的 user-service 扩展为 order-service、payment-service,构建完整微服务链;
    • 集成 Prometheus + Grafana 监控 Swarm 集群指标;
    • 使用 Portainer(开源 GUI)可视化管理 Swarm(Portainer Official Site);
    • 探索 Swarm 的 config 对象,实现配置中心化管理。

    🌟 最后赠言:技术的价值不在于它有多炫酷,而在于它能否让你更快交付价值、更少陷入运维泥潭。Docker Swarm,就是那个默默托起你业务、却从不抢镜的可靠伙伴。

    愿你在云原生的海洋中,既拥有 Kubernetes 的星辰大海,也珍惜 Swarm 的静水流深 🌊。


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:171主机测评 » Docker - Docker Swarm初识,搭建容器集群
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址