恢复 Halo 博客:一次双实例事故复盘
表面现象是网站还能打开,但文章、主题和账号像全部重置。最危险的第一反应是立即在新页面里重新初始化,因为这可能覆盖仍然存在的旧数据。排查后确认,同一台服务器上运行过两套 Halo:反向代理指向了后来创建的空实例,原实例及其 PostgreSQL 数据卷仍在磁盘中。
先停止扩大变化
我没有删除容器或重装应用,而是记录当前 Nginx upstream、容器列表、卷映射和监听端口。数据库与附件目录先做只读备份,备份文件计算校验值并复制到独立目录。只有确认可以回到原状态后,才开始切换。
判断哪个实例是原站点不能只看容器创建时间。需要同时比较用户、文章数量、主题名称、附件目录、站点标题和数据库更新时间。原实例中保留了管理员账号、Joe3 主题及历史文章,空实例只包含初始化数据,因此目标明确。
切换分成三步
第一步让原数据库和 Halo 容器在内部端口启动,不对公网切流;第二步直接访问健康接口和公共 API,确认文章、分类、主题资源可以读取;第三步修改 Nginx upstream,语法检查通过后平滑重载。
备份当前状态 -> 启动原实例 -> 内部验证
-> 修改 upstream -> nginx -t -> reload -> 外部验证
整个过程保留新实例,不立即销毁。若原实例启动失败或页面异常,只需恢复 upstream 即可回到切换前状态。
容易遗漏的验证
首页返回 200 不代表恢复完成。验证清单包括登录页、文章正文、附件、站内搜索、管理后台、主题静态资源和数据库写入。还要检查站点外部地址,否则生成的 canonical 链接、登录跳转和附件 URL 可能继续指向旧 IP 或错误协议。
恢复后重新确认自动启动策略、容器网络和数据卷实际路径,避免下次系统重启又由另一套实例抢占端口。所有人工命令整理成恢复记录,并注明哪些容器暂时保留、何时可以清理。
事故根因与改进
根因不是数据库消失,而是服务清单缺失,新的部署没有发现已有实例,反向代理修改也没有做内容级验收。改进包括:
- [x] 为每个域名记录唯一 upstream、项目目录和数据卷
- [x] 部署前检查同名服务、监听端口和现有数据库
- [x] Nginx 变更前自动备份并执行语法测试
- [x] 切流后验证标题、文章数和指定内容,而非只看状态码
- [x] PostgreSQL 定期逻辑备份,并实际导入临时实例
- [x] 废弃实例先隔离观察,再按清单清理
结论
“网站空了”并不等于“数据没了”。容器化环境里,应用、数据库、卷和反向代理是四个独立事实。恢复时要先识别真实数据源,再建立可回滚切换,最后做内容级验证。越紧张的时候,越应该避免删除和覆盖;保留证据、逐层确认,往往比重新安装更快。
评论区