Docker swarm集群搭建实战
下面是一个完整、可落地的 Docker Swarm 项目实战示例,涵盖:
- 15 台服务器节点规划
- Swarm 集群初始化与节点加入
- 自研 Web 应用(Python Flask)开发 + Docker 镜像构建
- 使用私有镜像仓库(本地 registry)避免依赖公网
- 通过
docker stack deploy一键部署包含 Nginx、Web App、PostgreSQL 的完整应用栈 - 精确控制服务调度(nginx/db 专用节点)
🧩 一、节点规划(共 15 节点)
| 类型 | 数量 | 主机名前缀 | 用途 |
|---|---|---|---|
| Manager | 3 | manager-1 ~ manager-3 |
集群管理(不跑应用) |
| Worker - Nginx | 3 | nginx-1 ~ nginx-3 |
仅运行 Nginx |
| Worker - DB | 2 | db-1, db-2 |
仅运行 PostgreSQL |
| Worker - App | 7 | app-1 ~ app-7 |
运行你的 Web 应用(也可让 nginx/db 节点兼跑,但本例隔离) |
✅ 所有节点操作系统:Ubuntu 22.04,已安装 Docker Engine ≥ 24.0
🌐 二、网络与端口要求
确保以下端口在所有节点间互通:
2377/tcp:Swarm 管理7946/tcp+udp:节点通信4789/udp:Overlay 网络(VXLAN)5000/tcp:本地私有镜像仓库(可选)
🔨 三、步骤 1:在 manager-1 初始化 Swarm
bash
# 在 manager-1 (IP: 192.168.1.101) 上执行
docker swarm init --advertise-addr 192.168.1.101
记下输出的 worker join token,例如:
text
docker swarm join --token SWMTKN-1-abc...xyz 192.168.1.101:2377
👥 四、步骤 2:加入其他节点
1. 加入另外 2 个 Manager(高可用)
在 manager-2 和 manager-3 上执行(先获取 manager token):
bash
# 在 manager-1 上运行:
docker swarm join-token manager
# 得到类似命令,在 manager-2/3 上执行
2. 所有 12 个 Worker 加入集群
在 每个 Worker 节点(nginx-1~3, db-1~2, app-1~7)上执行:
bash
docker swarm join --token SWMTKN-1-abc...xyz 192.168.1.101:2377
验证:
bash
# 在任意 Manager 上
docker node ls
# 应看到 15 个节点(3 Manager + 12 Worker),状态 Ready
🏷️ 五、步骤 3:给节点打标签(用于调度控制)
bash
# 标记 Nginx 节点
for i in {1..3}; do
docker node update --label-add role=nginx nginx-$i
done
# 标记 DB 节点
docker node update --label-add role=db db-1
docker node update --label-add role=db db-2
# (可选)标记 App 节点(本例中 app 服务不限制,可跑在任意非-manager 节点)
# 如果想限制,也可打 label: role=app
📦 六、步骤 4:构建你的 Web 应用程序(Python Flask)
1. 创建项目目录
bash
mkdir my-web-app && cd my-web-app
2. 编写 app.py
python
# app.py
from flask import Flask
import psycopg2
import os
app = Flask(__name__)
@app.route('/health')
def health():
return {"status": "ok", "service": "my-web-app"}
@app.route('/api/db')
def test_db():
try:
conn = psycopg2.connect(
host=os.getenv("DB_HOST", "db"),
user="postgres",
password=os.getenv("DB_PASSWORD", "mysecretpassword"),
dbname="myapp"
)
cur = conn.cursor()
cur.execute("SELECT NOW();")
now = cur.fetchone()[0]
cur.close()
conn.close()
return {"status": "db connected", "time": str(now)}
except Exception as e:
return {"error": str(e)}, 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
3. requirements.txt
flask==3.0.0
psycopg2-binary==2.9.9
gunicorn==22.0.0
4. Dockerfile
dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--workers", "2", "--bind", "0.0.0.0:5000", "app:app"]
🖼️ 七、步骤 5:构建并推送镜像(使用本地私有 Registry)
💡 为避免依赖 Docker Hub,我们在
manager-1上启动一个本地 registry。
1. 启动本地镜像仓库(在 manager-1)
bash
docker run -d -p 5000:5000 --name registry registry:2
2. 构建并推送镜像
bash
# 构建(注意 tag 为 localhost:5000/my-web-app:v1)
docker build -t localhost:5000/my-web-app:v1 .
# 推送
docker push localhost:5000/my-web-app:v1
3. 在所有 Worker 节点配置信任(因是 HTTP registry)
在 每个 Worker 节点 的 /etc/docker/daemon.json 中添加:
json
{
"insecure-registries": ["192.168.1.101:5000"]
}
然后重启 Docker:
bash
sudo systemctl restart docker
✅ 现在所有节点都能拉取
192.168.1.101:5000/my-web-app:v1
📄 八、步骤 6:编写 stack.yml 应用栈
yaml
# stack.yml
version: '3.8'
services:
# PostgreSQL 数据库(仅在 db 节点)
db:
image: postgres:15
deploy:
replicas: 2
placement:
constraints:
- node.labels.role == db
restart_policy:
condition: on-failure
environment:
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: myapp
volumes:
- db_data:/var/lib/postgresql/data
networks:
- appnet
# 自研 Web 应用(可在任意 Worker 运行,不含 Manager)
app:
image: 192.168.1.101:5000/my-web-app:v1
deploy:
replicas: 6
restart_policy:
condition: on-failure
# 可选:避免调度到 Manager(默认不会,但显式更安全)
placement:
constraints:
- node.role == worker
environment:
DB_HOST: db
DB_PASSWORD: mysecretpassword
networks:
- appnet
depends_on:
- db
# Nginx 反向代理(仅在 nginx 节点)
web:
image: nginx:alpine
deploy:
replicas: 3
placement:
constraints:
- node.labels.role == nginx
restart_policy:
condition: on-failure
ports:
- target: 80
published: 80
protocol: tcp
mode: ingress
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- appnet
depends_on:
- app
volumes:
db_data:
networks:
appnet:
driver: overlay
attachable: true
⚙️ 九、步骤 7:准备 Nginx 配置
创建 nginx.conf(与 stack.yml 同目录):
nginx
events {
worker_connections 1024;
}
http {
upstream backend {
server app:5000; # Swarm DNS 自动负载均衡
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
🚀 十、步骤 8:部署整个应用栈
bash
# 确保当前目录有 stack.yml 和 nginx.conf
ls -l
# 部署(使用 --with-registry-auth 传递 registry 认证,虽本地无需,但好习惯)
docker stack deploy -c stack.yml --with-registry-auth myapp
🔍 十一、验证部署
1. 查看服务
bash
docker service ls
应看到:
myapp_app replicated 6/6 192.168.1.101:5000/my-web-app:v1
myapp_web replicated 3/3 nginx:alpine
myapp_db replicated 2/2 postgres:15
2. 检查任务分布
bash
docker service ps myapp_db # 应只在 db-1/db-2
docker service ps myapp_web # 应只在 nginx-1~3
docker service ps myapp_app # 应在任意 Worker(不含 Manager)
3. 测试外部访问
从你本地电脑访问任一节点 IP:
bash
curl http://192.168.1.111/health
# 返回: {"status": "ok", "service": "my-web-app"}
curl http://192.168.1.101/api/db
# 返回: {"status": "db connected", "time": "2026-02-05 08:30:00..."}
✅ 成功!你的自研 Web 服务已在 15 节点 Swarm 集群中运行!
🔄 十二、后续运维(可选)
更新 Web 应用
bash
# 修改代码 → 重新 build & push v2
docker build -t 192.168.1.101:5000/my-web-app:v2 .
docker push 192.168.1.101:5000/my-web-app:v2
# 滚动更新
docker service update --image 192.168.1.101:5000/my-web-app:v2 myapp_app
扩容
bash
docker service scale myapp_app=10
查看日志
bash
docker service logs myapp_app --tail 50 -f
✅ 总结:你完成了什么?
| 步骤 | 成果 |
|---|---|
| ✅ 集群搭建 | 3 Manager + 12 Worker,高可用 |
| ✅ 节点标签 | 精确控制 nginx/db 调度 |
| ✅ 自研应用 | Python Flask + PostgreSQL |
| ✅ 镜像管理 | 本地私有 registry,不依赖外网 |
| ✅ 网络通信 | Overlay 网络 + 内置 DNS |
| ✅ 外部访问 | Routing Mesh + Nginx 反向代理 |
| ✅ 一键部署 | docker stack deploy |
这个架构可直接用于中小型生产环境,具备弹性、高可用、易维护等云原生特性。
如需扩展为 Java/Node.js,只需替换 Dockerfile 和应用代码,其余编排逻辑完全通用!