找回密码
 立即注册

QQ登录

只需一步,快速开始

peUfSYR.png
查看: 50|回复: 0

Day 27 生产部署策略

[复制链接]

876

主题

13

回帖

2808

积分

管理员

积分
2808
发表于 2026-3-29 16:17:55 | 显示全部楼层 |阅读模式
每次上线都像拆炸弹?改完代码心里没底,点下部署按钮手都在抖?别慌,今天我们就来聊聊怎么让生产部署变得像换灯泡一样简单——关掉旧的,拧上新的,灯还不能灭。

本文你将学到

  • 蓝绿部署的核心原理和实操方法
  • 金丝雀发布如何让风险降到最低
  • Docker 环境下实现零停机更新的完整方案
  • 出问题时秒级回滚的保命技巧

    ━━━━━━━━━━━━━━━━━━━━

    阅读时间: 约 12 分钟
    实操时间: 约 40 分钟
    难度等级: 中高级(需要 Day 1-26 的基础)

    ━━━━━━━━━━━━━━━━━━━━

    为什么要聊部署策略

    先说个真实场景:你辛辛苦苦写了一周的新功能,测试环境跑得好好的,信心满满地部署到生产环境。结果——白屏了。用户炸锅了,老板电话打过来了,你手忙脚乱地回滚,发现回滚脚本上次改完忘记测了。

    这种场景,但凡做过几年开发的人都经历过。问题的根源不是代码写得差,而是部署策略太粗暴。就像搬家,你不能把所有东西一股脑扔到新房子里,得有计划地搬,搬错了还能搬回来。

    蓝绿部署:两套房子轮着住

    原理

    蓝绿部署的思路特别简单:准备两套完全一样的环境,一套跑老版本(蓝),一套部署新版本(绿)。新版本验证没问题后,把流量从蓝切到绿。出问题?秒切回蓝。
    1.                     用户请求
    2.                        |
    3.                        v
    4.                  +----------+
    5.                  |  Nginx   |
    6.                  | 负载均衡  |
    7.                  +----------+
    8.                   /        \
    9.             当前流量      待切换
    10.                 /            \
    11.         +---------+    +---------+
    12.         | 蓝环境   |    | 绿环境   |
    13.         | (v1.0)  |    | (v1.1)  |
    14.         | 运行中   |    | 已就绪   |
    15.         +---------+    +---------+
    16.               |              |
    17.         +---------+    +---------+
    18.         | 数据库   |    | 数据库   |
    19.         | (共享)  |    | (共享)  |
    20.         +---------+    +---------+
    复制代码

    这就像你有两套房子,一套在住,另一套装修好了。搬过去住两天,发现马桶漏水?直接搬回老房子,一点不耽误。

    实操:Docker Compose 实现蓝绿部署

    先准备目录结构:
    1. mkdir -p ~/docker-blue-green && cd ~/docker-blue-green
    复制代码

    创建一个简单的应用来演示。先写
    1. app/server.js
    复制代码

    1. mkdir -p app
    2. cat > app/server.js << 'EOF'
    3. const http = require('http');
    4. const version = process.env.APP_VERSION || 'unknown';
    5. const color = process.env.APP_COLOR || 'unknown';
    6. const server = http.createServer((req, res) => {
    7.   if (req.url === '/health') {
    8.     res.writeHead(200, { 'Content-Type': 'application/json' });
    9.     res.end(JSON.stringify({ status: 'healthy', version, color }));
    10.     return;
    11.   }
    12.   res.writeHead(200, { 'Content-Type': 'text/plain' });
    13.   res.end(`Hello from ${color} environment, version ${version}\n`);
    14. });
    15. server.listen(3000, () => {
    16.   console.log(`${color} server v${version} running on port 3000`);
    17. });
    18. EOF
    复制代码

    1. Dockerfile
    复制代码

    1. cat > app/Dockerfile << 'EOF'
    2. FROM node:20-alpine
    3. WORKDIR /app
    4. COPY server.js .
    5. EXPOSE 3000
    6. CMD ["node", "server.js"]
    7. EOF
    复制代码

    创建蓝环境的 compose 文件
    1. docker-compose.blue.yml
    复制代码

    1. services:
    2.   app-blue:
    3.     build: ./app
    4.     environment:
    5.       - APP_VERSION=1.0.0
    6.       - APP_COLOR=blue
    7.     networks:
    8.       - web
    9.     healthcheck:
    10.       test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
    11.       interval: 5s
    12.       timeout: 3s
    13.       retries: 3
    14. networks:
    15.   web:
    16.     external: true
    复制代码

    创建绿环境的 compose 文件
    1. docker-compose.green.yml
    复制代码

    1. services:
    2.   app-green:
    3.     build: ./app
    4.     environment:
    5.       - APP_VERSION=1.1.0
    6.       - APP_COLOR=green
    7.     networks:
    8.       - web
    9.     healthcheck:
    10.       test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
    11.       interval: 5s
    12.       timeout: 3s
    13.       retries: 3
    14. networks:
    15.   web:
    16.     external: true
    复制代码

    Nginx 配置
    1. nginx.conf
    复制代码

    1. upstream backend {
    2.     server app-blue:3000;
    3. }
    4. server {
    5.     listen 80;
    6.     location / {
    7.         proxy_pass http://backend;
    8.         proxy_set_header Host $host;
    9.         proxy_set_header X-Real-IP $remote_addr;
    10.     }
    11.     location /health {
    12.         proxy_pass http://backend;
    13.     }
    14. }
    复制代码

    创建 Nginx 的 compose 文件
    1. docker-compose.nginx.yml
    复制代码

    1. services:
    2.   nginx:
    3.     image: nginx:alpine
    4.     ports:
    5.       - "8080:80"
    6.     volumes:
    7.       - ./nginx.conf:/etc/nginx/conf.d/default.conf
    8.     networks:
    9.       - web
    10. networks:
    11.   web:
    12.     external: true
    复制代码

    启动蓝环境:
    1. # 创建共享网络
    2. docker network create web
    3. # 启动蓝环境
    4. docker compose -f docker-compose.blue.yml up -d --build
    5. # 启动 Nginx
    6. docker compose -f docker-compose.nginx.yml up -d
    复制代码

    验证蓝环境正常工作:
    1. curl http://localhost:8080
    2. # 预期输出: Hello from blue environment, version 1.0.0
    复制代码

    现在部署绿环境:
    1. # 启动绿环境(此时蓝环境仍在服务)
    2. docker compose -f docker-compose.green.yml up -d --build
    3. # 验证绿环境健康
    4. docker compose -f docker-compose.green.yml ps
    复制代码

    切换流量到绿环境——修改
    1. nginx.conf
    复制代码
    中的 upstream:
    1. sed -i 's/app-blue:3000/app-green:3000/' nginx.conf
    2. # 重新加载 Nginx 配置(零停机)
    3. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    复制代码

    验证切换成功:
    1. curl http://localhost:8080
    2. # 预期输出: Hello from green environment, version 1.1.0
    复制代码

    回滚

    发现问题?一条命令切回去:
    1. sed -i 's/app-green:3000/app-blue:3000/' nginx.conf
    2. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    复制代码

    就这么简单。蓝环境一直在那儿等着你,随时可以切回来。

    金丝雀发布:先派侦察兵

    原理

    蓝绿部署虽然好,但有个问题——切换是全量的,要么全走新版本,要么全走老版本。金丝雀发布更精细,先让一小部分流量(比如 5%)走新版本,观察一段时间没问题,再逐步放大比例。
    1.                     用户请求 (100%)
    2.                         |
    3.                         v
    4.                   +-----------+
    5.                   |   Nginx   |
    6.                   | 权重分配   |
    7.                   +-----------+
    8.                    /         \
    9.               95%流量       5%流量
    10.                 /               \
    11.         +-----------+    +-----------+
    12.         | 稳定版本   |    | 金丝雀版本 |
    13.         | (v1.0)    |    | (v1.1)    |
    14.         | 3个实例    |    | 1个实例    |
    15.         +-----------+    +-----------+
    16.         第一阶段: 5%  → 观察 10 分钟
    17.         第二阶段: 25% → 观察 10 分钟
    18.         第三阶段: 50% → 观察 10 分钟
    19.         第四阶段: 100% → 全量发布完成
    复制代码

    名字的来历很有意思:早年矿工下矿井前,会先放一只金丝雀进去。金丝雀对有毒气体特别敏感,如果金丝雀没事,矿工再下去。我们的做法也一样——先让少量用户当"金丝雀",没问题再全量放开。

    实操:Nginx 权重实现金丝雀

    修改
    1. nginx.conf
    复制代码
    支持权重分配:
    1. upstream backend {
    2.     # 稳定版本 - 权重 95
    3.     server app-blue:3000 weight=95;
    4.     # 金丝雀版本 - 权重 5
    5.     server app-green:3000 weight=5;
    6. }
    7. server {
    8.     listen 80;
    9.     location / {
    10.         proxy_pass http://backend;
    11.         proxy_set_header Host $host;
    12.         proxy_set_header X-Real-IP $remote_addr;
    13.     }
    14. }
    复制代码

    确保两个环境都在运行,然后重载 Nginx:
    1. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    复制代码

    验证流量分配:
    1. # 发送 20 个请求,观察分布
    2. for i in $(seq 1 20); do
    3.   curl -s http://localhost:8080
    4. done
    复制代码

    预期输出大致是 19 个 blue、1 个 green(5% 的比例不是精确的,但趋势是对的)。

    逐步调大金丝雀比例:
    1. # 第二阶段: 25%
    2. sed -i 's/weight=95/weight=75/' nginx.conf
    3. sed -i 's/weight=5/weight=25/' nginx.conf
    4. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    5. # 第三阶段: 50%
    6. sed -i 's/weight=75/weight=50/' nginx.conf
    7. sed -i 's/weight=25/weight=50/' nginx.conf
    8. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    9. # 第四阶段: 全量切换
    10. sed -i 's/weight=50/weight=0/' nginx.conf  # 蓝环境权重设为 0
    11. sed -i 's/weight=50/weight=100/' nginx.conf  # 绿环境全量
    12. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    复制代码

    Docker 零停机滚动更新

    如果你用的是 Docker Swarm 或者单纯的 Docker Compose,还有一种更简单的方式——滚动更新。它的原理是逐个替换容器,始终保证有容器在服务。

    Docker Compose 滚动更新

    创建
    1. docker-compose.rolling.yml
    复制代码

    1. services:
    2.   app:
    3.     build: ./app
    4.     environment:
    5.       - APP_VERSION=1.1.0
    6.       - APP_COLOR=rolling
    7.     deploy:
    8.       replicas: 3
    9.       update_config:
    10.         parallelism: 1
    11.         delay: 10s
    12.         order: start-first
    13.       restart_policy:
    14.         condition: on-failure
    15.     healthcheck:
    16.       test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
    17.       interval: 5s
    18.       timeout: 3s
    19.       retries: 3
    20.     networks:
    21.       - web
    22. networks:
    23.   web:
    24.     external: true
    复制代码

    关键配置解读:

    1. parallelism: 1
    复制代码
    :每次只更新 1 个容器
    1. delay: 10s
    复制代码
    :两次更新之间等待 10 秒
    1. order: start-first
    复制代码
    :先启动新容器,再停掉旧容器(保证零停机)
    1. 滚动更新过程:
    2. 时间轴 →
    3.         ┌─────────┐
    4. 实例1   │  v1.0   │ ──停止──→ ┌─────────┐
    5.         └─────────┘           │  v1.1   │
    6.                               └─────────┘
    7.         ┌─────────┐                       ┌─────────┐
    8. 实例2   │  v1.0   │ ─────── 停止 ───────→│  v1.1   │
    9.         └─────────┘                       └─────────┘
    10.         ┌─────────┐                                    ┌─────────┐
    11. 实例3   │  v1.0   │ ──────────── 停止 ───────────────→│  v1.1   │
    12.         └─────────┘                                    └─────────┘
    13. 始终有至少 2 个实例在服务,用户无感知
    复制代码

    完整的回滚方案

    部署策略再好,也得有后手。回滚方案就是你的保险丝。

    镜像版本化管理
    1. # 构建时打上版本标签
    2. docker build -t myapp:1.0.0 -t myapp:latest ./app
    3. # 同时保留多个版本
    4. docker tag myapp:1.0.0 myapp:rollback
    5. docker tag myapp:1.1.0 myapp:latest
    复制代码

    自动化回滚脚本

    创建
    1. rollback.sh
    复制代码

    1. #!/bin/bash
    2. set -e
    3. PREVIOUS_VERSION=${1:?"用法: ./rollback.sh <版本号>"}
    4. CURRENT_ENV=$(cat .current-env 2>/dev/null || echo "blue")
    5. echo "当前环境: $CURRENT_ENV"
    6. echo "回滚到版本: $PREVIOUS_VERSION"
    7. # 确定回滚目标环境
    8. if [ "$CURRENT_ENV" = "blue" ]; then
    9.   ROLLBACK_ENV="green"
    10.   ROLLBACK_COMPOSE="docker-compose.green.yml"
    11. else
    12.   ROLLBACK_ENV="blue"
    13.   ROLLBACK_COMPOSE="docker-compose.blue.yml"
    14. fi
    15. # 检查回滚版本的镜像是否存在
    16. if ! docker image inspect "myapp:$PREVIOUS_VERSION" > /dev/null 2>&1; then
    17.   echo "错误: 镜像 myapp:$PREVIOUS_VERSION 不存在"
    18.   exit 1
    19. fi
    20. # 切换 Nginx 上游
    21. sed -i "s/app-$CURRENT_ENV:3000/app-$ROLLBACK_ENV:3000/" nginx.conf
    22. docker compose -f docker-compose.nginx.yml exec nginx nginx -s reload
    23. # 更新当前环境记录
    24. echo "$ROLLBACK_ENV" > .current-env
    25. echo "已回滚到 $ROLLBACK_ENV 环境 (版本 $PREVIOUS_VERSION)"
    26. echo "验证: curl http://localhost:8080/health"
    复制代码
    1. chmod +x rollback.sh
    复制代码

    健康检查驱动的自动回滚

    更高级的玩法是让系统自己判断要不要回滚:
    1. #!/bin/bash
    2. # auto-rollback-check.sh
    3. HEALTH_URL="http://localhost:8080/health"
    4. MAX_FAILURES=3
    5. CHECK_INTERVAL=10
    6. failure_count=0
    7. echo "开始健康检查监控..."
    8. while true; do
    9.   response=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null)
    10.   if [ "$response" != "200" ]; then
    11.     failure_count=$((failure_count + 1))
    12.     echo "健康检查失败 ($failure_count/$MAX_FAILURES), HTTP状态: $response"
    13.     if [ "$failure_count" -ge "$MAX_FAILURES" ]; then
    14.       echo "连续失败 $MAX_FAILURES 次,触发自动回滚!"
    15.       ./rollback.sh "$(cat .previous-version)"
    16.       exit 1
    17.     fi
    18.   else
    19.     failure_count=0
    20.   fi
    21.   sleep "$CHECK_INTERVAL"
    22. done
    复制代码

    部署策略对比:选哪个

    | 特性 | 蓝绿部署 | 金丝雀发布 | 滚动更新 |
    |------|---------|-----------|---------|
    | 资源占用 | 双倍 | 少量额外 | 几乎不变 |
    | 回滚速度 | 秒级 | 秒级 | 分钟级 |
    | 风险控制 | 全量切换 | 渐进式 | 渐进式 |
    | 复杂度 | 中等 | 较高 | 较低 |
    | 适用场景 | 重大版本更新 | 功能灰度测试 | 常规迭代 |

    选择建议:
  • 日常小版本迭代 → 滚动更新,简单够用
  • 大版本上线、数据库迁移 → 蓝绿部署,回滚无忧
  • 新功能灰度、A/B 测试 → 金丝雀发布,精细控制

    常见问题 Q&A

    Q1: 蓝绿部署需要双倍的服务器资源,成本太高怎么办?

    A: 不一定需要常驻双倍资源。平时只跑一套环境,部署时临时拉起另一套,切换完成后可以把旧环境缩容。用云服务器的弹性伸缩功能,按需付费,成本可以控制在增加 10%-20% 左右。

    Q2: 金丝雀发布时,如果新版本往数据库写了脏数据怎么办?

    A: 这是个好问题。核心原则是数据库变更要向前兼容。新版本的数据库迁移脚本必须保证:老版本读得了新数据,新版本也读得了老数据。具体做法是"先加后删"——先加新字段,双版本都能跑了,再在下个版本删旧字段。

    Q3: 小团队没精力搞这么复杂的部署流程,有简化方案吗?

    A: 从滚动更新开始就够了。Docker Compose 配合健康检查,加一个简单的回滚脚本,覆盖 80% 的场景。等团队规模大了、服务多了,再逐步引入蓝绿或金丝雀。千万别一上来就搞全套,过度工程比没有工程更可怕。

    小结

    今天我们学了三种核心部署策略:

  • 蓝绿部署:两套环境轮着来,切换快、回滚也快,适合大版本上线
  • 金丝雀发布:先放一小波流量试水,没问题再全量,适合灰度测试
  • 滚动更新:逐个替换容器,简单实用,适合日常迭代

    核心思想就一句话:永远给自己留退路。部署不是赌博,是工程。有了这些策略,你再也不用上线时拆炸弹了。

    记住几个关键点:
  • 一定要有健康检查,让系统自己告诉你它是不是好的
  • 一定要有回滚方案,而且回滚方案要提前测过
  • 数据库变更要向前兼容,这是所有部署策略的基础

    明天是 Day 28 容器故障排查,我们来聊聊容器出问题时怎么快速定位和修复。毕竟部署只是开始,运维才是持久战。
  • 您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    扫码关注微信公众号

    Archiver|手机版|小黑屋|风叶林

    GMT+8, 2026-5-8 21:43 , Processed in 0.159861 second(s), 20 queries .

    Powered by 风叶林

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表