今天 (2021年12月前后 )大家都在热议 AWS down 掉的事情,突然想起来了2017年 gitlab.com 的宕机事件,所以又去回顾了一下当时究竟发生了什么,顺便也根据当时事件的记录整理一个中文版本,其中还是有很多东西值得我们学习的。
事件回顾
这次事件发生在 2017 年 1 月 31 号,当时 gitlab.com 整个网站不能访问,持续时间从 17:20 UTC 到第二天 17:00 UTC。而且更严重的是这个事件最终造成了 production 数据的丢失,粗略估计有约 5000 个 project,5000 个 comments 以及 700 个新用户账户丢失了(6 个小时左右的数据丢失),Gitlab 商业版本没有受到任何影响。
数据库的建立
这次宕机事件其实是发生在数据库层面的一次灾难,所以在详细分析之前,我们来看看 gitlab.com 当时的数据库是什么样的架构。当时的gitlab.com 使用的是一个主数据库加一个热备份数据库的架构,和我很早之前在的一个内部 tool 组使用的数据库架构类似,而且几乎所有的负载都是直接到主数据库那边的(也因此出过好几次负载相关的事故)。
时间线
事情的起因很简单,就是 gitlab.com 有人说 AWS 使用了 Pgpool-2 来做负载均衡,我们现在使用的是一个 Azure 的 LB,要不我们也来试试Pgpool-2,所以他们就开始在 staging 环境中进行测试。没想到吧,原来这次宕机的起始也和 AWS 有关(当然根源和他没关系,相反这个 feature 的开发还多拯救了一些数据)。
17:20 UTC: 为了在 Staging 环境测试这个功能,他们的工程师就从 production 上拉了一个 LVM 的 snapshot 下来了,这样做主要还是想让staging 的环境和 production 的环境数据类似,测试的结果更准确,一般来说 LVM 的 snapshot 每天也会自动备份一份,只是当时的程序员想要一个最新的进行测试,就去 production 上拉了一个最新的。注意就是这份拷贝最后起到了大用处。
19:00 UTC:这个时候 gitlab.com 的人发现他们的数据库的负载突然加大了,后来发现主要的原因是一个后台程序试图删除一个被 abuse 的员工账号导致的。
23:00 UTC:由于这个负载很大,上面提到的备份数据库的 replication 跟不上了,而且 lag 越来越大,直到备份数据库的 WAL 部分都被删除了,也就是说这个时候唯一能让备份数据库和主数据同步的方法就是把备份数据库手动删除了,然后再同步一次。所以就有一个员工去真的删除了备份数据库(传说的删备份库),然后再次运行了同步的命令,没想到这个命令运行了两次都没有啥反应,这个员工就以为这个备份命令出问题了。虽然他尝试就做了一些debug的操作,但是没有发现原因。
23:30 UTC:后来有员工说这个命令开始没反应是正常的,毕竟需要一段时间才能开始真正的同步,但可惜的是这些没有被写在文档里面,前面操作的员工根本不知道这件事情(文档是多么重要啊)。在上面命令不工作的时候,又有员工来帮忙一起看,准备重试上面的操作,上面的操作是什么大家还记得吗?就是先把数据库(备份)删了,然后再重启同步。这次来帮忙的员工也准备重试一下这个操作,很多时候你那边运行不行,我这边试试说不定就好了,哈哈。可惜的是这个员工在做删除的操作的时候不小心操作在主数据库上!!!(真的传说中的删主数据库发生了),虽然他几秒钟的时间就意识到了这个问题,赶快各种ctrl+z,还是很不幸有近300GB的数据被删除了(说好的同事情谊啊)。
这就是整个事件发生的过程,没有太多的 surprise,这种事情在我们日常工作中很容易就发生,下面要做的就是怎么恢复数据了。
尝试恢复
下面就是开始尝试恢复,要想恢复总得找到数据啊,现在主库的数据没了,备份数据库的数据也没了,但是 gitlab.com 还是有一些别的数据,这些数据保存下来是因为一些别的原因:
- 每24小时备份一次数据库,并且上传到 Amazon 的 S3。每次上传都会覆盖旧的数据。
- 每24小时备份一次 LVM snapshot,这个 snapshot 是针对数据库所在磁盘进行备份的。同时这个 snapshot 被用来作为 staging 环境的测试数据,这样开发的时候不会影响真实数据。
- 有些服务器(NTFS server)还会每 24 小时做一个 Azure 磁盘 snapshot。
- PostgreSQL host 之间的 replication,主要用来做 failover 的。(就是我们上面说的备份数据库)
显然现在第 4 点的备份数据库已经没了,那我们就只能尝试从另外 3 个当中来进行恢复了。我们再来详细看看当时这三个备份的情况是怎么样的:
Amazon S3
首先我们来看看那个 24 小时备份到 S3 的数据库怎么样了,当去看的时候才发现这个备份的文件夹竟然是空的,对是空的!!!难以相信他们没有任何 monitor 和 alert 来监控这个。原因也很简单,就是 PostgreSQL 的版本不兼容,使用的备份程序是 pg_dump9.2,而他们当时运行的数据库已经升级到了 PostgreSQL9.6,两者不兼容。当时 gitlab.com 支持 9.2 和 9.6 两个版本,有一套机制来向前兼容,但是这套机制是在数据库层面,而 gp_dump 则是在应用层面,这套机制在这个层面不 work。哎,一个备份丢失,虽然在也不一定用得上。
Azure Disk Snapshots
Azure Disk snapshot 主要是用来防止磁盘出问题的,从它上面恢复数据是可以的,但是比较复杂。这个 snapshot 当时是存在 Azure storage 上的,每个 storage account 有一个 30 TB 的限制,就说同一个 storage account 的恢复速度很快,但是不同 storage account 的 host 进行数据恢复比较慢,所以在最初的时候他们就没有想用这个进行数据恢复,因此这个 feature 也只是在支持 NTFS 的服务器上使能了,其它的服务器没并有支持这个功能。
LVM Snapshot
如我们是上面所说,这个 snapshot 是用来拷贝 production 数据,然后放到 staging 环境中的,在当时有两个可用的拷贝:
- 正常的每 24 小时同步一次的数据,当时这个 snapshot 也差不多就是24小时之前的了。
- 上面提到的程序员自己通过同样的程序拷贝的一份用于测试 pgpool-2 的数据,这个数据大概是六小时前的拷贝的。
正式恢复
在有了上面这些分析之后,gitlab.com 最终决定使用那个六小时前的 LVM 版本来恢复(功臣啊),所以大概的就是把这个 snapshot 从staging拷贝到了 production,然后再 re-enable gitlab.com。然而 staging 的环境使用的是 Azure 的标准环境,没有使用 premium storage,当然主要还是从 cost 的角度考虑,毕竟只是一个开发环境,标准环境也够了,然而标准环境要拷贝数据的速度就不那么快了,大概只有 60 Mbps(瓶颈主要在磁盘),而且没法从标准环境立即改到 premium 的环境,所以最终这个拷贝花费了近18个小时。所以整个拷贝完成并使得数据库重新运行的时候已经是第二天的 18:00 UTC 了。
总结
这个就是整个事件发生的全部过程,很显然这里有很多需要值得注意的地方。Gitlab.com 他们自己当时也列出了多达 15 个 work item 来进行改进,我们也可以发现这件事情的背后有很多偶然,但也存在着很多必然,就如他们CEO在总结博客中所说,我们的系统不应该是一个不能犯错的系统,而应该是一个允许犯错,但能够快速恢复的系统。
这句话真的很不错,值得与君共勉。
-------------------------------------------------------------------
参考:http://www.manongjc.com/detail/50-vmukmjceznujfhy.html
https://www.tmtpost.com/2565002.html