为什么难做到原子
一个部署动作通常包含下载制品、解压、更新配置、重启服务、健康检查等多步操作,任何一步失败都会在磁盘上留下混合状态:新版本文件已就位但服务还在跑旧进程。真正原子化需要底层系统支持事务,而这在操作系统层面基本不存在。可行替代是分步提交:每步都有明确的成功判据,失败即停,并保证从任一中断点都能重跑或回退。
基本信息
| 原子性障碍 | 多步操作、无统一事务、外部副作用 |
|---|---|
| 替代做法 | 分步提交 + 每步可验证 + 可重跑 |
| 两段式结构 | 先准备好(写入临时位置),再切换(生效) |
| 中断处理 | 保留断点信息,支持从断点续跑或回退 |
分步提交要点
- 先准备后生效:制品先落地到临时目录并校验,再通过一次切换动作生效。
- 切换动作尽量短:生效步骤越短,失败窗口越小。
- 每步有判据:每一步都要有可检查的结果,而不是靠下一步失败来暴露。
- 中断可识别:记录执行到哪一步,重跑时能跳过已完成步骤。
- 回退有依据:保留上一版本与上一份配置,确保可切回。
- 清理有步骤:临时目录、旧版本与锁文件要有明确清理动作。
先准备再切换
先准备再切换
release=/opt/app/releases/1.4.2
staging=/opt/app/.staging-1.4.2
mkdir -p "${staging}" # 第一步:在临时目录准备
cp -a ./build/. "${staging}/" # 制品落地
sha256sum -c checksums.txt --status || exit 1 # 校验,失败即停
mv -T "${staging}" "${release}" # 第二步:一次切换生效
ln -sfn "${release}" /opt/app/current # 更新软链指向新版本
systemctl restart app && systemctl is-active app # 后置校验分步设计与中断表现
| 步骤 | 成功判据 | 中断后的状态 | 处置 |
|---|---|---|---|
| 准备制品 | 校验通过 | 临时目录残留 | 重跑覆盖临时目录 |
| 切换生效 | 软链指向新版本 | 指向旧版本 | 重新执行切换 |
| 重启服务 | 健康检查通过 | 服务未起或半起 | 切回旧版本并重启 |
| 清理旧版 | 旧目录已删除 | 旧版本仍在 | 重跑清理步骤 |
把最危险的一步放在最后单独确认,是分步提交最实用的价值:即使前面所有准备都出错,只要切换动作没执行,业务就仍然跑在旧版本上。
涉及数据格式变更时,切换后很难回退:新版本写出的数据老版本可能读不了。这类变更应在执行前完成备份,并明确不可回退点。