镜像打好了,容器启动了,健康检查也是绿色。打开用户入口,看到的却还是旧版本。
这不一定是代码没更新。可能新实例还没接流量,可能入口指向另一组服务,也可能应用加载的配置仍然来自旧文件。
服务内部的成功信号很重要,但它们需要与用户真正经过的路径连接起来。
发布不是一个瞬间
可以把一次发布拆成几个容易检查的事实:
| 阶段 | 可以证明什么 | 还需要检查什么 |
|---|---|---|
| 构建成功 | 某份代码能生成产物 | 产物是否对应目标提交 |
| 实例启动 | 新实例可以运行 | 是否加载了预期配置 |
| 流量切换 | 入口开始指向新实例 | 用户请求是否实际到达 |
| 入口验证 | 真实路径返回预期结果 | 关键业务流程是否完成 |
| 业务验收 | 目标用户完成了任务 | 是否满足持续运行要求 |
小项目不一定需要复杂平台,但需要知道自己现在证明到了哪一步。
给运行中的版本留一个标识
提交号有用,但只知道代码版本还不够。对于知识问答应用,答案也可能受提示词、知识快照和模型配置影响。
可以在受控诊断接口或内部日志中记录:
1 | { |
这些值应当来自实际加载的产物和配置。把“准备部署的版本号”硬编码进健康检查,会让检查失去意义。标识里也不应包含凭证、配置正文或内部资料内容。
缓存同样需要版本边界。如果知识快照已经更新,而答案缓存仍复用旧结果,就算流量切换正确,用户也可能持续看到旧答案。
从用户入口发一次有意义的请求
直接访问服务端口可以排查实例问题,但无法覆盖反向代理、认证、路由和缓存。
发布后,可以从正式入口选择一个稳定的代表性问题或操作,检查输入、身份、响应、版本标识以及对应日志。对于多副本服务,还要结合部署控制面的实例状态确认切换进度;连续请求几次并不能证明所有副本都已更新。
如果请求会产生订单、邮件或其他副作用,应使用已有的测试机制或经过授权的可撤销样本。不要为了验证一次发布制造真实业务噪声。
回滚也需要验证
“重新启动旧镜像”并不总是完整回滚。数据库结构可能已经改变,缓存里可能保存了新格式的数据,消息队列里也可能存在旧版本不认识的事件。
因此,发布前要看清楚这次修改涉及哪些状态。只有代码变化时,回滚可能比较直接;涉及数据格式或外部写入时,需要额外的兼容设计和恢复步骤。
对于 AI 应用,可以把模型配置、提示词和知识快照作为有版本的组合记录。回滚后再次读取运行状态,再从用户入口验证关键问题,确认恢复的是原本预期的行为。
留下别人可以复核的证据
一次简洁的发布记录可以包含:
- 发布的产物标识,以及本次行为变化。
- 生效入口、实际运行版本和验证时间。
- 代表性检查的输入与结果,必要时保留去敏后的请求编号。
- 已完成的验收、尚未覆盖的场景,以及回滚位置。
不要把“测试通过”写成没有范围的一句话。可以明确写“已通过本地回归,尚未切换线上”,或“线上已生效,关键流程通过,仍待真实用户确认”。
这样的表达不会削弱工作成果,反而能让接手的人知道接下来应该看哪里。
把这件事缩小到个人博客
个人博客也有同样的问题:Markdown 写好了,不代表页面生成正确;页面生成正确,不代表域名已经展示新内容。
最小做法是先本地预览,再核对发布目录中的文章、资源和链接;推送后访问正式域名,检查标题和一段本次新增的正文。必要时查看响应缓存信息,确认自己读到的版本。
不管系统大小,最后那次从真实入口出发的检查,都在回答同一个问题:用户现在使用到的,是否就是这次准备交付的东西。