2020 年 12 月 Google 宕机事件复盘

你会在意云服务的可用性到底是 99.99% 还是 99.9999% 吗?要知道,互联网世界中再健壮的基础设施也有不可靠的时候

2020 年 12 月 14 日凌晨 4 点,来自美洲地区的用户发现,所有需要使用 Google 账号登录的站点都发生问题:访问 Gmail、YouTube 等服务时均提示错误信息。一些依赖 Google 账号体系的第三方服务(如 Figma 等)也遇到错误。此时正值欧洲时间正午、东亚晚间时段,许多使用 Google 服务的用户和企业均受到影响。

由两张描述 Google 服务返回错误代码的插图组成:左图为包含 Google 徽标,标识错误代码为 500 的网页截图;右图包含 YouTube 徽标和一只紫色的拿着锤子的猴子,上方表示为“Something went wrong”
Google 于 2020 年 12 月 14 日宕机时,用户访问 Gmail 以及 YouTube 服务失败

距离此次宕机事件近一年后,我们重新对本次事件进行复盘与总结,希望能为读者带来架构设计方面的体验与思考。有关本次事件的技术性细节参考了 Google 云状态面板中的相关事故报告[由 Wayback Machine 存档]

用户身份服务

根据 Google 的事故报告,本次事件是由于用户身份服务(User ID Service)出错引起的。我们首先对该服务进行简要介绍。

众所周知,Google 公司提供的互联网服务广阔且复杂,大多数服务均允许用户通过 Google 账号登录。 而支撑 Google 账号认证体系的,则是部署于 Google Cloud 上的,由 Google 开发的包含用户身份服务在内的多套服务。大多数公司并不会公布这些服务的技术细节,但我们可以从 Google Cloud 在 2017 年 1 月发布的《Google 基础架构安全设计概览》一文中了解 Google 所使用的安全架构:

基础架构提供了一项中央用户身份识别服务,该服务可以签发“最终用户权限工单”。中央身份识别服务会对最终用户的登录信息进行验证,然后向该用户的客户端设备签发用户凭据,例如 Cookie 或 OAuth 令牌。从该客户端设备向 Google 发出的任何后续请求都需要提交此用户凭据……此服务通常作为 Google 登录页面显示给最终用户。……

——《Google 基础架构安全设计概览》2017 年 1 月中文版本

用户身份服务为每个账户维护了一个独立的 ID,并根据 OAuth 令牌或者 Cookie 进行凭证认证。这项服务将用户账户数据存储在一个分布式数据库中,使用 Paxos 算法来协调数据一致性。出于安全性的考虑,该服务在检测到过期数据时,会拒绝相关请求。作为顶级云服务商之一,Google 内部的许多服务同其他客户(的服务)一样,也是以租户身份运行在 Google Cloud 上的。这些服务运行时同样会收到来自基础设施环境的标准资源配额限制。本次事件的引发原因即与配额限制存在关系。

埋下事故的种子

为了管理向这些内部服务分配的各项资源配额,Google 开发了一套内部服务,能够根据这些服务的实际资源使用量动态地向基础设施申请或释放相应资源配额。2020 年 10 月,在对用户身份服务接入到新的配额管理系统的过程中,Google 员工在将用户身份服务注册到新系统之后,并没有将该系统从旧版配额系统中移除。这导致配额管理系统持续地报告用户身份服务使用的存储空间容量为“零”。

配额管理系统从来不会因为单个服务报告自己所使用的空间比原来少,就轻易缩减为这个服务所分配的存储空间容量。相反,配额管理系统通过设计有诸多机制,既能够发现资源配额管理中的异常情况,又能根据服务实际占用的资源情况自动地对该服务的未来资源进行动态调整。对于用户身份服务报告自己占用“零空间”的问题,Google 配额管理系统本应将其作为一种异常情况报警,但当时系统的设计并没有考虑到某个单个服务报告空间使用量为“零值”的这种临界情况——这次的情况完美地绕过了 Google 内部系统设计的所有报警策略:

  • 系统会因为对大量用户进行配额修改而报警,但本次的修改只涉及到单一用户组
  • 系统会因为要将存储空间分配地比实际使用量低而报警,但这次用户管理服务的存储使用量被错误地上报为了“零”
  • 系统应当对于大幅修改服务的存储空间配额而报警,但本次事件中配额系统没有发出任何警告
  • 系统应当对存储空间分配下限进行报警,然而本次报告的使用量以及分配的存储空间超出了保护限制

由于新的配额限制不是立刻被应用的,所以当时将系统接入新版本的配额系统后并没有能够立刻发现问题。这就像在系统中埋设了一颗定时炸弹:当下次配额自动调整存储空间时,用户身份服务将发现它没有任何存储空间可用。

不过,即使配额系统将自动减少用户身份服务的配额,也还是有机会能够发现其中的问题:在执行配额限制的宽限期期间,来自 Google 的运维人员本来也应该能通过查看配额计划调整日志,注意到来自用户身份服务的存储空间异常分配情况。我们不清楚 Google 是如何运营管理其内部基础设施服务的,但这个问题完美地绕过了 Google 内部所有环节的审查,直到下一次配额系统自动减少了用户身份服务的容量……

事故与反思

最终,存储空间问题于 2020 年 12 月 14 日发生。当天太平洋时间凌晨 03:43,来自 Google 的报警服务在配额管理系统自动将用户身份服务分配的存储容量降至极低的水平后即发出容量报警。03:46,由于用户身份服务无法在没有额外存储空间的情况下记录新增会话,该服务开始遇到错误。04:08 ,来自 Google 的工程师定位到问题并给出了潜在的解决方案。04:22,工程师在一个数据中心临时关闭了存储容量的相关限制,这个操作很快解决了问题。04:27,同样的缓解方法被应用到了所有数据中心。04:33,所有错误率降低到了正常水平,一些下游服务在稍后得到恢复。整体事故共持续约 50 分钟,使 Google 大多数服务的年可用性降低了 0.01%。

相比上个世纪的软件工程领域,得益于互联网和全球化,现代软件工程面向的用户比之前多得多。这对于软件的开发、测试和部署都提出了更高的要求。架构师和软件工程师等始终致力于设计高可靠性架构并交付高质量软件。这些年来,我们提倡“上云“以提高可伸缩性,”解耦“以缓解单点故障时对整体系统的影响。但是架构设计过程中一定会出现一些被集中依赖的服务,比如这次 Google 的中央用户身份服务。一旦这些服务遇到问题,不稳定性甚至会传播到整个系统。对此,Google 的工程师在事故报告中提出的建议和措施如下:

  • 审查配额管理的自动化程序,以防止全局变化的快速实施
  • 改进监控和警报,以尽早发现不正确的配置
  • 在影响内部工具的故障期间,提高发布外部通信的工具和程序的可靠性
  • 评估并实施改进的用户 ID 服务数据库的写入故障恢复能力
  • 提高 GCP 服务的弹性,以更严格地限制用户 ID 服务故障期间对数据平面的影响

我个人非常喜欢看《空中浩劫》(Air Crash Investigation)系列节目。不仅是因为每一期编排紧凑、引人入胜的节目都讲述了一起惊心动魄的飞行器事故,更是因为我们能看到来自各个机构的空难调查人员是如何渐进式地从有限的证据中不断深挖,最终发现每一起事故的背后原因的。软件行业中的事故通常不是致命的,当事故发生时,每一个负责任的人所想的不应该是对造成事故的关联人员进行处罚,而应该是如何从流程和机制中进行改善,从事故中得到学习,设计更有效和更可靠的软件体系,来尽可能减小人为事故的影响范围。

多说几句

在完成本篇博文时,我在互联网上查询了诸多相关内容。其中 Twitter 平台上事故发生时网友的讨论很有意思:这些讨论代表着不同网友的负责任和不负责任的观点,一些合理推断和阴谋论式的恶意揣测。Twitter 这种社交平台每天承载了大量的信息流,我们在事故发生约一年之后再回头去看当时的各路言论和推断,就不难发现哪些社区成员是真有水平,而哪些人只是调侃,还有些人完全是不懂技术、随便胡诌了。