Build Log / 部署实践

一人公司部署方案:
预验证与快速回滚

一台生产服务器,先让新版本证明自己能工作,再把流量交给它;旧版本留下来,出问题时有路可退。

·5 分钟阅读·阿策搞AI

独立开发最怕的不是部署步骤多,而是上线后才发现新版本不能用。这份流程来自我的单机 Docker 项目:用一个临时的候选容器完成发布前检查,再切换正式服务,并保留旧容器。它不需要新增测试服务器,但需要你认真写检查项、处理代理和数据迁移。

先设两道闸门

01 / 上线前验证先于切流

候选版本独立启动。关键页面、内容和后端连通性过关后,才碰线上服务。

02 / 上线后切流留有退路

旧容器和它对应的镜像保留一段时间;公网复验失败时,恢复旧版。

这里的“零成本”指不增加一台测试服务器,不代表发布没有时间成本。预验证也只能挡住覆盖到的故障。

一次发布,走完五步

  1. 给镜像一个能追溯的版本号

    用日期加 Git 短哈希,例如 20261004-a1b2c3d。发布记录要能对应到代码提交;不要只依赖会被覆盖的 latest 标签。

  2. 在隔离环境启动候选版本

    旧版继续对外服务,新版先不接入公网代理。在我使用的“容器改名晋升”流程里,候选容器也不发布宿主机端口;检查从容器内或专用网络进行。多服务项目要一起验证依赖。

  3. 做真正有用的冒烟检查

    至少查启动存活、关键页面内容、后端健康接口和必要配置。只看到 HTTP 200 不够;检查应能识别“页面能开,但内容或配置是错的”。失败就清理候选版本,结束这次发布。

  4. 切流后再从公网确认一次

    停下旧容器并改名留存;停下候选容器,改为正式名称,接入正式网络与代理,再启动新版。若代理配置依赖启动时解析的容器地址,按实际配置重载代理。随后走正式入口检查状态和内容,别把容器内检查当作发布成功。

  5. 失败就恢复旧版,并再次复验

    停掉失败版本,恢复旧容器及代理连接,确认正式入口重新可用。旧镜像和容器至少覆盖你的观察窗口;回滚脚本应记录切流阶段,处理“旧版已停、新版尚未启动”这类中途失败。

最容易漏掉的三个细节

  • 端口绑定跟着容器走。如果你的方案靠改名晋升候选容器,不要给候选版本占用正式宿主机端口;否则下一轮发布可能撞端口。其他部署拓扑应按自己的入口设计。
  • 网络连接也要纳入回滚演练。候选版切流时需要什么网络、旧版恢复时需要什么网络,都要在脚本里明确处理。不要在仍有容器依赖时清理网络。
  • 代理和公网是最后一关。容器自身健康不等于用户能访问。切流或回滚后,检查代理解析与路由,并通过正式入口复验。

哪些事,这套流程兜不住

它不是零停机保证。单机停止旧容器再启动新版,仍可能有短暂中断;耗时也会随应用启动、健康检查和代理配置变化。目标是缩短故障暴露时间,让恢复步骤可执行。

数据库要单独设计退路。镜像能切回旧版,不代表 schema 和数据能自动恢复。迁移前备份,并优先设计新旧代码都能短期兼容的变更;破坏性迁移要有单独的恢复方案。

冒烟检查挡不住长尾问题。重要业务路径还需要人工或自动化验证,发布后也要观察日志与真实请求。

发布前的最小清单

  • 镜像带可追溯版本号,旧版镜像与容器在观察窗口内保留。
  • 候选版本与公网入口隔离,并完成内容、接口、配置检查。
  • 切流和回滚都包含代理处理与公网复验。
  • 涉及数据库迁移时,先备份并确认兼容与恢复步骤。
  • 上线前完整演练一次:部署新版、回到旧版、再切回新版。

我的经验是:先把手动流程走通,再封装为 deploy.sh 和 rollback.sh。脚本真正有价值的地方,是失败发生在任何一步时,都知道当前处于哪个阶段,以及下一步该恢复什么。