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

4.1 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
devops.cicd.release-rollback 发布与回滚:镜像标签、环境、滚动 / 蓝绿 / 金丝雀、数据库迁移 lesson devops
cicd
发布策略
回滚
蓝绿发布
金丝雀
数据库迁移
特性开关
intermediate
devops.cicd.pipeline
3
devops.cicd.scenario.multi-lang-pipeline
devops.nginx.https-stream-limit
devops.cicd.interview
14
2

发布与回滚:镜像标签、环境、滚动 / 蓝绿 / 金丝雀、数据库迁移

解释说明

不可变产物:一次构建出镜像(标签 = 提交哈希或版本号),测试、预发、生产用同一个镜像,只是配置不同。这样测试过的东西就是上线的东西,回滚就是切回上一个标签。

环境:开发 → 测试 → 预发(和生产配置尽量一致)→ 生产。配置通过环境变量注入,不打进镜像。

发布策略

策略 做法 优点 缺点
重建 停掉旧版本再启动新版本 最简单 有停机
滚动 逐台替换实例 无停机、资源占用少 新旧版本同时在线一段时间;回滚也要逐台
蓝绿 准备一整套新环境,验证后切流量 切换和回滚都是瞬间 需要双倍资源
金丝雀 先给小部分流量,观察指标后逐步放大 风险最小 需要流量控制能力和监控

回滚:应用回滚 = 部署上一个镜像标签。难点在数据库:新版本已经改了表结构,旧版本可能跑不起来。

数据库迁移的原则:迁移脚本放在仓库里、有版本号、由工具执行(Go 的 golang-migrate / goose,Python 的 Alembic);每次迁移都要向后兼容——新旧代码都能在新表结构上运行。删除或重命名字段要分多次发布(“扩展 → 迁移 → 收缩”):

  1. 加新字段(旧代码不受影响),新代码同时写新旧字段;
  2. 回填历史数据,新代码改为读新字段;
  3. 确认不再回滚到旧版本后,再删除旧字段。

特性开关:代码先上线但功能默认关闭,通过配置打开;出问题直接关开关,比回滚代码更快。

案例

一次发布要把 users.name 改名为 display_name,迁移脚本直接执行了 RENAME COLUMN。发布后发现新版本有 bug,回滚到旧版本——旧代码找不到 name 字段,全站报错,故障时间翻倍。按“扩展 → 迁移 → 收缩”分三次发布就能避免。

代码示例

单机部署 + 健康检查 + 失败自动回滚(完整脚本 examples/pipeline/deploy.sh):

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):

-- 第 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 面试题:蓝绿和金丝雀的区别、怎么回滚、数据库变更怎么做到可回滚。