1. 升级脚本基础认知
说实话,很多人一听到「升级脚本」就觉得很简单——不就是写几行命令把新版本部署上去吗?我在项目里见过太多因为这种想法翻车的案例了。今天咱们就把这个基础打扎实。
1.1 什么是升级脚本
升级脚本,说白了就是一套自动化执行升级操作的程序。它把人工操作步骤变成可重复、可验证的代码。你想想看,一个系统从 v1.0 升到 v2.0,中间要停服务、备份数据、替换文件、跑迁移、验证结果……这些步骤靠人手动敲,早晚会出问题。
核心定义:升级脚本 = 操作步骤 + 状态检查 + 异常处理 + 回滚能力
我个人习惯把升级脚本分成三个层次:
- 原子脚本:只做一件事,比如停服务、备份数据库、替换配置文件
- 组合脚本:把多个原子脚本串起来,形成完整的升级流程
- 编排脚本:管理多节点、多服务的升级顺序和依赖关系
我在项目中遇到过最典型的例子:一个同事把停服务、备份、替换、重启全写在一个 200 行的 shell 脚本里。结果备份那步磁盘满了,脚本直接崩掉,服务也停了,数据也没备份成。嗯,这就是没理解脚本分层的重要性。
1.2 升级脚本的常见场景
升级脚本不是只有「应用发布」这一种场景。我整理了三个最常见的场景,每个都有不同的坑。
| 场景 | 典型操作 | 常见风险 |
|---|---|---|
| 应用发布 | 停服务 → 备份旧包 → 替换新包 → 启动服务 → 健康检查 | 版本回退困难、依赖缺失、配置不兼容 |
| 数据库迁移 | 备份数据 → 执行 DDL → 数据转换 → 校验一致性 | 数据丢失、表结构冲突、迁移超时 |
| 配置变更 | 备份配置 → 修改参数 → 重载服务 → 验证生效 | 语法错误、参数范围越界、影响范围不可控 |
应用发布场景
这是最基础的场景。我建议你每次发布都走「蓝绿部署」或「灰度发布」的思路,脚本里必须包含回滚入口。我曾经见过一个发布脚本,升级到一半网络断了,服务处于「半死不活」的状态——旧包删了,新包没传完。从那以后,我写的每个发布脚本第一件事就是做全量备份。
数据库迁移场景
这个场景最考验脚本的严谨性。为什么?因为数据丢了很难找回。我记得有一次做 MySQL 表结构变更,脚本里直接执行了 ALTER TABLE,没检查外键约束。结果生产环境有脏数据,迁移直接卡死。后来我养成了习惯:任何 DDL 操作前先做兼容性检查。
我的经验:数据库迁移脚本一定要支持「幂等执行」。同一个脚本跑两次,结果应该一样。用 IF NOT EXISTS、IF EXISTS 这类语法,或者先检查再执行。
配置变更场景
配置变更看着简单,其实最容易出幺蛾子。你想想看,改一个参数可能影响整个集群的行为。我建议在脚本里加两步:语法校验和影响范围评估。比如改 Nginx 配置,先跑 nginx -t 检查语法,再输出当前配置的 diff 让运维确认。
1.3 脚本的生命周期管理
很多人写完脚本就扔那不管了。其实升级脚本跟代码一样,需要管理生命周期。我把它分成五个阶段:
- 开发阶段:写脚本、做单元测试、做集成测试
- 评审阶段:代码审查、安全审查、回滚方案审查
- 测试阶段:在预发环境跑完整流程,验证结果
- 发布阶段:正式执行,记录日志,监控状态
- 归档阶段:脚本入库,打标签,写变更记录
注意:我见过最惨的案例是——脚本在测试环境跑得好好的,到生产环境就挂了。原因很简单:测试环境的目录结构、权限、依赖版本跟生产不一样。所以脚本里一定要做环境预检,别假设环境是「标准」的。
这里我画了一张升级脚本生命周期的流程图,帮你理清整体脉络:
你看这个流程,关键点在于每个阶段都有明确的进入和退出条件。比如测试阶段没通过,就不能进入发布阶段。回滚分支是独立的,一旦发布失败立刻触发回滚,回滚完成后重新走开发或评审流程。
我的建议:给每个脚本加一个版本号,格式建议 vYYYYMMDD-N。归档时把脚本、执行日志、变更记录一起打包。这样半年后回头看,还能知道当时为什么这么改。