把 PostgreSQL 从 16 升到 18,命令本身是文档里抄的;真正花时间的,是让新旧环境在细节上对齐。我们按"每一关都验证过再进下一关"的顺序走完了这次升级,把过程与坑记录下来。
背景与原则
升级对象是一个容器化部署的业务库(应用与数据库分属两台容器)。定下两条原则:生产库不试错——任何未验证的步骤先在临时环境走一遍;回退路径先于升级路径——先确认"升坏了怎么回去",再执行升级。
第一关:数据目录的挂载点变了
新版本官方镜像调整了数据目录的布局。沿用旧写法的挂载,容器启动时数据目录是空的或不可写,表现为初始化失败或"数据消失"。这不是数据损坏,而是挂载没有落在新版本期望的位置。
检查方法很直接:对比新旧镜像的目录结构,确认挂载点、数据目录与属主三者一致。教训是——大版本升级前先读目标镜像的变更说明,把挂载与属主当作需要复核的对象,而不是"照抄旧编排文件就行"。
第二关:环境变量从哪来
升级过程中还撞上了经典问题:镜像构建时烘焙了环境变量。入口脚本"未设置则默认"的写法,被永远"已设置"的烘焙变量绕过,服务连向了错误的主机。表面症状是"连不上数据库",第一反应很容易去查网络。
排查路径(详见我们另一篇复盘):打印运行时环境 → 与预期逐项比对 → 定位覆盖来源。防复发的手段是入口脚本改为显式导出,并加启动自检:关键依赖不可达就明确报错,不静默降级。
第三关:备份要可恢复,才算备份
升级前置里最重要的一条:备份不是"看到文件生成",而是"验证过可恢复"。我们的检查表:
| 步骤 | 验证方式 | 通过标准 |
|---|---|---|
| 全量导出 | 列出归档内容 | 表结构完整、无报错 |
| 临时环境恢复 | 实际还原一次 | 服务可启动、无告警 |
| 业务级抽查 | 关键表行数、最新单据、附件完整性 | 与源库计数一致 |
| 回退路径 | 在临时环境模拟回退 | 可回到旧版本运行状态 |
只看"服务能启动"是不够的——服务启动和业务数据完整是两回事。
试升级与切换窗口
有了可恢复的备份,才进入实质升级:先在临时环境对全量数据做一次"试升级",跑一遍应用的冒烟用例(登录、关键单据、报表)。试升级通过后,切换窗口里的正式升级基本是照表执行:
- 停写(维护窗口开始,应用切换只读);
- 最后一次增量备份 + 校验;
- 升级数据目录(全程计时);
- 启动新版本,跑冒烟用例;
- 恢复写入,观察窗口内盯关键指标(连接数、慢查询、锁等待)。
复盘:三个可复用的结论
- 顺序不能省:读变更说明 → 复核挂载与属主 → 比对运行时环境 → 验证备份可恢复 → 试升级 → 业务级校验 → 正式切换。每一步都不复杂,顺序错了就返工。
- 大版本升级的功夫在升级之前:真正执行升级的时间很短,前面的准备决定了它是十分钟还是十小时。
- 清单要回流:这次的三类坑(挂载、环境变量、备份验证)都加进了部署检查单——下次升级不必再重新发现一遍。
