Files
zhuangang/topics/devops/cicd/02-release-rollback.md
T

88 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: devops.cicd.release-rollback
title: 发布与回滚:镜像标签、环境、滚动 / 蓝绿 / 金丝雀、数据库迁移
type: lesson
domain: devops
stack: [cicd]
tags: [发布策略, 回滚, 蓝绿发布, 金丝雀, 数据库迁移, 特性开关]
level: intermediate
prerequisites: [devops.cicd.pipeline]
est_hours: 3
related: [devops.cicd.scenario.multi-lang-pipeline, devops.nginx.https-stream-limit, devops.cicd.interview]
week: [14]
order: 2
---
# 发布与回滚:镜像标签、环境、滚动 / 蓝绿 / 金丝雀、数据库迁移
## 解释说明
**不可变产物**:一次构建出镜像(标签 = 提交哈希或版本号),测试、预发、生产用**同一个镜像**,只是配置不同。这样测试过的东西就是上线的东西,回滚就是切回上一个标签。
**环境**:开发 → 测试 → 预发(和生产配置尽量一致)→ 生产。配置通过环境变量注入,不打进镜像。
**发布策略**
| 策略 | 做法 | 优点 | 缺点 |
| --- | --- | --- | --- |
| 重建 | 停掉旧版本再启动新版本 | 最简单 | 有停机 |
| 滚动 | 逐台替换实例 | 无停机、资源占用少 | 新旧版本同时在线一段时间;回滚也要逐台 |
| 蓝绿 | 准备一整套新环境,验证后切流量 | 切换和回滚都是瞬间 | 需要双倍资源 |
| 金丝雀 | 先给小部分流量,观察指标后逐步放大 | 风险最小 | 需要流量控制能力和监控 |
**回滚**:应用回滚 = 部署上一个镜像标签。难点在**数据库**:新版本已经改了表结构,旧版本可能跑不起来。
**数据库迁移的原则**:迁移脚本放在仓库里、有版本号、由工具执行(Go 的 golang-migrate / goose,Python 的 Alembic);**每次迁移都要向后兼容**——新旧代码都能在新表结构上运行。删除或重命名字段要分多次发布(“扩展 → 迁移 → 收缩”):
1. 加新字段(旧代码不受影响),新代码同时写新旧字段;
2. 回填历史数据,新代码改为读新字段;
3. 确认不再回滚到旧版本后,再删除旧字段。
**特性开关**:代码先上线但功能默认关闭,通过配置打开;出问题直接关开关,比回滚代码更快。
## 案例
一次发布要把 `users.name` 改名为 `display_name`,迁移脚本直接执行了 `RENAME COLUMN`。发布后发现新版本有 bug,回滚到旧版本——旧代码找不到 `name` 字段,全站报错,故障时间翻倍。按“扩展 → 迁移 → 收缩”分三次发布就能避免。
## 代码示例
单机部署 + 健康检查 + 失败自动回滚(完整脚本 [examples/pipeline/deploy.sh](examples/pipeline/deploy.sh)):
```bash
previous="$(cat "$STATE_FILE" 2>/dev/null || true)" # 上一次成功部署的标签
deploy "$IMAGE_TAG"
if healthy; then
echo "$IMAGE_TAG" > "$STATE_FILE"
else
echo "健康检查失败,回滚到 $previous" >&2
[[ -n "$previous" ]] && deploy "$previous"
exit 1
fi
```
“扩展 → 迁移 → 收缩”的迁移脚本(PostgreSQL):
```sql
-- 第 1 次发布:扩展(旧代码不受影响)
ALTER TABLE users ADD COLUMN display_name TEXT;
-- 第 2 次发布前:回填(大表要分批执行)
UPDATE users SET display_name = name WHERE display_name IS NULL;
-- 第 3 次发布:确认不再回滚到读 name 的版本后,收缩
ALTER TABLE users DROP COLUMN name;
```
## 易错点
- **每个环境重新构建镜像**:测试过的和上线的不是同一个东西。
- **迁移不向后兼容**:应用无法回滚。
- **应用启动时自动执行破坏性迁移**:多实例同时启动会并发迁移;迁移失败导致所有实例起不来。迁移作为单独的发布步骤执行。
- **只看“部署成功”不看业务指标**:容器起来了不代表功能正常。发布后观察错误率、延迟、关键业务指标。
- **回滚流程从来没有演练过**:真出事时脚本跑不通。
- **特性开关从不清理**:代码里分支越来越多。功能稳定后删除开关。
## 面试常问
见 [CI/CD 面试题](interview.md):蓝绿和金丝雀的区别、怎么回滚、数据库变更怎么做到可回滚。