『宝藏代码胶囊开张啦!』—— 我的 CodeCapsule 来咯!✨写代码不再头疼!我的新站点 CodeCapsule 主打一个 “白菜价”+“量身定制”!无论是卡脖子的毕设/课设/文献复现,需要灵光一现的算法改进,还是想给项目加个“外挂”,这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网

Docker Swarm集群管理:从单机到生产级容器编排

一、引言:容器编排的进化之路

在掌握了Docker Compose多服务编排、容器网络存储、监控日志等技能后,你已经能够熟练地在单台主机上运行复杂的容器化应用。然而,生产环境的挑战远不止于此:单点故障如何避免?流量突增如何快速扩展?服务更新如何做到零停机?容器异常如何自动恢复?

这些问题将我们引向容器编排的下一阶段——集群管理。Docker Swarm作为Docker原生内置的集群管理工具,能够将多台Docker主机组合成一个虚拟的、强大的容器集群,提供服务发现、负载均衡、弹性伸缩、滚动更新等生产级特性。与Kubernetes相比,Swarm的学习曲线平缓、与Docker CLI无缝集成,是中小团队迈向容器化生产的理想起点。

本文将通过一个 Python Flask投票应用 的实战,从零搭建Docker Swarm集群,深入讲解节点管理、服务部署、滚动更新、弹性伸缩等核心操作,并结合多阶段构建等镜像优化技巧,帮助你掌握生产级容器编排的核心技能。

二、核心概念:Swarm集群架构解析

2.1 什么是Docker Swarm?

Docker Swarm是Docker原生的容器集群管理工具,从Docker 1.12版本开始内置在Docker引擎中。它将多台Docker主机抽象为一个统一的虚拟主机,用户可以通过与操作单台Docker相同的命令来管理整个集群。

Docker Swarm集群

Raft共识

Raft共识

调度

调度

调度

运行任务

运行任务

运行任务

访问服务

访问服务

分发请求

分发请求

分发请求

Manager节点
Leader

Manager节点
Follower

Manager节点
Follower

Worker节点1

Worker节点2

Worker节点3

容器

容器

容器

用户/客户端

内置负载均衡

2.2 核心概念

节点(Node):集群中的每个Docker主机被称为一个节点,分为两种角色:

  • Manager节点:负责集群的管理与控制,维护集群状态、调度服务。生产环境建议部署奇数个Manager(如3个)以确保高可用。
  • Worker节点:用于运行实际的任务(容器),由Manager节点分配任务并执行。

服务(Service):在Swarm集群中部署的最小单元,定义了容器镜像、副本数量、网络、端口等信息。一个服务对应一个微服务组件。

任务(Task):服务的具体实例,由Manager节点调度到Worker节点上运行。每个任务对应一个容器。

2.3 工作原理

Swarm集群的核心是Raft共识协议:Manager节点通过Raft选举产生Leader,所有管理操作(如服务创建、节点加入)都经过Leader处理,并在Manager节点间同步,确保集群状态一致。Leader故障时,其他Manager会选举出新的Leader,整个过程对用户透明。

2.4 与Docker Compose的对比

特性 Docker Compose Docker Swarm
适用场景 单主机开发测试 多主机生产集群
高可用 不支持 支持(节点故障自动迁移)
弹性伸缩 需手动调整 支持动态扩缩容
滚动更新 不支持 支持零停机更新
内置负载均衡 不支持 支持(ingress网络)
跨主机通信 需额外配置 内置overlay网络

三、实战演练:搭建高可用投票应用集群

本节将搭建一个包含 Flask Web服务Redis缓存 的投票应用,并在Swarm集群中实现多副本部署、负载均衡和滚动更新。

3.1 环境准备

准备三台安装Docker的Linux主机(物理机、虚拟机或云主机):

角色 主机名 IP地址
Manager1 manager1 192.168.1.10
Worker1 worker1 192.168.1.20
Worker2 worker2 192.168.1.21

确保主机间网络互通,并开放以下端口:

  • 2377/tcp:集群管理通信
  • 7946/tcp/udp:节点间发现
  • 4789/udp:overlay网络数据平面

3.2 项目结构

swarm-vote-app/
├── app/
│   ├── app.py              # Flask投票应用
│   ├── requirements.txt    # Python依赖
│   ├── Dockerfile          # 多阶段构建
│   └── .dockerignore
└── docker-stack.yml        # Swarm stack文件

3.3 Flask应用代码

app/app.py:实现投票接口,数据存储在Redis中。

import os
import redis
import socket
from flask import Flask, request, jsonify

app = Flask(__name__)

# 从环境变量读取配置
REDIS_HOST = os.getenv('REDIS_HOST', 'redis')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', '')

# 初始化Redis连接
redis_client = redis.Redis(
    host=REDIS_HOST,
    port=REDIS_PORT,
    password=REDIS_PASSWORD if REDIS_PASSWORD else None,
    db=0,
    decode_responses=True
)

@app.route('/')
def index():
    """返回服务信息(包含主机名,便于验证负载均衡)"""
    hostname = socket.gethostname()
    return jsonify({
        'service': 'Vote API',
        'hostname': hostname,
        'endpoints': {
            'GET /votes': '查看当前投票结果',
            'POST /vote': '投票,参数: {"option": "A"}'
        }
    })

@app.route('/votes', methods=['GET'])
def get_votes():
    """从Redis获取当前投票结果"""
    total_a = int(redis_client.get('vote:A') or 0)
    total_b = int(redis_client.get('vote:B') or 0)
    return jsonify({'A': total_a, 'B': total_b})

@app.route('/vote', methods=['POST'])
def vote():
    """投票接口"""
    data = request.get_json()
    option = data.get('option')
    if option not in ['A', 'B']:
        return jsonify({'error': 'Invalid option'}), 400

    # 更新Redis计数器
    redis_client.incr(f'vote:{option}')
    total_a = int(redis_client.get('vote:A') or 0)
    total_b = int(redis_client.get('vote:B') or 0)
    return jsonify({'message': f'Voted for {option}', 'current': {'A': total_a, 'B': total_b}})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

app/requirements.txt

flask==2.3.3
redis==5.0.1
gunicorn==21.2.0

3.4 多阶段构建Dockerfile

# app/Dockerfile
# 构建阶段
FROM python:3.11-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# 运行阶段
FROM python:3.11-slim

# 创建非root用户
RUN addgroup --system --gid 1001 appgroup && \
    adduser --system --uid 1001 --gid 1001 --no-create-home appuser

WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

COPY . .
RUN chown -R appuser:appgroup /app

USER appuser
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/')" || exit 1

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]

app/.dockerignore

__pycache__
*.pyc
.env
.git
README.md
Dockerfile
.dockerignore

3.5 构建镜像并推送到仓库

在Manager节点上构建镜像并推送到私有仓库(如Docker Hub或Harbor):

# 构建镜像
docker build -t your-registry/vote-app:latest ./app

# 推送到仓库
docker push your-registry/vote-app:latest

3.6 初始化Swarm集群

在Manager节点上执行:

# 初始化Swarm(指定Manager节点的IP)
docker swarm init --advertise-addr 192.168.1.10

# 查看节点列表
docker node ls

# 查看加入Worker节点的命令
docker swarm join-token worker

输出示例:

Swarm initialized: current node (dxn1...znjz) is now a manager.

To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-49nj1cmql...-9v2pg7qfh7cgug4y 192.168.1.10:2377

在Worker节点上执行加入命令:

# Worker1
docker swarm join --token SWMTKN-1-49nj1cmql...-9v2pg7qfh7cgug4y 192.168.1.10:2377

# Worker2
docker swarm join --token SWMTKN-1-49nj1cmql...-9v2pg7qfh7cgug4y 192.168.1.10:2377

在Manager节点验证集群状态:

docker node ls
ID                            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS
dxn1...znjz *                  manager1    Ready     Active         Leader
ps2a...mngt                    worker1     Ready     Active
rt3b...klmn                    worker2     Ready     Active

3.7 创建overlay网络

# 创建用于服务通信的overlay网络
docker network create --driver overlay --attachable vote-network

3.8 部署服务

方式一:使用命令行部署

# 部署Redis服务(global模式:每个节点运行一个副本)
docker service create --name redis \
  --network vote-network \
  --mode global \
  --mount type=volume,source=redis-data,target=/data \
  redis:7-alpine

# 部署投票应用(replicated模式:运行3个副本)
docker service create --name vote-app \
  --network vote-network \
  --replicas 3 \
  -p 5000:5000 \
  --health-cmd "curl -f http://localhost:5000/ || exit 1" \
  --health-interval 10s \
  --health-retries 3 \
  --update-delay 10s \
  --update-parallelism 1 \
  your-registry/vote-app:latest

方式二:使用Stack文件部署(推荐)

创建docker-stack.yml

version: '3.8'

services:
  redis:
    image: redis:7-alpine
    networks:
      - vote-network
    volumes:
      - redis-data:/data
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == worker  # 只在worker节点运行
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  vote-app:
    image: your-registry/vote-app:latest
    networks:
      - vote-network
    ports:
      - "5000:5000"
    environment:
      REDIS_HOST: redis
      REDIS_PORT: 6379
    deploy:
      mode: replicated
      replicas: 3
      update_config:
        parallelism: 1        # 每次更新1个副本
        delay: 10s            # 更新间隔
        order: start-first    # 先启动新副本再停止旧副本
      rollback_config:
        parallelism: 0         # 同时回滚所有副本
        order: stop-first
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/"]
      interval: 30s
      timeout: 10s
      retries: 3
    depends_on:
      - redis

networks:
  vote-network:
    external: true

volumes:
  redis-data:

部署stack:

# 部署stack
docker stack deploy -c docker-stack.yml vote

# 查看stack中的服务
docker stack services vote

# 查看stack中的任务
docker stack ps vote

# 查看服务日志
docker service logs vote_vote-app

3.9 验证服务

# 查看服务列表
docker service ls

# 查看服务详情
docker service ps vote_vote-app

# 多次访问应用,验证负载均衡(返回不同hostname)
curl http://任意节点IP:5000/

四、进阶优化:Swarm集群生产级实践

4.1 服务模式选择

Swarm提供两种服务模式,各有适用场景:

模式 命令参数 适用场景
replicated --replicas N 无状态应用(Web、API),需要水平扩展
global --mode global 每个节点必须运行的任务(日志收集、监控代理)

4.2 调度约束

通过placement constraints控制服务运行在特定节点:

# 给节点添加标签
docker node update --label-add storage=ssd worker1

# 创建服务时指定约束
docker service create \
  --constraint 'node.labels.storage == ssd' \
  --constraint 'node.role == worker' \
  --replicas 2 \
  nginx

4.3 滚动更新与回滚

滚动更新是Swarm的核心特性,允许在不中断服务的情况下升级应用:

# 更新服务镜像版本
docker service update \
  --image your-registry/vote-app:v2 \
  --update-parallelism 2 \
  --update-delay 30s \
  vote_vote-app

# 查看更新状态
docker service ps vote_vote-app

# 回滚到上一个版本
docker service rollback vote_vote-app

4.4 弹性伸缩

根据负载动态调整服务副本数:

# 扩容到5个副本
docker service scale vote_vote-app=5

# 或者使用update命令
docker service update --replicas 5 vote_vote-app

4.5 数据持久化

在Swarm集群中,数据卷需要跨节点共享,推荐使用:

  • NFS卷:将NFS共享目录挂载到容器
  • 云存储驱动:如Rex-Ray、Portworx
  • 分布式存储:Ceph、GlusterFS
services:
  mysql:
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:
    driver: rexray/ebs  # 使用云存储驱动

4.6 安全加固

  • 启用集群锁定:防止未授权访问
# 启用自动锁定
docker swarm update --autolock=true

# 查看解锁密钥
docker swarm unlock-key

# 重启后解锁集群
docker swarm unlock
  • 使用加密网络
docker network create --driver overlay --opt encrypted secure-network

4.7 多阶段构建收益

与之前系列一致,多阶段构建将镜像体积从412MB优化至82MB,大幅提升分发速度。

五、效果验证

5.1 服务高可用验证

# 查看服务分布
docker service ps vote_vote-app

# 模拟worker1节点故障
ssh worker1 "systemctl stop docker"

# 等待片刻后再次查看,Swarm会在其他节点自动重建副本
docker service ps vote_vote-app

5.2 负载均衡验证

# 连续访问10次,观察返回的hostname
for i in {1..10}; do curl -s http://192.168.1.10:5000/ | grep hostname; done

预期输出会交替出现三个worker节点的hostname。

5.3 滚动更新验证

# 更新镜像并观察更新过程
docker service update --image your-registry/vote-app:v2 vote_vote-app

# 在另一个终端实时监控
watch docker service ps vote_vote-app

5.4 镜像体积对比

构建方式 基础镜像 最终镜像大小
单阶段 python:3.11 912 MB
单阶段 slim python:3.11-slim 412 MB
多阶段 python:3.11-slim 82 MB

六、完整代码

6.1 app/app.py

(见上文)

6.2 app/requirements.txt

flask==2.3.3
redis==5.0.1
gunicorn==21.2.0

6.3 app/Dockerfile

(见上文)

6.4 app/.dockerignore

(见上文)

6.5 docker-stack.yml

(见上文)

七、总结与最佳实践清单

通过本文的实战,我们从零搭建了一个三节点的Docker Swarm集群,部署了高可用的投票应用,并实践了滚动更新、弹性伸缩等生产级特性。以下是 Swarm集群管理的最佳实践清单

  • Manager节点高可用:部署奇数个Manager(3个或5个),确保Raft共识可用。
  • 网络规划:提前规划overlay网络,服务间通信使用服务名而非IP。
  • 服务约束:利用节点标签和placement constraints精细控制服务分布。
  • 滚动更新策略:设置合理的并行度和延迟,确保更新过程平滑。
  • 健康检查:为每个服务配置健康检查,让Swarm自动管理任务状态。
  • 资源限制:为服务设置CPU/内存限制,避免资源争抢。
  • 数据持久化:有状态服务使用共享存储或云存储驱动。
  • 安全加固:启用集群锁定,使用加密网络,定期更新镜像。
  • 监控集成:配合Prometheus等工具监控集群健康状态。

Docker Swarm以其简单易用、与Docker原生集成的特性,成为中小团队迈向容器化生产的理想选择。当你的集群规模进一步扩大(超过数十个节点),或需要更复杂的编排能力(如CRD、Operator)时,可以平滑迁移到Kubernetes。但Swarm中积累的容器化、服务抽象、滚动更新等理念,将成为你云原生之路的宝贵财富。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐