【CSDN编者按】10 月下旬,全球最大的同性交友网站 GitHub 挂了,引得一大批吃瓜群众围观吐槽。很多人纷纷诟病,“GitHub 也不过如此”、“还我代码”、“微软收购 GitHub 真是一团糟”等等,叫嚷着 GitHub 给个说法。时隔数日,GitHub 技术总监 Jason Warner 在其官方博客上终于给出了一份姗姗来迟的技术故障解读。
以下为译文:
上周,GitHub经历了一次意外事件,导致服务降级24小时11分钟。虽然我们平台的一部分没有受到此次事件的影响,但多个内部系统均受到影响,导致显示信息陈旧且不一致。万幸没有用户数据丢失;但是,我们依然在手工核对几秒钟的数据库写入事件。此次事件的主要影响是,GitHub无法提供Webhook事件的服务,也不能构建以及发布GitHub Pages网站。
GitHub所有员工在此谨对受此次事件影响的每个人表达真诚的歉意。我们明白您对GitHub的信任,并自豪地为各位构建弹性系统,以确保我们平台的高可用性。在此次事件中,我们出现了失误,我们深感抱歉。
虽然我们在很长一段时间内依然无法消除GitHub无法使用而造成的影响,但是我们可以解释导致此次事件发生的始末,我们吸取的教训,以及公司采取的相应措施,确保不会再次发生这种情况。
1.背景
大多数面向用户的GitHub服务都在我们自己的数据中心设施上运行。数据中心的拓扑旨在提供强大且可扩展的边缘网络,该网络背后是多个区域数据中心,为我们的计算和存储工作负载提供动力。尽管物理和逻辑组件中设计了多层冗余,但站点之间仍然可能无法在一段时间内相互通信。
在UTC时间(世界标准时间)10月21日22:52,在日常维护工作中更换发生故障的100G光纤设备时,引发了美国东海岸的网络中心与美国东海岸主要的数据中心之间的连接中断。这两个地方之间的连接在43秒后得到了恢复,但是这次短暂的中断引发了一系列事件,导致了24小时11分钟的服务降级。
过去,我们曾经讨论过如何利用MySQL存储GitHub的元数据,以及MySQL的高效利用方案。GitHub上运行了多个MySQL集群,其大小从几百GB到将近5TB不等,每个集群最多有几十个只读副本来存储非Git的元数据,确保我们的应用程序提供拉取请求和问题、管理身份验证、协调后台处理等服务,还可以提供原始Git对象存储之外的其他功能。应用程序不同部分的不同数据通过功能分区存储在各个集群中。
为了提高大规模服务的性能,我们的应用程序会直接写入每个集群中的主服务器,但在绝大多数情况下读取请求交由副本服务器处理。我们使用Orchestrator来管理我们的MySQL集群拓扑并处理自动故障转移。在这个过程中Orchestrator会考虑许多因素,并根据Raft共识算法构建拓扑。 Orchestrator可能会生成应用程序无法支持的拓扑,因此必须谨慎地配置Orchestrator,保证它与应用层的期望一致。
2.事件的时间线
2018年10月21日22:52 UTC
在前面提到的网络分区中,Orchestrator正好在主数据中心激活,并根据Raft共识算法开始了领导者选举过程。美国西海岸数据中心美国东海岸公共云Orchestrator节点成功地达到了法定人数,并开始了故障转移过程,将写请求直接导向至美国西海岸数据中心。Orchestrator继续生成美国西海岸的数据库集群拓扑。在连接恢复后,我们的应用程序将写入请求导向了西海岸的新的主服务器。
于是,在那一短暂时刻发生的写操作留在了美国东海岸数据中心的数据库服务器中,没有被复制到美国西海岸的数据中心。现在,由于两个数据中心中的数据库集群都包含另一个数据中心没有的写入数据,因此我们无法安全地将主数据库重新转移回美国东海岸数据中心。
2018年10月21日22:54 UTC
我们的内部监控系统开始出现警报,表明我们的系统遇到了大量故障。这时已经有几位工程师在积极响应并对传入的通知进行分拣。
到23:02 UTC时,我们的第一响应团队的工程师已经确定许多数据库集群的拓扑处于意外状态。Orchestrator API的查询结果显示,数据库副本拓扑仅包含来自美国西海岸数据中心的服务器。
2018年10月21日23:07 UTC
在这一刻,响应团队决定手动锁定我们的内部部署工具,以防止引入其他变化。到23:09 UTC时,响应团队宣布网站进入黄色状态。这一举动会自动将情况升级为意外事件,并向事件协调员发送警报。事件协调员于23:11 UTC加入,并在两分钟后宣布此次事件为红色状态。
2018年10月21日23:13 UTC
到此为止,我们明白该问题影响了多个数据库集群,更多来自GitHub数据库工程团队工程师加入了战斗。他们开始调查当前的状态,以确定需要采取哪些操作来手动为美国东海岸数据库的每个集群配置主服务器并重建复制拓扑。这项工作充满了挑战,因为到目前为止,西海岸数据库集群已经接收了应用层近40分钟的数据写入。另外,东海岸集群中还存在几秒钟的数据写入,而这些数据没有复制到西海岸,导致西海岸的数据无法复制到东海岸。
保护用户数据的机密性和完整性是GitHub的首要任务。为了保护这些数据,我们作出判断,写到美国西海岸数据中心的30多分钟的数据导致我们只能将错就错,才能保护用户数据的安全。但是,在东海岸运行的应用程序依赖于向西海岸的MySQL集群写入信息,这些应用程序无法应对跨国数据库调用带来的额外延迟。因此,这一决定必然会导致许多用户无法使用我们的服务。我们认为,必须进行大范围的服务降级,才能保证用户数据的一致性。
2018年10月21日23:19 UTC
通过查询数据库集群的状态,我们认为我们必须停止运行一切写入与推送等相关元数据的作业。我们选择了暂停webhook交付和GitHub Pages构建进行部分降级,以免危害我们已经从用户收到的数据。换句话说,我们的策略是优先考虑站点可用性和恢复时间的数据完整性。
2018年10月22日00:05 UTC
参与事件响应团队的工程师开始制定解决数据不一致的计划,并实施MySQL的故障恢复程序。我们计划从备份还原,同步两个数据中心中的副本,退回到稳定的服务拓扑,再继续处理排队的作业。我们更新了状态,以通知用户我们将执行内部数据存储系统的受控故障恢复。
虽然MySQL数据备份每四个小时发生一次并保留多年,但备份远程存储在公共云blob存储服务中。恢复TB级别的备份数据所需的时间导致该过程长达数小时,其中大部分时间用在了远程备份服务的数据传输上。而解压大型备份文件、校验和准备并加载到新配置的MySQL服务器上的过程也需要花费大量时间。该过程至少每天都会进行一次测试,因此我们很了解恢复所需的时间,但在这次事故之前我们从来没遇到过真正需要从备份重建整个集群的情况,一般都是依赖于其他策略,如延迟复制等。
2018年10月22日 00:41 UTC
此时我们已经开始了对所有受影响的MySQL的备份过程,一些工程师在监视该过程的进度。同时还有多个团队在调查怎样才能加快数据传输和恢复的速度,避免造成更多的服务降级,也避免数据损坏的风险。
2018年10月22日06:51 UTC
东海岸数据中心的几个集群完成了备份恢复,开始从西海岸复制数据。这导致一些必须执行跨国写操作的页面的加载速度变慢,但对于那些需要从这些集群读取信息的页面来说,如果读取请求落在了刚刚恢复的集群上,就能获得正常的速度。其他大型数据库集群依然在恢复中。
我们的团队已经找到了从西海岸直接恢复数据的方法,来避免从离线存储下载造成的带宽限制,同时我们也看到了备份恢复即将完成的曙光,而建立健康的复制拓扑的时间只取决于追赶西海岸集群所需的时间了。这个时间与复制的遥测数据呈线性关系,于是我们更新了状态页面,表明大约两个小时之后恢复即可完成。
2018年10月22日 07:46 UTC
GitHub发布的一篇博文(https://blog.github.com/2018-10-21-october21-incident-report)提供了更多信息。我们内部使用GitHub Pages,而几个小时之前所有网站的构建都被暂停了,所以我们大费周折才成功地发布这篇文章。我们为延迟道歉,本该更早地发布这篇文章,而且以后我们会保证能够在各种限制下依然能及时地更新我们的进度。
2018年10月22日11:12 UTC
东海岸所有的主数据库都建立起来了。这样整个网站就可以响应更多的请求了,因为所有写请求都会被导至与应用层处于同一物理数据中心的数据库服务器上。虽然现在性能大幅度提高了,但依然有多个数据库读副本落后于主服务器几个小时。这些延迟的复制会导致用户看到不一致的信息。我们将读请求分散到许多读副本上,所以每个请求都有可能看到延迟几个小时的副本。
实际上,追赶复制需要的时间不是线性的,而是呈指数形式下降。随着欧洲和美国的用户开始工作后,写请求会大幅度增加,因此恢复过程比预计的长了很多。
2018年10月22日13:15 UTC
现在,我们达到了GitHub.com网站的负载峰值。事故响应团队讨论了处理方案。显然,复制的延迟并没有减少,而是在增加,我们开始在美国东海岸的公有云上部署更多的MySQL读副本。这些副本上线后,就可以很容易地将读请求分散到更多服务器上,从而整体减少读副本的负载,加速复制的进度。
2018年10月22日16:24 UTC
副本同步之后,我们进行了故障恢复,以恢复到以前的拓扑结构,并解决延迟和可用性的问题。我们有意识地在一段时间内提高了数据完整性的优先级,所以在处理积攒下的数据时,服务的状态依然为红色。
2018年10月22日16:45 UTC
在恢复阶段,我们必须平衡积攒下的数据带来的越来越多的负载,以及可能会给生态系统的合作伙伴们带来的大量通知,并尽快让服务恢复到100%的水平。现在队列里已经积攒了500万个webhook事件和八万多个GitHub Pages构建请求。
重新开始处理数据后,我们处理了大约20万个超过生命期的webhook负载,结果这些都被抛弃了。发现了这一点后,我们暂停了处理,并且做了更改,暂时增加了webhook的生命期。
为了避免事件恶化,我们依然保持服务降级的状态,直到处理完所有积攒的数据,并确保服务完全恢复到正常的性能水平。
2018年10月22日23:03 UTC
所有等待的webhook和Pages构建都处理完毕,所有系统的完整和正常工作也得到了确认。网站状态更新为绿色。
3.下一步
解决数据不一致
在恢复过程中,我们发现受影响集群的MySQL的二进制日志包含了一些主站点的写入,这些写入没有被同步到西海岸。没有被复制到西海岸的写入请求数量相对较小。例如,最繁忙的集群之一有954个写操作受到了影响。
我们现在正在分析这些日志,判断哪些写操作能够被自动同步,哪些需要额外处理。多个团队参与了这项工作,我们已经分析出了一部分写操作已经被用户重复过,从而正确保存了。
这次分析的主要目标是保证用户保存到GitHub上数据的完整性和准确性。
沟通
为了能在事故过程中给用户提供一些有用的信息,我们根据积攒数据的处理比率做了一些时间的估算并公开。现在回顾起来,我们的估算并没有考虑到所有因素。我们为造成的困扰致以歉意,并争取在以后能够提供更准确的信息。
技术任务
分析过程中发现了多个技术任务。我们内部依然在进行更广泛的事故分析,希望能找出更多需要改进的地方。
调整Orchestrator的配置,避免跨区域边界提升主数据库。Orchestrator的行为完全符合其配置,但我们的应用层无法支持这种拓扑逻辑的改变。在同一区域内的领导者选举通常是安全的,但突然的跨区域延迟才是这次事故的主要原因。我们以前只从内部网络分区的层次上考虑过这个问题,因此这种行为就成了紧急事故。
加快我们状态报告系统的迁移进度,提供一个更丰富的论坛,让我们能用更清晰、更明确的语言来沟通事故的状况。尽管GitHub的许多部分在事故过程中依然可用,但我们只能把状态设置为绿、黄或红。我们认识到,这无法给用户提供准确的信息,无法告诉他们什么功能可以使用、什么功能不能使用,所以以后我们会对平台的各个部分分别显示,让用户知道每个服务的状态。
在事故发生前一周,我们启动了一项全公司范围的工程任务,以支持多个数据中心的active/active/active架构。该项目的目标是支持设施级别的N+1的冗余度,从而在不影响用户的前提下,容忍单一数据中心的完全故障。这项工作需要巨大的努力,而且需要大量时间,但我们相信,多个地理位置上连接良好的数据中心是物有所值的。而这次事故则提高了这项工作的紧迫性。
我们将在测试假设方面采取更积极的态度。GitHub是个迅速增长的公司,在过去时间内积攒了非常多的复杂性。随着我们不断增长,将各种历史遗留问题和历史决策转移给新员工的难度越来越大。
组织层面的任务
这次事故改变了我们对于网站可靠性的认知。
我们认识到,对于我们这样复杂的服务,仅凭更紧密的运营控制或更快的响应时间是不够的。为支持这些工作,我们还会从体制上启动一项故障验证工作,保证故障在发生之前就能得到验证。这项工作会导致GitHub未来在错误注入和混乱工程工具方面的投入增加。
结论
我们清楚你们的项目和业务成功十分依赖于GitHub。没有人比我们对于服务的可用性和数据的正确性更有热情。我们会继续分析这次事故,以便今后为你们提供更好的服务,不辜负你们对我们的信任。
原文:https://blog.github.com/2018-10-30-oct21-post-incident-analysis/
作者:Jason Warner,GitHub技术总监。
译者:弯月,责编:郭芮