首页 › 案例
GitLab · 2017

空桶

工程师在错的数据库上执行了删除,一两秒后才叫停。接着,GitLab 去找它的备份。

◷ 约 2 分钟

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 发布了复盘。它说今后的重点,是让犯了错能轻松恢复,而不是禁止工程师敲某些命令。整改里有一条:让每台服务器的命令提示符更醒目,看得清自己正在往哪台机器上敲字。

复盘的最后一个「为什么」问的是:备份为什么从没定期测试过?答案是:「因为没有归属,结果就没有人负责测试这个流程。」

这件事里藏着几个道理

瑞士奶酪模型删除命令敲在了错的服务器上,备份任务用错了版本,报错邮件被收件服务器退回,数据库没开磁盘快照,备库又已经被清空。这几个洞各自独立,偏偏在这一夜对上了。不追责复盘GitLab 把抢修笔记公开,还直播了恢复过程。复盘从那次误删开始追问,一路问到垃圾信息、恶意举报和没人测过的备份。五个为什么GitLab 的复盘当众把「为什么」问到底。为什么恢复花了这么久?一连问了八层,最后落在「没有归属」上。直接负责人(DRI)GitLab 有好几种留副本的办法。按它自己的复盘,没有一个人负责测试这些备份。整改清单里有一条:「为数据持久性指定一位负责人」。

出处

  1. GitLab, Postmortem of database outage of January 31, February 10, 2017
  2. GitLab, GitLab.com database incident, February 1, 2017