OpenAI 最近发布了一篇关于 12 月 11 日发生的事件的公开报告,其中包含大量有价值的细节!以下是一些我的即兴观察:
**系统饱和**
当数千个节点同时执行这些操作时,Kubernetes API 服务器不堪重负,导致大多数大型集群中的 Kubernetes 控制平面宕机。
术语“饱和”描述了系统达到其处理能力极限的状态。这有时被称为过载或资源耗尽。在 OpenAI 事件中,Kubernetes API 服务器因接收过多的流量而饱和。一旦发生这种情况,API 服务器便无法正常工作。结果,基于 DNS 的服务发现机制最终失败。
饱和是事件中极其常见的故障模式,而 OpenAI 在这里为我们提供了另一个例子。您还可以阅读一些先前关于涉及饱和的公共事件报告的文章:Cloudflare、Rogers 和 Slack。
**所有测试都通过**
更改已在暂存集群中进行了测试,未观察到任何问题。影响特定于超过一定大小的集群,并且我们每个节点上的 DNS 缓存延迟了足够长的时间以供故障可见,从而使推出得以继续。
难以防止与饱和相关的事件的原因之一是,所有软件在功能上都可以是正确的,因为它们通过了所有功能测试,并且故障模式仅在系统暴露于生产环境中才会出现的条件下才会出现。即使使用生产流量进行金丝雀发布也不能防止仅在满负荷下才会出现的问题。
我们部署前的主要可靠性问题是新遥测服务的资源消耗。在部署之前,我们评估了所有集群(CPU/内存)中的资源利用率指标,以确保部署不会中断正在运行的服务。虽然资源请求是在每个集群的基础上进行调整的,但没有采取预防措施来评估 Kubernetes API 服务器的负载。此推出过程监视服务运行状况,但缺乏足够的集群运行状况监视协议。
值得注意的是,工程师确实验证了已部署新遥测配置的集群上的资源利用率的变化。问题在于交互:它增加了 API 服务器上的负载,这使我们进入下一要点。
**复杂且意想不到的交互**
这是多个系统和进程同时发生故障并以意想不到的方式交互的结果。
当我们查看系统故障时,我们通常会寻找各个组件中的问题。但在复杂的系统中,识别复杂且意想不到的交互可以更好地了解故障是如何发生的。您不仅需要查看框,还需要查看箭头。
简而言之,根本原因是新的遥测服务配置意外地产生了巨大的 Kubernetes API 负载,横跨大型集群,压垮了控制平面并破坏了基于 DNS 的服务发现。
“所以,我们推出了新的遥测服务,然后,等等,我们的服务无法再相互调用了。”
在这种情况下,令人惊讶的交互是 kubernetes API 故障和由此导致的 kubernetes 上运行的服务故障之间。通常,如果您在 kubernetes 上运行的服务并且您的 kubernetes API 变为不健康,则您的服务仍应正常运行,您只是无法对当前部署进行更改(例如,部署新代码,更改 pod 数)。但是,在这种情况下,kubernetes API(控制平面)中的故障最终导致正在运行的服务(数据平面)的行为发生故障。
两者之间的耦合?是 DNS。
**DNS**
简而言之,根本原因是新的遥测服务配置意外地产生了巨大的 Kubernetes API 负载,横跨大型集群,压垮了控制平面并破坏了基于 DNS 的服务发现。
**更改的影响分散在一段时间内**
DNS 缓存在进行更改和服务开始发生故障之间添加了延迟。
使与 DNS 相关的事件难以处理的事情之一是 DNS 缓存的性质。
当更改的影响分散在一段时间内时,这可能会使诊断导致故障的更改变得更加困难。当停止工作的关键服务(在本例中为服务发现)不是发生更改的服务(遥测服务部署)时,尤其如此。
DNS 缓存使问题在推出开始扩展到整个集群之前变得不那么明显。
在这种情况下,影响分散在一段时间内是因为 DNS 缓存的性质。但通常我们会有意将更改分散在一段时间内,因为如果我们正在推出的更改最终成为导致故障的更改,我们希望减少爆炸半径。如果我们在推出过程中检测到问题,这将非常有效。但是,这也可能使检测问题变得更加困难,因为错误信号较小(按设计!)。而且,如果我们只在推出完成后检测到问题,则可能更难以将更改与影响相关联,因为更改在一段时间内被分散了。
**故障模式使补救更加困难**
为了进行该修复,我们需要访问 Kubernetes 控制平面,但由于 Kubernetes API 服务器的负载增加,我们无法做到这一点。
有时,破坏生产依赖的系统的故障模式也会破坏运营人员依赖于其工作的系统。我认为 James Mickens 在他写道时说得最好:
我没有任何工具,因为我已经用我的工具摧毁了我的工具
Facebook 在 2021 年遇到重大中断时也遇到了类似的问题:
而且,当我们的工程师努力弄清楚发生了什么以及为什么发生时,他们面临着两个巨大的障碍:首先,无法通过我们的正常方式访问我们的数据中心,因为他们的网络已关闭,其次,DNS 的完全丢失破坏了许多我们通常用来调查和解决此类中断的内部工具。
此类问题通常需要运营人员当场想出一个解决方案。OpenAI 工程师寻求多种策略使系统恢复健康。
我们在几分钟内确定了问题,并立即启动了多个工作流来探索各种方法,以便快速使集群恢复联机:
缩减集群规模:减少 Kubernetes API 的总负载。
阻止对 Kubernetes 管理 API 的网络访问:防止新的昂贵请求,让 API 服务器有时间恢复。
扩展 Kubernetes API 服务器:增加可用资源以处理挂起的请求,使我们能够应用修复程序。
通过并行执行这三项工作,我们最终恢复了足够的控制权以删除有问题的服务。
他们的干预取得了成功,但很容易想象其中一项干预意外地使情况变得更糟的场景。正如 Richard Cook 指出的那样:所有从业者的行动都是赌博。事件总是伴随着当下的不确定性,当我们凭借事件如何展开的完美知识回顾时,很容易忽略这一点。
**旨在提高可靠性的更改**
作为一项推动整个组织提高可靠性的工作的一部分,我们一直在努力改进我们的集群范围内的可观察性工具,以增强对系统状态的可见性。太平洋标准时间下午 3:12,我们部署了一项新的遥测服务来收集详细的 Kubernetes 控制平面指标。
这是一个子系统意外行为的绝佳示例,该子系统的首要目的是提高可靠性。这是我对可靠系统为何会发生故障的猜想的数据点。