1. 为什么选择 Docker Swarm?
Docker Swarm 是 Docker 原生的集群管理与服务编排工具,简单、轻量、开箱即用。如果你正在寻找一个既能快速上手,又能应对中小型生产环境的解决方案,Swarm 绝对值得一试。相比 Kubernetes 的复杂配置,Swarm 的学习曲线更平缓,但功能却一点不含糊:负载均衡、服务发现、滚动更新、高可用,全都能轻松搞定。
Swarm 的核心优势
原生集成:无需额外安装,Docker CLI 直接支持 Swarm 命令。
简单易用:几行命令就能搭建集群,适合快速部署。
生产可用:支持多节点高可用、自动故障转移。
灵活扩展:轻松增加节点,动态调整服务规模。
但它也不是万能的。如果你需要超大规模集群(比如上千节点)或复杂的 CRD(自定义资源定义),Kubernetes 可能更适合。不过,对于中小型团队,Swarm 的“简单即强大”哲学能让你省下不少心力。
2. 基础概念扫盲:Swarm 的核心组件
在动手之前,先搞清楚 Swarm 的几个关键概念,这样后续操作才能心中有数。
节点(Node):集群中的每台机器都是一个节点,分为 Manager(管理节点)和 Worker(工作节点)。管理节点负责调度任务、维护集群状态,工作节点跑具体的容器。
服务(Service):Swarm 的核心编排单位,定义了容器如何运行、需要几个副本、暴露哪些端口等。
任务(Task):服务的具体执行单位,通常是一个容器。
栈(Stack):通过 YAML 文件定义一组服务,类似 Kubernetes 的 Helm Chart,方便批量部署。
覆盖网络(Overlay Network):Swarm 提供的虚拟网络,节点之间通过它通信,天然支持服务发现。
小贴士:Swarm 的覆盖网络默认加密,通信安全有保障,但高负载场景下可能略影响性能,记得根据业务场景权衡!
3. 环境准备:打造你的 Swarm 试验田
动手之前,先准备好实验环境。以下是我推荐的配置,简单但足以模拟生产场景:
硬件与系统要求
机器:至少 3 台虚拟机或云服务器(1 台 Manager,2 台 Worker),每台建议 2 核 4GB 内存。
操作系统:Ubuntu 20.04 LTS 或 CentOS 8(其他 Linux 发行版也行,但确保内核支持 Docker)。
网络:确保节点间网络互通,防火墙开放以下端口:
TCP 2377:管理通信(加密)
TCP/UDP 7946:节点间通信
UDP 4789:覆盖网络数据传输
安装 Docker
在每台机器上安装最新版 Docker(以 Ubuntu 为例):
# 更新包索引
sudo apt-get update
# 安装依赖
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 添加 Docker 软件源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# 验证安装
docker --version
注意:确保所有节点的 Docker 版本一致,避免兼容性问题。可以用 docker version 检查。
配置节点
为便于管理,给每台机器起个好记的名字:
Manager 节点:swarm-manager
Worker 节点:swarm-worker1、swarm-worker2
# 设置主机名(以 swarm-manager 为例)
sudo hostnamectl set-hostname swarm-manager
4. 初始化 Swarm 集群
好了,准备工作就绪,接下来正式组建 Swarm 集群!
4.1 在 Manager 节点初始化
在 swarm-manager 上运行以下命令:
docker swarm init --advertise-addr <MANAGER_IP>
--advertise-addr 指定 Manager 的 IP 地址(用 ifconfig 或 ip addr 查看节点 IP)。执行后,你会看到类似以下输出:
Swarm initialized: current node (xxxx) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-xxxx <MANAGER_IP>:2377
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-2-xxxx <MANAGER_IP>:2377
复制 docker swarm join 的 Worker 令牌,准备加入 Worker 节点。
4.2 加入 Worker 节点
在 swarm-worker1 和 swarm-worker2 上运行 Manager 提供的 join 命令,例如:
docker swarm join --token SWMTKN-1-xxxx <MANAGER_IP>:2377
成功后,输出会提示节点已加入集群。
4.3 验证集群状态
回到 Manager 节点,运行:
docker node ls
你会看到类似以下输出:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
xxxx * swarm-manager Ready Active Leader
yyyy swarm-worker1 Ready Active
zzzz swarm-worker2 Ready Active
恭喜!你的 Swarm 集群已经搭建完成!接下来,我们要开始真正的服务编排了。
5. 部署第一个服务:从简单到复杂
让我们从一个简单的 Nginx 服务开始,逐步深入 Swarm 的服务编排功能。
5.1 部署单容器服务
在 Manager 节点运行:
docker service create --name my-nginx -p 8080:80 nginx:latest
这条命令做了啥?
--name:给服务命名。
-p 8080:80:将主机的 8080 端口映射到容器的 80 端口。
nginx:latest:使用最新的 Nginx 镜像。
运行后,用 docker service ls 查看服务状态:
ID NAME MODE REPLICAS IMAGE PORTS
xxxx my-nginx replicated 1/1 nginx:latest *:8080->80/tcp
访问 http://<任一节点IP>:8080,你会看到 Nginx 的欢迎页面!
5.2 扩展服务规模
假设流量增加,单容器不够用,我们扩展到 3 个副本:
docker service scale my-nginx=3
Swarm 会自动在集群中调度 3 个 Nginx 容器,可能分布在不同节点。用 docker service ps my-nginx 查看任务分布:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
xxxx my-nginx.1 nginx:latest swarm-manager Running Running 5 minutes ago
yyyy my-nginx.2 nginx:latest swarm-worker1 Running Running 4 minutes ago
zzzz my-nginx.3 nginx:latest swarm-worker2 Running Running 4 minutes ago
小技巧:Swarm 的调度器默认使用 spread 策略,尽量将任务均匀分布到节点上,减少单点压力。
5.3 更新服务:零停机滚动更新
假如你想更新 Nginx 到特定版本(比如 nginx:1.21),Swarm 支持无缝滚动更新:
docker service update --image nginx:1.21 my-nginx
Swarm 会逐个替换容器,确保服务不中断。你可以用 --update-delay 10s 设置更新间隔,避免更新过快。
6. 覆盖网络:让服务互联互通
Swarm 的覆盖网络是服务通信的秘密武器。让我们创建一个自定义网络,并部署两个服务验证通信。
6.1 创建覆盖网络
在 Manager 节点运行:
docker network create --driver overlay my-overlay-network
6.2 部署带网络的服务
假设我们要部署一个前端(Nginx)和后端(自定义 API)服务,它们通过 my-overlay-network 通信。
前端服务:
docker service create --name frontend \
--network my-overlay-network \
-p 8080:80 \
nginx:latest
后端服务(假设用一个简单的 Python Flask 应用): 先创建一个简单的 Flask 应用镜像(后续会详细讲如何构建):
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
RUN pip install flask
CMD ["python", "app.py"]
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Backend!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
构建并推送镜像到 Docker Hub(或其他镜像仓库):
docker build -t yourusername/my-flask-app:latest .
docker push yourusername/my-flask-app:latest
部署后端服务:
docker service create --name backend \
--network my-overlay-network \
yourusername/my-flask-app:latest
6.3 测试服务通信
在前端容器中访问后端服务(Swarm 的服务发现基于 DNS):
docker exec -it <frontend-container-id> curl http://backend:5000
你会看到 Hello from Backend! 的输出。Swarm 的覆盖网络让服务间通信像在同一台机器上一样简单!
7. 日志收集:让问题无处遁形
生产环境中,日志是排查问题的命脉。Swarm 默认将容器日志存储在节点本地,但分散的日志管理起来很麻烦。我们需要一个集中化的日志收集方案。
7.1 使用 Docker 日志驱动
Docker 支持多种日志驱动,推荐使用 Fluentd 或 Loki 收集日志。我们以 Fluentd 为例,搭建一个简单的日志收集系统。
步骤 1:部署 Fluentd 服务
创建一个 Fluentd 配置文件 fluentd.conf:
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<match docker.**>
@type stdout
</match>
构建 Fluentd 镜像:
# Dockerfile
FROM fluent/fluentd:v1.14-1
COPY fluentd.conf /fluentd/etc/fluentd.conf
docker build -t yourusername/fluentd:latest .
docker push yourusername/fluentd:latest
在 Stack 文件中添加 Fluentd 服务:
version: '3.8'
services:
fluentd:
image: yourusername/fluentd:latest
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
ports:
- "24224:24224"
networks:
- wp-network
wordpress:
image: wordpress:latest
deploy:
replicas: 3
ports:
- "8080:80"
logging:
driver: fluentd
options:
fluentd-address: fluentd:24224
networks:
- wp-network
mysql:
image: mysql:8.0
deploy:
replicas: 1
volumes:
- wp-data:/var/lib/mysql
logging:
driver: fluentd
options:
fluentd-address: fluentd:242Så24
networks:
- wp-network
networks:
wp-network:
driver: overlay
volumes:
wp-data:
步骤 2:部署并验证
docker stack deploy -c wordpress-stack.yml wp-stack
Fluentd 会收集 WordPress 和 MySQL 的日志,输出到标准输出。你可以通过 docker service logs wp-stack_fluentd 查看收集的日志。
进阶玩法:将 Fluentd 配置为将日志转发到 Elasticsearch 或 Loki,再用 Grafana 可视化,打造一个强大的日志分析平台。
8. 监控:让集群健康一目了然
没有监控的集群就像开夜车没开大灯,迟早要撞墙。我们需要实时了解集群的健康状态、资源使用情况和服务性能。
8.1 使用 Prometheus 和 Grafana
Prometheus 是监控领域的明星,搭配 Grafana 的仪表盘,简直是运维的“千里眼”。我们用 Stack 文件部署一个监控堆栈。
监控 Stack 文件:monitoring-stack.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.37.0
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
volumes:
- prometheus-data:/prometheus
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- monitoring-network
node-exporter:
image: prom/node-exporter:v1.3.1
deploy:
mode: global
networks:
- monitoring-network
grafana:
image: grafana/grafana:8.5.0
deploy:
replicas: 1
ports:
- "3000:3000"
networks:
- monitoring-network
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
networks:
monitoring-network:
driver: overlay
volumes:
prometheus-data:
Prometheus 配置文件:prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'docker'
static_configs:
- targets: ['<manager-ip>:9323']
注意:Docker 默认不暴露 metrics 端点,需在 Manager 节点的 /etc/docker/daemon.json 中启用:
{
"metrics-addr": "0.0.0.0:9323",
"experimental": true
}
重启 Docker 服务:
sudo systemctl restart docker
部署监控堆栈:
docker stack deploy -c monitoring-stack.yml monitoring
访问 http://<manager-ip>:3000,用默认账户(admin/admin123)登录 Grafana,导入 Docker 和 Node Exporter 的仪表盘模板(Grafana 社区有现成的模板,ID 比如 1860 或 11074),你就能看到节点 CPU、内存、容器状态等实时数据。
实战经验:为关键服务添加自定义 metrics(比如 WordPress 的请求延迟),可以进一步优化监控效果。
9. 负载均衡:让流量分配更聪明
Swarm 内置的负载均衡器(基于 IPVS)已经很强大,但我们可以通过一些配置让它更高效。
9.1 外部负载均衡
如果你的集群暴露多个服务,建议在 Swarm 前加一层 Nginx 或 HAProxy 作为外部负载均衡器。例如,部署一个 Nginx 服务作为反向代理:
version: '3.8'
services:
proxy:
image: nginx:latest
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- wp-network
networks:
wp-network:
driver: overlay
Nginx 配置文件:nginx.conf
worker_processes 1;
events { worker_connections 1024; }
http {
upstream wordpress {
server wordpress:80;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://wordpress;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
部署后,外部流量通过 Nginx 打到 WordPress 服务,Swarm 内部负载均衡器再将流量分发到多个 WordPress 容器。
9.2 优化 Swarm 内置负载均衡
Swarm 的负载均衡默认是基于 DNS 的轮询(round-robin)。如果需要更复杂的策略(如基于权重),可以结合外部负载均衡器或调整服务副本数。
小技巧:用 docker service update --endpoint-mode vip(默认模式)确保负载均衡稳定。如果服务通信频繁,考虑用 --endpoint-mode dnsrr 启用 DNS 轮询,减少 VIP 开销。
10. 安全加固:让你的集群固若金汤
生产环境的 Swarm 集群就像一座城堡,功能再强大也得防住“入侵者”。我们将从 Secrets 管理、TLS 加密 和 访问控制 三方面加固你的集群。
10.1 Secrets 管理:保护敏感数据
数据库密码、API 密钥等敏感信息不能明文写在 Stack 文件或环境变量里。Swarm 提供了 Secrets 功能,让你安全地存储和分发敏感数据。
创建 Secrets
假设我们的电商系统需要保护 Redis 的密码。先在 Manager 节点创建 Secret:
echo "supersecretpassword" | docker secret create redis_password -
检查 Secret 是否创建成功:
docker secret ls
输出类似:
ID NAME CREATED UPDATED
xxxx redis_password 5 minutes ago 5 minutes ago
在 Stack 文件中使用 Secrets
修改 ecommerce-stack.yml,将 Redis 的密码通过 Secret 注入:
version: '3.8'
services:
frontend:
image: yourusername/ecommerce-frontend:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
ports:
- "3000:3000"
networks:
- ecommerce-network
depends_on:
- backend
backend:
image: yourusername/ecommerce-backend:latest
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
environment:
REDIS_HOST: redis
REDIS_PASSWORD_FILE: /run/secrets/redis_password
secrets:
- redis_password
networks:
- ecommerce-network
depends_on:
- redis
redis:
image: redis:6.2
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
command: redis-server --requirepass $(cat /run/secrets/redis_password)
volumes:
- redis-data:/data
secrets:
- redis_password
networks:
- ecommerce-network
networks:
ecommerce-network:
driver: overlay
volumes:
redis-data:
secrets:
redis_password:
external: true
代码调整
修改后端代码以读取 Secret 文件:
// backend/index.js
const express = require('express');
const redis = require('redis');
const fs = require('fs');
const app = express();
const password = fs.readFileSync('/run/secrets/redis_password', 'utf8').trim();
const client = redis.createClient({
url: `redis://:${password}@redis:6379`
});
client.connect();
app.get('/products', async (req, res) => {
const cached = await client.get('products');
if (cached) return res.json(JSON.parse(cached));
const products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Phone', price: 499 }
];
await client.set('products', JSON.stringify(products), { EX: 3600 });
res.json(products);
});
app.listen(3001, () => console.log('Backend running on port 3001'));
重新构建并推送镜像:
docker build -t yourusername/ecommerce-backend:latest ./backend
docker push yourusername/ecommerce-backend:latest
docker stack deploy -c ecommerce-stack.yml ecommerce
安全提示:Secrets 存储在 Manager 节点的 Raft 数据库中,加密传输到容器。定期轮换 Secrets(删除旧的,创建新的),并限制 Manager 节点的访问权限。
10.2 TLS 加密:保护网络通信
Swarm 默认加密管理流量(2377 端口)和覆盖网络,但外部流量(如用户访问前端的 HTTP 请求)需要额外的 TLS 保护。我们用 Nginx 反向代理加上 Let’s Encrypt 的免费证书来实现 HTTPS。
部署 Certbot 获取证书
在 Manager 节点运行 Certbot 容器获取证书:
docker run -it --rm -v certs:/etc/letsencrypt -p 80:80 certbot/certbot certonly --standalone -d yourdomain.com
证书会存储在 certs 卷中。
更新 Nginx 配置
修改 nginx.conf 支持 HTTPS:
worker_processes 1;
events { worker_connections 1024; }
http {
upstream frontend {
server frontend:3000;
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/certs/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
更新 ecommerce-stack.yml 加入 Nginx 服务:
version: '3.8'
services:
proxy:
image: nginx:latest
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
ports:
- "80:80"
- "443:443"
volumes:
- certs:/etc/nginx/certs
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- ecommerce-network
frontend:
image: yourusername/ecommerce-frontend:latest
deploy:
replicas: 3
networks:
- ecommerce-network
backend:
image: yourusername/ecommerce-backend:latest
deploy:
replicas: 2
environment:
REDIS_HOST: redis
REDIS_PASSWORD_FILE: /run/secrets/redis_password
secrets:
- redis_password
networks:
- ecommerce-network
redis:
image: redis:6.2
deploy:
replicas: 1
command: redis-server --requirepass $(cat /run/secrets/redis_password)
volumes:
- redis-data:/data
secrets:
- redis_password
networks:
- ecommerce-network
networks:
ecommerce-network:
driver: overlay
volumes:
redis-data:
certs:
secrets:
redis_password:
external: true
部署并验证
docker stack deploy -c ecommerce-stack.yml ecommerce
访问 https://yourdomain.com,你会看到安全的电商前端页面。注意:定期用 Certbot 续期证书(每 90 天),可以用定时任务自动化。
10.3 访问控制:最小权限原则
限制 Docker API 访问:默认情况下,Docker 守护进程监听 unix:///var/run/docker.sock。如果暴露了 TCP 端口(如 2375),务必用防火墙限制访问:
sudo ufw allow from <trusted-ip> to any port 2375
RBAC:Swarm 本身不提供细粒度 RBAC,但可以通过限制 Manager 节点的 SSH 访问和 Docker CLI 权限来实现。例如,只允许特定用户运行 docker 命令:
sudo groupadd docker
sudo usermod -aG docker <username>
节点隔离:用 node.labels 将敏感服务(如数据库)限制在特定节点,防止非授权节点运行。
11. 性能优化:榨干集群的每一分潜力
Swarm 集群的性能直接影响服务的响应速度和用户体验。我们从 容器资源调优 和 网络优化 两方面入手。
11.1 容器资源调优
在 Part 2,我们为服务设置了 CPU 和内存限制,但这只是基础。以下是一些进阶技巧:
动态调整资源:根据负载动态调整副本数。Swarm 没有内置的 HPA(水平自动扩展),但可以用外部工具(如 Prometheus + 自定义脚本)实现。例如,监控前端服务的 CPU 使用率,超过 70% 时增加副本:
#!/bin/bash
CPU_USAGE=$(curl -s http://<prometheus-ip>:9090/api/v1/query?query=rate(container_cpu_usage_seconds_total{service="ecommerce_frontend"}[5m]) | jq '.data.result[0].value[1]' | awk '{print $1*100}')
if (( $(echo "$CPU_USAGE > 70" | bc -l) )); then
docker service scale ecommerce_frontend=5
fi
将脚本加入 cron 每分钟运行。
内存优化:避免容器内存泄漏。定期用 docker stats 检查容器内存使用情况,必要时重启服务:
docker service update --force ecommerce_frontend
11.2 网络优化
Swarm 的覆盖网络虽然方便,但高流量场景下可能成为瓶颈。优化方法:
减少跨节点通信:用 placement 约束将相关服务调度到同一节点,降低网络延迟。例如,将前端和后端放在同一节点:
frontend:
image: yourusername/ecommerce-frontend:latest
deploy:
replicas: 3
placement:
constraints:
- node.labels.type == app
backend:
image: yourusername/ecommerce-backend:latest
deploy:
replicas: 2
placement:
constraints:
- node.labels.type == app
给节点加标签:
docker node update --label-add type=app swarm-worker1
启用压缩:在 Nginx 反向代理中启用 Gzip 压缩,减少数据传输量:
http {
gzipocios2
gzip on;
gzip_types text/plain text/css application/json;
}
调整 MTU:如果网络性能不佳,检查节点的 MTU 设置,确保与覆盖网络一致(默认 1500)。
12. 复杂分布式系统:带消息队列的架构
让我们部署一个更复杂的系统,加入 RabbitMQ 作为消息队列,用于异步处理订单。
Stack 文件:ecommerce-mq-stack.yml
version: '3.8'
services:
proxy:
image: nginx:latest
deploy:
replicas: 1
ports:
- "80:80"
- "443:443"
volumes:
- certs:/etc/nginx/certs
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- ecommerce-network
frontend:
image: yourusername/ecommerce-frontend:latest
deploy:
replicas: 3
networks:
- ecommerce-network
backend:
image: yourusername/ecommerce-backend:latest
deploy:
replicas: 2
environment:
REDIS_HOST: redis
REDIS_PASSWORD_FILE: /run/secrets/redis_password
RABBITMQ_HOST: rabbitmq
secrets:
- redis_password
networks:
- ecommerce-network
redis:
image: redis:6.2
deploy:
replicas: 1
command: redis-server --requirepass $(cat /run/secrets/redis_password)
volumes:
- redis-data:/data
secrets:
- redis_password
networks:
- ecommerce-network
rabbitmq:
image: rabbitmq:3.9-management
deploy:
replicas: 1
ports:
- "15672:15672"
networks:
- ecommerce-network
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
networks:
ecommerce-network:
driver: overlay
volumes:
redis-data:
certs:
secrets:
redis_password:
external: true
后端代码调整
添加 RabbitMQ 生产者,处理订单:
// backend/index.js
const amqp = require('amqplib');
const express = require('express');
const redis = require('redis');
const fs = require('fs');
const app = express();
const password = fs.readFileSync('/run/secrets/redis_password', 'utf8').trim();
const client = redis.createClient({ url: `redis://:${password}@redis:6379` });
client.connect();
async function sendToQueue(order) {
const conn = await amqp.connect('amqp://guest:guest@rabbitmq:5672');
const channel = await conn.createChannel();
await channel.assertQueue('orders');
channel.sendToQueue('orders', Buffer.from(JSON.stringify(order)));
await channel.close();
await conn.close();
}
app.get('/products', async (req, res) => {
const cached = await client.get('products');
if (cached) return res.json(JSON.parse(cached));
const products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Phone', price: 499 }
];
await client.set('products', JSON.stringify(products), { EX: 3600 });
res.json(products);
});
app.post('/order', express.json(), async (req, res) => {
await sendToQueue(req.body);
res.json({ message: 'Order placed' });
});
app.listen(3001, () => console.log('Backend running on port 3001'));
部署并测试
docker stack deploy -c ecommerce-mq-stack.yml ecommerce
访问 RabbitMQ 管理界面 http://<node-ip>:15672(默认用户/密码:guest/guest),查看订单队列。发送一个 POST 请求到 /order,检查队列是否收到消息。