Docker Swarm集群管理
本文介绍了Docker Swarm集群管理的核心概念与实践应用。通过搭建高可用投票应用集群的实战演练,展示了从单机容器编排到生产级集群管理的演进过程。文章详细解析了Swarm架构、核心组件(Manager/Worker节点)和工作原理,并与Docker Compose进行了功能对比。实战部分包含Flask应用开发、多阶段构建Docker镜像优化,以及通过docker-stack.yml文件实现多副
目录
『宝藏代码胶囊开张啦!』—— 我的 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相同的命令来管理整个集群。
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中积累的容器化、服务抽象、滚动更新等理念,将成为你云原生之路的宝贵财富。
更多推荐

所有评论(0)