2017 年 1 月 31 日深夜,当地时间 11 点左右,GitLab 的一位工程师早就说过要下线休息了。偏偏这时数据库的复制断了,他只好留下来。
GitLab.com 是开发者存代码、管任务的网站,背后是两台数据库服务器。主库 db1 扛下所有读写;备库 db2 存一份副本,万一 db1 出事好顶上。那天傍晚早些时候,一波负载冲上来,GitLab 怀疑是垃圾信息,db2 被甩得太远,干脆不再同步。修法是把 db2 清空,从头再拷一遍。
可拷贝怎么也启动不了。团队改了配置,重启了数据库,还上了调试工具,它就是一声不吭地挂在那儿。那位工程师猜,是 db2 数据目录里残留的文件在捣乱,于是敲下命令,删掉整个目录。
一两秒后,他发现自己是在哪台机器上敲的:db1,主库。UTC 时间晚上 11 点 27 分,他叫停了删除。大约 300 GB 的数据,只剩下 4.5 GB 左右。
(按 GitLab 复盘的说法,那个拷贝其实根本没卡住。这个工具本来就会一声不响地等主库开始发数据,而 GitLab 的运维手册没把这一点写清楚。)
按说,这种时候备份一上场,故事就该平淡收尾了。GitLab 的备份不止一种。团队一边找,一边把发现记进一份 Google 文档,这份文档后来被 GitLab 公开了。
每天跑的数据库导出,用的一直是旧版导出工具,和新版数据库对不上,什么也没导出来。任务失败时倒是会发邮件,可邮件没有按收件服务器要求的方式签名,全被退了回去,GitLab 从来不知道备份一直在失败。本该存放导出文件的那个 Amazon S3 存储桶,是空的。Azure 的磁盘快照给文件服务器开了,数据库服务器却没开。至于备库,正是刚才被团队亲手清空的 db2。照那份实时笔记里的统计,部署了五种备份和复制手段,没有一种在可靠地工作,有的根本就没配好。
救命的是一个巧合。误删前大约六小时,就是这位工程师,为了手头的负载均衡工作,手动给数据库拍过一张快照。快照放在一套测试环境里,那套环境用的是便宜的慢速磁盘,把数据拷回来花了大约 18 个小时。GitLab 在 YouTube 上直播了整个恢复过程。按它的统计,同时在看的人最多时约有 5000,有好几个小时,这是全站排第二的直播。
UTC 时间 2 月 1 日下午 6 点 14 分,GitLab.com 恢复上线。大约六小时的数据库改动没能找回,GitLab 估计约有 5000 个项目、5000 条评论和 700 个新注册账号。代码仓库没有受影响。
十天后,GitLab 发布了复盘。它说今后的重点,是让犯了错能轻松恢复,而不是禁止工程师敲某些命令。整改里有一条:让每台服务器的命令提示符更醒目,看得清自己正在往哪台机器上敲字。
复盘的最后一个「为什么」问的是:备份为什么从没定期测试过?答案是:「因为没有归属,结果就没有人负责测试这个流程。」