零停机回滚策略
版本保留、数据库可逆性与自动回滚触发,实现安全恢复。
CI/CD进阶
为什么
部署破坏生产时,每分钟停机损失收入和信任。预测试回滚路径 — 保留旧版本、数据库迁移可逆、触发自动化 — 秒级恢复服务而非数小时“向前修复”恐慌。回滚必须是一等部署操作,而非事后想法。
何时使用
任何有 SLA、频繁部署(>1/天)或有状态数据的生产系统实现正式回滚。静态站跳过,重部署瞬时且无状态。
如何做
1. 保留版本 — 注册表 + 集群保留最近 N(3-5)镜像标签/部署,不立即 GC
2. 可逆迁移 — 每个 up 有 down;用扩展/收缩使旧版本仍读新 schema
3. 特性开关 — 风险变更包在开关内,"回滚" = 关开关(无重部署,瞬时)
4. 自动触发 — 监控错误率/延迟,阈值突破自动回滚(见 canary-release)
5. 手动回滚 — 一条命令:kubectl rollout undo、vercel --prod --rollback、aws deploy rollback
6. staging 测回滚 — 定期练习回滚,路径在需要时已验证
7. 数据库回滚计划 — 不可逆迁移则部署前快照,回滚时恢复
8. 回滚后 — 告警 + 复盘,回滚非终点 — 下次部署前找根因
示例
# K8s — 保留修订历史 + 即时回滚
# deployment.yaml
spec:
revisionHistoryLimit: 5 # 保留最近 5 个 ReplicaSet 供回滚
# 回滚命令
kubectl rollout undo deployment/myapp # 到上一个
kubectl rollout undo deployment/myapp --to-revision=3
kubectl rollout status deployment/myapp # 观察进度
kubectl rollout history deployment/myapp # 列修订
# Vercel — 即时回滚到上一个生产部署
vercel --prod --rollback # CLI
# 或经 API:POST /v13/deployments/{id}/rollback
# 特性开关回滚(无重部署)— LaunchDarkly / Unleash
import { LDClient } from "launchdarkly-node-server-sdk";
const client = await LDClient.init(process.env.LD_KEY);
// "回滚" = 关开关,<1s 生效,无部署
await client.variation("new-checkout-flow", user, false);
# 可逆数据库迁移(Knex)— 始终写 down()
exports.up = function (knex) {
return knex.schema.table("orders", (t) => t.string("status_v2").nullable()); // 扩展
};
exports.down = function (knex) {
return knex.schema.table("orders", (t) => t.dropColumn("status_v2")); // 收缩
};
# 自动回滚触发(GitHub Actions 部署失败时)
- name: Deploy
run: npm run deploy
- name: Verify
run: npm run smoke-test
- name: Rollback on failure
if: failure()
run: npm run deploy:rollback
常见问题
- ⚠旧版本被 GC — 注册表保留太短,回滚目标消失,被迫向前修复
- ⚠不可逆迁移 — 加了 NOT NULL 列,旧版本回滚崩溃,必须向前修复
- ⚠回滚从未测试 — 理论可行,实际失败(配置漂移、缺环境变量)
- ⚠向前修复文化 — 团队从不回滚,路径腐烂,恐慌修复耗时数小时
最佳实践
- ✓设 revisionHistoryLimit: 5+ 且注册表保留 90 天 — 回滚目标始终可用
- ✓优先特性开关而非重部署回滚 — 瞬时,无版本博弈,无数据库顾虑
- ✓每月“回滚演练”— 部署 staging,破坏,回滚,验证路径可行
- ✓每个迁移可逆 — 扩展/收缩,绝不在同一次部署中既改代码又破坏性改 schema
常见问题解答
回滚应该多快?
5 分钟内——更久则用户在调查时持续受影响。预烤上一镜像/版本使回滚为单命令(kubectl rollout undo)。事故中绝不从旧提交重新构建。
回滚和前向修复有什么区别?
回滚退到上一已知良好版本(快、安全)。前向修复部署修复 bug 的新版本(更慢但保持历史干净)。先回滚止血,再后续部署前向修复。
数据库变更如何回滚?
加性迁移(新列)可回滚。破坏性迁移(删列)不可——数据无法恢复。分两阶段应用破坏性变更:部署停止写入的代码,安全期后再删列。
为什么需要测试回滚?
未测回滚在最需要时失败。每次发布在 staging 跑回滚——捕获不兼容 DB 模式、缺镜像、仅新版本存在的配置等问题。回滚即部署;像部署一样测试。
如何在错误激增时自动回滚?
用 Flagger 或 Argo Rollouts 配指标提供者(Prometheus)。设:错误率超 1% 持续 2 分钟则自动回滚。秒级捕获问题 vs. on-call 工程师 10 分钟才注意并行动。