背景

在微服务架构下,服务的部署与更新频率远高于传统单体应用。手工SSH登录服务器逐台操作不仅效率低下,还极易因人为失误导致服务中断。本文介绍如何利用Docker Compose结合Nginx实现一套轻量级的微服务自动化部署与灰度发布方案,适用于中小规模团队(5~20个服务节点),无需引入Kubernetes等重型基础设施。

核心思路

  1. GitOps驱动:代码提交至Git后,通过Webhook触发部署脚本完成服务更新。
  2. 蓝绿/灰度双轨:通过Nginx upstream权重调整,实现新老版本共存与平滑切换。
  3. 环境一致性:所有服务以Docker Compose YAML定义,环境差异归零。

目录结构

推荐在每台服务器上建立统一部署目录:

1
2
3
4
5
6
7
8
9
10
/opt/app/
├── docker-compose.yml # 主编排文件
├── .env # 环境变量(版本、镜像TAG)
├── nginx/
│ └── upstream.conf # 动态upstream配置
├── scripts/
│ ├── deploy.sh # 部署脚本
│ ├── rollback.sh # 回滚脚本
│ └── switch.sh # 灰度切换脚本
└── backup/ # 历史版本备份

核心配置

docker-compose.yml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: "3.8"
services:
api-gateway:
image: registry.example.com/api-gateway:${VERSION:-latest}
container_name: api-gateway
restart: always
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
networks:
- frontend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3

user-service:
image: registry.example.com/user-service:${VERSION:-latest}
container_name: user-service
restart: always
ports:
- "8081:8080"
depends_on:
- api-gateway
networks:
- backend

networks:
frontend:
backend:

.env 文件内容:

1
2
3
VERSION=1.2.3
REGISTRY=registry.example.com
DEPLOY_TIME=2026-04-02

Nginx 灰度upstream配置

1
2
3
4
5
# upstream.conf(include到主nginx.conf)
upstream backend {
server 127.0.0.1:8081 weight=10; # 老版本
server 127.0.0.1:8082 weight=0; # 新版本,权重0暂不启用
}

部署脚本 deploy.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
set -euo pipefail
ENV_FILE="/opt/app/.env"
NGINX_CONF="/opt/app/nginx/upstream.conf"

source "$ENV_FILE"

echo "[$(date)] 开始部署,版本:$VERSION"

# 拉取最新镜像
docker-compose pull

# 停止老容器(不删除volume)
docker-compose up -d --remove-orphans

# 等待健康检查通过
sleep 10

# 检查新容器状态
if docker inspect --format='{{.State.Health.Status}}' api-gateway | grep -q "healthy"; then
echo "[$(date)] 服务健康检查通过"
else
echo "[$(date)] 健康检查失败,执行回滚"
docker-compose down
docker-compose -f docker-compose.backup.yml up -d
exit 1
fi

灰度切换脚本 switch.sh

将新版本容器(监听8082)权重从0调整为5,观察错误率:

1
2
3
4
5
6
7
8
#!/bin/bash
# 逐步增加新版本权重:0 → 5 → 10(全量)
NEW_WEIGHT=${1:-5}

sed -i "s/weight=0/weight=$NEW_WEIGHT/" /opt/app/nginx/upstream.conf
nginx -s reload

echo "[$(date)] 灰度权重调整为:$NEW_WEIGHT"

验证方式:

1
2
3
4
5
# 查看当前upstream实时状态(需开启nginx stub_status)
curl http://127.0.0.1:8080/nginx_status

# 持续监控错误率(30秒窗口)
watch -n5 "curl -s http://127.0.0.1:8080/api/health | jq '.errors // 0'"

回滚脚本 rollback.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
BACKUP_DIR="/opt/app/backup"
LATEST_BACKUP=$(ls -t "$BACKUP_DIR" | head -1)

if [ -z "$LATEST_BACKUP" ]; then
echo "[$(date)] 无可用的备份版本"
exit 1
fi

echo "[$(date)] 开始回滚至:$LATEST_BACKUP"
cp "$BACKUP_DIR/$LATEST_BACKUP/docker-compose.yml" /opt/app/docker-compose.yml
docker-compose up -d --remove-orphans
nginx -s reload
echo "[$(date)] 回滚完成"

完整部署流程

  1. 代码提交 → Git仓库触发Webhook调用部署服务器 /hooks/deploy
  2. 部署服务器 执行 git pull && ./scripts/deploy.sh
  3. 新版本容器 以不同端口(如8082)启动,与老版本(8081)并存
  4. 灰度切换 调用 ./scripts/switch.sh 5,5%流量切换新版本
  5. 监控观察 持续5分钟,错误率稳定后调用 ./scripts/switch.sh 10 全量
  6. 异常回滚 调用 ./scripts/rollback.sh

注意事项

  • .env 文件包含敏感信息,需确保权限为600:chmod 600 /opt/app/.env
  • 备份策略建议保留最近5个历史版本,使用 cron 每日清理
  • 镜像仓库建议启用镜像签名,防止供应链攻击
  • 大规模场景(20+服务节点)建议逐步引入Ansible或SaltStack做批量编排

总结

该方案以Docker Compose为容器编排核心,配合Nginx实现灰度发布,无需额外学习成本即可落地。对于团队规模在20人以内的微服务项目,能够在10~15分钟内完成从代码提交到生产上线的完整闭环,兼顾了部署效率与运行稳定性。