回滚不是一句‘Git 回退’
生产失败时最宝贵的是时间。若回滚步骤还需要查提交、重新安装依赖、猜数据库是否兼容,它不是回滚方案。可执行的回滚必须在发布前准备好上一版本代码或镜像、配置、数据保护点和流量切换方式。 单服务器无法消除所有中断,但可以避免不必要的停机:候选版本先在另一个本地端口启动,通过健康和关键路径检查后,再让 Caddy 把新请求交给它。
发布前先定义成功和停止条件
写下本次变更、受影响路径、数据库变化、预计时间和负责人。成功不是服务显示 active,而是健康端点、首页、关键 API、登录或表单按预期工作。停止条件要具体,例如健康检查连续失败、5xx 比例显著上升、迁移耗时超过窗口。 发布期间冻结无关变更,保存当前版本号、配置哈希和数据库备份。不要在同一次发布里顺便升级操作系统、数据库大版本和应用依赖,那会让失败原因无法区分。
在另一个端口启动候选版本
当前版本运行 127.0.0.1:3000,候选版本可运行 127.0.0.1:3002。两个 release 目录分别只读,共享数据前必须确认新旧版本对数据结构兼容。
curl -fsS http://127.0.0.1:3002/health
curl -fsS -H 'Host: www.example.com' http://127.0.0.1:3002/健康端点检查进程已经准备接流量,关键路径检查页面和依赖。不要把健康检查写成永远返回 200 的常量;也不要包含太重的外部调用,导致正常抖动就被判死。
切换代理前验证配置,切换后立即观察
把 Caddy 上游从 3000 改到 3002,运行 caddy validate,通过后 reload。保存修改前配置,重载后同时检查外部 HTTPS、Caddy 日志、新版本日志和旧版本连接。旧进程先保留一个短观察窗口,不要刚切换就删除。 如果应用支持多个上游,也可以短暂加入候选并控制流量,但同一数据库上的两个版本必须兼容。单机蓝绿的价值是快速切换,不等于拥有独立基础设施。
数据库迁移决定回滚上限
删除列、重命名字段或不可逆转换会让旧版本无法继续运行。更安全的做法是扩展—迁移—收缩:先增加向后兼容字段,部署能同时读写新旧结构的版本,完成数据迁移并观察,最后在后续发布删除旧结构。 任何写数据的迁移都先在最新备份副本演练,记录耗时和锁影响。不要把长迁移藏进应用启动命令,否则 systemd 重启和容器重建都可能重复执行。
回滚按预先写好的顺序执行
触发停止条件后先切回旧上游并 reload,确认用户流量恢复,再停止候选版本。若数据库变更向后兼容,不必立刻恢复整库;若发生错误写入,按事件级恢复方案处理,不能为了修一张表贸然覆盖整个生产数据库。 回滚后保留候选日志和时间线。恢复服务优先,根因分析随后进行,但不要删掉失败现场证据。
一份最小发布记录
记录版本、操作者、备份位置、开始时间、切换时间、健康结果、异常、是否回滚和结束时间。连续几次发布后,你会知道真实耗时和最容易失败的阶段,才能继续自动化。 完成标准是:候选版本可独立验证,切换不需要重启整个代理,旧版本能在几分钟内恢复,数据迁移兼容策略清楚。下一篇建立能及时发现退化的 服务器监控与告警。
参考来源
- Dockerfile HEALTHCHECKDocker Docs
- Caddy reverse_proxy directiveCaddy Documentation