本文是公开报告的摘要与解读,事实以原始报告为准。
2018 年 10 月 21 日 22:52 UTC,GitHub 在更换故障的 100G 光传输设备时,切断了美国东海岸网络枢纽与东海岸主数据中心之间的连接。连接在 43 秒后恢复,但这短暂的网络分区触发了更大的故障。
运行在东海岸主数据中心的 Orchestrator 在 Raft 共识中失去 leader 身份;西海岸和公有云中的 Orchestrator 节点组成 quorum,把西海岸的 MySQL 主库提升为新的主库,应用写入随之转向西海岸。当连接恢复时,东海岸数据库保留了少量尚未复制到西海岸的写入,两边出现分叉,GitHub 无法安全地把主库切回东海岸。
GitHub 选择保留西海岸的写入,也就是“向前恢复”,而不是回滚并丢弃这部分数据。事故从 2018 年 10 月 21 日 22:52 UTC 持续到 10 月 22 日 23:03 UTC,报告给出的时长是 24 小时 11 分钟。
| 时间(UTC) | 事件 |
|---|---|
| 10 月 21 日 22:52 | 更换 100G 光设备时切断东海岸网络枢纽与主数据中心连接;43 秒后恢复,触发后续故障 |
| 22:54 | 监控产生故障告警,值班人员开始排查 |
| 23:02 | 发现多个 MySQL 集群处于非预期拓扑;Orchestrator API 只显示西海岸数据库服务器 |
| 23:07 | 内部部署工具已被手动锁定,停止进一步变更 |
| 23:09 | 站点状态设为黄色,自动升级为事故并通知事故协调人 |
| 23:11 | 事故协调人加入 |
| 23:13 | 状态改为红色,数据库工程师被呼叫;西海岸集群已承接应用写入近 40 分钟 |
| 23:19 | 暂停 webhook 投递和 Pages 构建,主动降低功能以保护数据 |
| 10 月 22 日 00:05 | 确定恢复计划:恢复备份、同步两侧副本、回到稳定拓扑、处理积压 |
| 00:41 | 开始恢复所有受影响的 MySQL 集群备份 |
| 06:51 | 部分东海岸集群已恢复并从西海岸复制;较大集群仍在恢复,团队找到更快的“直接从西海岸恢复”路径,估计还需两小时 |
| 07:46 | 发布事故博客;由于 Pages 构建暂停,发布过程变慢 |
| 11:12 | 所有数据库主库回到东海岸,响应改善,但数十个只读副本仍落后数小时,读取可能不一致 |
| 13:15 | 接近流量高峰,复制延迟上升;在东海岸公有云增加 MySQL 只读副本并分散读流量 |
| 16:24 | 副本同步完成,切回原拓扑;状态仍为红色,继续处理积压 |
| 16:45 | 积压包含超过 500 万个 hook 事件和 8 万个 Pages 构建;约 20 万个 webhook 负载超过内部 TTL 被丢弃,暂停处理并提高 TTL |
| 23:03 | 所有待处理 webhook 和 Pages 构建处理完毕,确认系统完整性,状态恢复绿色 |
事故持续 24 小时 11 分钟。用户看到的是过期或不一致的信息;大部分时间里,GitHub 无法投递 webhook 事件,也无法构建和发布 GitHub Pages。报告没有给出错误率百分比。
数据方面,报告明确说“最终没有用户数据丢失”,但写作时仍在人工核对“几秒钟的数据库写入”。受影响最繁忙的一个集群有 954 条写入需要核对;部分写入已被用户重试成功。恢复后,GitHub 通过 MySQL binlog 捕获未复制到西海岸的东海岸写入,能自动对账的自动处理,必要时联系用户。
积压方面,报告给出超过 500 万个 hook 事件和 8 万个 Pages 构建;约 20 万个 webhook 负载因超过内部 TTL 被丢弃。GitHub 暂停处理并提高 TTL,避免继续丢弃。
触发点是 43 秒的网络分区。GitHub 当时运行多个 MySQL 集群,Orchestrator 负责拓扑管理和故障切换。东海岸的 Orchestrator 失去 Raft leader 后,西海岸与公有云节点形成 quorum,并按配置把西海岸主库提升为新的写入点。
Orchestrator 并没有违背配置。报告的原话是:Orchestrator 的行为符合配置,但应用层无法支持这种拓扑变化。东海岸应用跨全国写入西海岸数据库带来的延迟,超过应用层能承受的范围,许多用户因此无法正常使用服务。
当网络恢复时,东海岸数据库还有少量写入没有复制到西海岸,西海岸则已承接近 40 分钟的应用写入。两边都有对方没有的数据,直接切回东海岸会丢失西海岸写入,因此 GitHub 选择保留西海岸数据,先恢复服务,再对账。
GitHub 的处置顺序是:先停止会放大问题的写入型工作,暂停 webhook 投递和 Pages 构建;再恢复备份、同步副本、回到稳定拓扑;最后处理积压并对账分叉写入。
备份每 4 小时一次,存放在远程公有云 blob 存储中。恢复多个 TB 的数据需要传输、解压、校验、准备和加载,耗时数小时。报告说这套流程经常测试,但 GitHub 此前从未从备份重建过整个集群。恢复过程中,团队发现直接从西海岸恢复部分集群更快,绕过离站存储的吞吐限制。
10 月 22 日 11:12,所有主库回到东海岸,但只读副本仍落后;13:15 增加东海岸公有云只读副本,分散读流量并降低延迟;16:24 副本同步完成,切回原拓扑;23:03 积压清空、完整性确认,事故结束。
第一,故障切换必须理解应用层的延迟边界。Orchestrator 按 Raft 规则选主没有错,但它把一个应用无法承受的跨区域拓扑变成了现实。自动化的“正确”不等于业务上的“可用”。
第二,恢复时间估算不能线性外推。报告指出复制追赶呈“幂衰减”而不是线性轨迹,欧洲和美国的早高峰流量又延长了恢复时间。演练和估算都应考虑负载变化。
第三,状态页要按组件细分。简单的绿黄红无法表达“部分功能可用、部分数据可能不一致”,会放大沟通成本。
第四,数据优先于可用性。GitHub 选择保留西海岸写入、暂停 webhook 和 Pages,而不是丢弃数据换快速恢复;这个取舍需要在事故前就明确。
第五,复杂系统需要故障注入和混沌工程。报告把“更积极地测试假设”和“投资故障注入、混沌工程工具”列为改进方向,并推动多数据中心 active/active/active 架构与 N+1 设施冗余。
合上页面,先回答,再展开对照。
小程序版
微信扫码,随时查命令