阿里妹导读
文章探讨了为什么大规模集群中的可观测性服务会产生大量API请求、API服务器为何对DNS解析至关重要以及故障恢复过程为何缓慢的原因。
背景
12月11日,OpenAI 出现了全球范围内的不可用故障,影响了 ChatGPT,API,Sora,Playground 和 Labs 等服务。影响范围从 12 月 11 日下午 3:16 至晚上 7:38 期间,持续时间超过四个小时,产生显著影响。
从官方复盘[1]中摘取根因分析如下:
Root Cause
OpenAI operates hundreds of Kubernetes clusters globally. Kubernetes has a control plane responsible for cluster administration and a data plane from where we actually serve workloads like model inference.
As part of a push to improve reliability across the organization, we’ve been working to improve our cluster-wide observability tooling to strengthen visibility into the state of our systems. At 3:12 PM PST, we deployed a new telemetry service to collect detailed Kubernetes control plane metrics.
Telemetry services have a very wide footprint, so this new service’s configuration unintentionally caused every node in each cluster to execute resource-intensive Kubernetes API operations whose cost scaled with the size of the cluster. With thousands of nodes performing these operations simultaneously, the Kubernetes API servers became overwhelmed, taking down the Kubernetes control plane in most of our large clusters. This issue was most pronounced in our largest clusters, so our testing didn’t catch it – and DNS caching made the issue far less visible until the rollouts had begun fleet-wide.
The Kubernetes data plane can operate largely independently of the control plane, but DNS relies on the control plane – services don’t know how to contact one another without the Kubernetes control plane.
In short, the root cause was a new telemetry service configuration that unexpectedly generated massive Kubernetes API load across large clusters, overwhelming the control plane and breaking DNS-based service discovery.
这里我们需要理清楚如下几个问题:
为什么可观测服务会产生如此大量的 API Server 请求? Api Server 为什么会影响 DNS 解析? 故障恢复为什么这么慢?
K8s 场景下
为什么可观测服务需要访问 API Server
K8s 元信息数据感知与可观测数据富化
在之前的文章中《跟着iLogtail学习容器运行时与K8s下日志采集方案》 ,我们有分析过在 Kubernetes(K8s)环境下,容器日志数据采集面临着多重挑战:
1. 生命周期短容器的短暂性和高度动态特性使得它们的生命周期极其短暂,这意味着许多容器可能在运行期间生成重要的日志信息,但一旦容器被销毁,这些信息就会随之消失。日志的瞬时性给日志采集带来了极大的挑战。
2. 分布式架构容器通常部署在多个节点上,这种分布式架构导致日志数据存储在不同的位置。数据的分散性不仅增加了管理的复杂性,而且可能导致实时数据采集和分析的困难。
3. 存储方式多样容器的存储方式各异,包括卷、挂载等。这些存储方式的多样性使得日志的写入、读取和管理变得更加复杂。
4. 数据与容器元信息关联容器日志作为可观测数据,必须与容器的元信息(如容器 ID、名称、标签等)进行精准关联,以便于后续的查询与分析,有效地匹配数据与容器信息有助于快速定位问题源头。然而,由于容器的频繁创建和销毁,以及其动态变化的特性,在数据采集时及时、准确地建立这种关联,依然是一大挑战。
其中,Pod 信息感知、可观测数据与元信息关联,都可以通过对 API Server 请求来获取。
可观测数据采集服务发现需要跟 API Server 交互
对于 Prometheus 指标数据采集,比如定义 PodMonitor/ServiceMonitor 之后,Prometheus Operator 实际上就会去 API Server 请求对应的 Pod 信息,从而发现需要采集指标数据的 Pod/Service。
OpenAI 的可观测组件的做法
vs LoongCollector/iLogtail 的做法
OpenAI 的做法
从故障分析报告中,Telemetry services have a very wide footprint, so this new service’s configuration unintentionally caused every node in each cluster to execute resource-intensive Kubernetes API operations whose cost scaled with the size of the cluster. 可以看到 OpenAI 的可观测服务,从每个节点上,都向 API Server 去请求资源数据。
由此,我们可以猜测,OpenAI 的可观测服务是类似 Daemonset 的部署模式,每个节点的 Pod,都会向 API Server List-Watch 大量资源。比如使用Daemonset 的方式部署 Prometheus Agent ,这样做会对API Server 产生大量的压力:
当 Prometheus 运行在集群中的每个节点上时,watch 的压力 * 节点数量增加
在 O(100) 或 O(1000) 节点的大型集群上,这会给 K8s API Server 带来相当大的压力,压力体现在如下几方面:
每个 watch 产生 2 个 go 协程,具体可以参考源码[2] 需要处理给定种类的对象的所有更改 需要序列化并向客户端发送给定种类对象的更新
所以 OpenAI 为什么测试的时候没有发现问题,故障分析报告中也有提到:
The change was tested in a staging cluster, where no issues were observed. The impact was specific to clusters exceeding a certain size, and our DNS cache on each node delayed visible failures long enough for the rollout to continue. 本质上就是测试集群的规模、节点数不够多,所以没有触发问题。
Our main reliability concern prior to deployment was resource consumption of the new telemetry service. Before deployment, we evaluated resource utilization metrics in all clusters (CPU/memory) to ensure that the deployment wouldn’t disrupt running services. While resource requests were tuned on a per cluster basis, no precautions were taken to assess Kubernetes API server load. This rollout process monitored service health but lacked sufficient cluster health monitoring protocols. 其实就是没有意识到这样架构下,会对 API Server 产生大压力,测试的时候,甚至根本没有观测过 API Server 的情况。
LoongCollector/iLogtail 的做法
LoongCollector 秉承 All-in-One 的设计理念,致力于所有的采集工作用一个 Agent 实现 Logs、Metric、Traces、Events、Profiles 的采集、处理、路由、发送等功能。LoongCollector 着重强化了 Prometheus 指标抓取能力,深度融入 eBPF(Extended Berkeley Packet Filter)技术以实现无侵入式采集,提供原生的指标采集功能,做到真正的 OneAgent。在 K8s 场景下,同样面临 K8s 资源监控与元信息获取的需求,为了减少对 API Server 的影响,我们采用了如下的优化方案:
节点上与容器运行时交互,获取基础的容器元信息,用来满足 Daemonset 部署场景的容器发现和可观测数据与元信息关联;
集群内,单副本 List-Wactch,为 LoongCollector 指标采集提供统一的 Target Allocator 能力,避免对 API Server 产生大量请求;
Api Server 为什么会影响
DNS 解析 && 故障恢复为什么这么慢
从 K8s 官方文档,我们很容易找到这样的架构图:
可以看到 API Server 是 Kubernetes 集群中的最核心的组件, 在 Kubernetes 集群中扮演着至关重要的角色,不仅提供了强大的 API 服务,还负责数据存储、版本管理、访问控制等多个方面的功能,确保了集群的高效运行和安全性:
API 服务端:作为 Kubernetes API 的服务端,API Server 为集群内的节点以及 kubectl 工具提供 API 服务。这意味着用户可以通过 API Server 与 Kubernetes 集群进行交互,执行如创建、更新、删除资源等操作。 集群组件代理:API Server 还充当集群组件的代理,例如 Kubernetes UI,使得这些组件能够通过 API Server 与集群进行通信。 数据存储交互:API Server 是唯一能够与存储系统 etcd 交互通信的组件。它负责将客户端请求的数据进行编码、解码,并存储到 etcd 中。同时,API Server 支持不同版本之间的数据转换,确保数据的一致性和兼容性。 访问控制:API Server 负责身份鉴权和访问控制,确保只有经过授权的用户才能访问特定的资源。此外,API Server 还可以设置访问逻辑(Admission Control),进一步增强安全性。 通信机制:API Server 与 Controller Manager、Scheduler 以及 kubelet 之间通过 List-Watch 方式进行通信。Controller Manager 通过 etcd 获取需要创建资源的副本数,交由 Scheduler 进行策略分析,最后由 kubelet 负责 Pod 的创建和容器加载。
Api Server 为什么会影响 DNS 解析
当客户端 Pod 需要进行域名解析时,首先会向 KubernetesDNS/CoreDNS 发出查询请求,KubernetesDNS/CoreDNS基于其内存中维护的 DNS 记录,迅速返回相应服务的 IP 地址。这一过程不仅提高了域名解析的效率,也增强了系统的可伸缩性和可靠性。
当然为了避免CoreDNS 压力过大,Kubernetes 也提供高了 Local DNS Cache 的方式,在节点上,对于 DNS 记录进行缓存。但是当 API Server 几乎挂掉的情况下,KubernetesDNS/CoreDNS 里的 DNS 记录没有办法得到更新,因此 Kubernetes 服务发现基本处于不可用的状态。
故障恢复为什么这么慢
其实定位到可观测服务的问题之后,最简单的方式就是直接kubectl delete把可观测服务删除掉,但是很可惜,API Server 其实就是kubectl命令的入口,API Server 在几乎故障的情况下,是没有办法响应管理命令的。
为了保证 API Server 可用,OpenAI 团队在做故障恢复的时候做了如下措施:
缩小集群规模:降低总 Kubernetes API 负载。 阻止对 Kubernetes 管理员 API 的网络访问:阻止新的高资源成本请求,让 API 服务器有时间恢复正常。 扩展 Kubernetes API 服务器:增加可用资源以消化待处理请求,借此为修复操作留出空间。
由此,把有问题的可观测服务删掉之后,集群恢复了正常的状态。
给 iLogtail/LoongCollector 关于稳定性方面的启示
OpenAI 的这次故障,有着多方面的原因,比如服务依赖没有梳理清楚,可观测服务架构设计上不合理,准入测试没有起到作用等等。这些原因对我们自己的服务有着同样的警示作用,这不得不让我们再反思一下我们在稳定性方面还有哪些需要注意的地方。
深刻理解我们的服务对基础环境的影响
在设计和实施可观测方案时,最重要的一点是确保其对基础环境系统的影响最小化。我们需要避免过多的负载和资源消耗,以免影响系统的正常运行。任何可观测数据的采集都应经过精心设计,确保不会对业务流程造成干扰或延迟。
在主场景中,需要特别关注CPU、内存、磁盘、网络、句柄等资源的占用情况,确保我们不会占用预期之外的资源。在 Kubernetes 环境中,我们的服务对核心组件如CRI(容器运行时接口)、API Server、DNS等的影响与依赖同样不容忽视,这直接关乎整个集群业务的稳定性。
避免循环依赖
例如,服务 A 的正常运作对服务 B 至关重要,而同样,服务 B 也可能依赖于服务 A。这种双向依赖关系在 Kubernetes 环境中特别凸显,以 ECS 异常监控为例,如果我们采用 LoongCollector Daemonset 对节点进行监控,但 ECS 异常又会反过来影响 LoongCollector 数据上报,这种复杂的依赖关系使得故障排查和系统恢复变得更加困难。在设计服务时,应该尽量避免或减少循环依赖的情况,以提升系统的稳定性与可维护性。
架构层面数据面与管控面隔离
我们需要探讨数据面是否会对管控面产生依赖。例如,如果我们的配置服务器(configserver)出现故障,这种故障是否会影响到已在运行的日志采集工具(logtail)?理想情况下,当配置服务器无法下发新配置时,logtail 应该能够依靠本地缓存继续正常发送数据。这就强调了系统在设计时必须确保数据发送的独立性,以避免由于管控层的故障而导致数据流失或中断。
压力测试
随着 K8s 单集群节点规模的不断增加,iLogtail/LoongCollector DaemonSet部署模式下对单一服务请求的压力也是呈现倍数级增加。我们当前的架构中也存在类似的设计与依赖,比如DaemonSet 的 Pod 会请求 LoongCollector 承担 Meta Server 的 Pod。 这意味着我们有必要对一定集群规模下服务架构的稳定性与性能进行模拟测试,确保在生产环境,面对巨大规模集群的情况下,我们的服务依然能够保持高效的响应与稳定性,避免出现意外的性能崩溃。
通过不断反思与优化,我们将进一步提升 iLogtail/LoongCollector 的可靠性与稳定性,确保其在未来的复杂环境中依然具备卓越的表现。
参考链接:
应用日志数据归档
为企业提供云上日志数据的存储成本优化,供了访问日志数据采集、自动压缩、持久化存储,满足企业的网站访问日志数据的归档需求。具有低成本、免运维、弹性扩容的优势。