昨天晚上写那篇小鸡和手机存储的文章,结尾顺手挖了个坑:“明天记得给服务器做个快照”。今天是周日,早上备份 cron 刚跑完,趁着坑还新鲜,决定把这事办得再彻底一点:不只是快照,来一次完整的恢复演练。
说干就干,理由很简单。九月底配备份那篇写过,脚本每周日凌晨三点跑一次,本地留四周,rsync 推一份到对象存储。之后我又写过两次"睡前看一眼备份时间戳"的仪式,每次看到文件都在,就安心关灯。直到前几天半夜躺床上,我突然意识到一个问题:我验证的只是"备份存在",从来没人验证过"这包东西能恢复"。
这两件事完全不是一回事。tar 包躺在对象存储里,不代表解压出来是完整的,更不代表照着它能把博客原地拉起来。备份脚本跑一百次成功,只能证明 tar 和 rsync 没坏,证明不了我哪天真用得上它。
于是今天上午,我在一台小鸡上开演。流程不复杂:从对象存储拉最新的包下来,解压到临时目录,diff 一遍 content 和 static 跟线上的差异,然后把 Hugo 的源文件在临时目录里完整构建了一次,起的页面跟线上的对比着看。
结果,还真让我抓到一个东西。博客源文件和 nginx 配置都在,都完整。但我当初在脚本里放了个证书路径的清单文件,十月初从 Docker 迁到系统 nginx 之后,证书挪进了 /etc/nginx/ssl,清单没跟着更新。也就是说这五周的备份里,证书的记录一直是旧的。博客源文件丢了我哭一场还能重写,证书要是不在了就得重新走一遍签发流程,网站当场全站 403。不算大 bug,但演练的价值就在这:它是在我心态平静的周日上午被发现的,而不是在某个手忙脚乱的事故现场。
修完之后把流程从头再跑一遍,这次全部通过。从拉包到本地构建出完整的页面,全程二十分钟。二十分钟,换来以后每次看到备份文件时那份"安心"是真的成立,这买卖太划算了。
顺手把这周的备份记录翻了一下,rsync 到对象存储那边两周多一次没断过,脚本比我想的靠谱。真正不靠谱的是我,配完就当任务结束了,压根没想过"备份"这个词的宾语不是文件,是恢复。
写代码的人应该都懂这个感觉:测试通过不代表功能正确,只代表你想到了的路径是对的。备份也一样,备份完成不代表能恢复,它只验证了你想到了的那一半。今天补上了没想过的那一半。
最后还是那句给同样跑小鸡的朋友:去看看你的备份,然后找个周日,真的恢复一次。时间戳只能告诉你它在,恢复演练才能告诉你它能用。
坑填了,就这样。