【阅读原文】戳:OpenAI故障复盘 - 阿里云容器服务与可观测产品如何保障大规模K8s集群稳定性
本文作者:
容器服务团队:刘佳旭、冯诗淳
可观测团队:竺夏栋、麻嘉豪、隋吉智
1. 前言
Kubernetes(K8s)架构已经是当今IT架构的主流与事实标准(CNCF Survey[1])。随着承接的业务规模越来越大,用户也在使用越来越大的K8s集群。Kubernetes官方建议的最大集群规模是5000节点。甚至,如OpenAI通过技术优化,曾将K8s集群扩展至7500节点(Scaling Kubernetes to 7,500 nodes[2])。这种千级别节点的大规模K8s集群,会容易引起分布式系统内部瓶颈,但也增加了系统的脆弱性。
1.1. OpenAI故障复盘分析
近日OpenAI旗下AI聊天机器人平台ChatGPT、视频生成工具 Sora及其面向开发人员的API自太平洋时间12月11日下午3点左右起发生严重中断。故障的根因是在上线获取集群控制面监控数据的新的新可观测功能时,可观测的组件会在每个集群节点上对K8s Resource API发起访问,突发造成巨大的Kubernetes API负载,导致K8s控制平面瘫痪,进而使DNS服务发现功能中断,最终影响到了业务。(OpenAI故障报告[3])
1.2. 阿里云如何保障大规模K8s集群稳定性,以应对如此故障
这次故障在阿里云产品体系中直接相关的是阿里云容器服务(Kubernetes),以及阿里云可观测产品(Prometheus、Telemetry)产品。
故,我们对本次OpenAI故障高度重视,希望借此机会介绍我们在大规模K8s场景下我们的建设与沉淀。以及分享对类似故障问题的应对方案:包括在K8s和Prometheus的高可用架构设计方面、事前事后的稳定性保障体系方面。
阿里云容器服务团队(负责Kubernetes云产品)与阿里云可观测团队(负责Prometheus云产品)旨在为用户提供稳定可靠的K8s集群环境,以及使用可观测体系帮助用户构建全面的稳定性保障体系。我们的客户也有非常多上千节点的大规模K8s集群环境,在大规模K8s集群的稳定性保障体系方面有一定经验沉淀。
OpenAI的大规模集群故障也是我们很好的学习案例,这里本文想借助这次案例为契机:
1. 介绍阿里云容器服务(K8s)、可观测团队(Prometheus)的大规模集群稳定性建设。
2. 以及用户在大规模K8s集群场景下的最佳实践,用户也需要采用正确的使用方式才能一起保障大规模K8s集群的稳定性。
2. 当K8s集群规模过大会遇到哪些风险与挑战
K8s集群控制面/数据面数据流图
2.1. K8s集群的本质是分布式系统,剖析其中瓶颈才能对症下药
首先我们要简单介绍下K8s集群控制面、数据面的大致架构:
控制面负责集群的API层、调度、资源管理、云资源管理等控制面功能,K8s组件:
apiserver/etcd/scheduler/kube-controller-manger/cloud-controller-manager
数据面负责集群的节点管理、Pod生命周期管理、Service实现等数据面功能,承载业务Pod的主体。包含:K8s组件,kubelet/kube-proxy;系统组件,日志、监控、安全等组件;其他组件,用户业务组件。
控制面、数据面和云资源是有机结合的整体!集群的全链路中,任何一个组件、子链路成为瓶颈,都可能影响到集群的整体稳定性。
我们需要从K8s系统中发现瓶颈、治理以及优化瓶颈,最终实现K8s系统在给定云资源情况下的稳定、高效的利用。
3. 稳定性增强-阿里云在大规模K8s集群场景的保障增强
3.1. 大规模容器服务ACK集群的稳定性保障机制增强
大规模ACK集群通过高可用配置、托管组件稳定性增强和系统组件优化等综合技术,对集群的的稳定性进行全面优化和提升。
通过控制面可用区级别和节点级别的高可用配置,全部控制面组件实现高可用打散。以APIServer为例,多副本跨AZ、跨节点高可用部署方式,任何一个AZ失效不影响服务可用性。在3AZ地域,ACK托管集群控制面的SLA是99.95%。对于不具备3AZ的地域,ACK托管集群控制面SLA是99.5%(不具备单可用区的故障容忍)。
托管核心组件进行弹性、资源隔离、限流、请求处理能力等方面的稳定性优化,例如:APIServer支持自动弹性VPA+HPA、etcd支持基于推荐资源画像的VPA、APIServer的动态限流等等。
ACK系统组件严格按照最佳规范进行优化和改造,降低对控制面的压力,包括:对控制面的LIST请求、消除对控制面资源消耗大的请求;对非CRD资源的API序列化协议不使用JSON,统一使用Protobuf等等。
3.2. 阿里云Prometheus对大规模集群场景的增强
本次导致OpenAI故障的根因是上线新的可观测能力时发生的,这里在阿里云体系中,直接对应着可观测团队容器监控产品-阿里云Prometheus。
阿里云Prometheus与容器服务一直以来深度合作,对上千节点的大规模K8s场景也有很多深入的沉淀和建设。
不管是本次OpenAI故障直接相关的容器服务K8s控制面观测能力[4],还是数据可靠性上,还是可观测组件本身对集群造成的负载上,都有重点优化建设。以下是一些具体的工作内容:
3.2.1. 更智能的服务发现与多副本采集架构 - 消除热点
阿里云Prometheus结合支撑众多阿里云产品、阿里集团内部大规模生产集群中可观测能力建设的多年经验,首先对采集探针架构完成了升级改造,实现控制本次OpenAI所遇到故障爆炸半径与影响面的目标。
我们采用两级角色体系解耦了Prometheus采集过程中的两类关注点:
1. Master角色负责实时地服务发现、任务调度与配置分发
• 所有标准类型的资源对象数据均通过二进制Protobuf编码获取,以便在大规模环境中获得更好的性能
• 默认仅一个Pod实例对K8s API Server建立List && Watch通信,降低API Server访问压力
• 在必要情况下通过主备双副本实现高可用工作
2. Worker角色负责高效地指标采集、Metric Relabel处理与数据上报
• 可依据采集目标规模以及各个目标上暴露的指标量规模进行Scale Out
• 由于Worker不直接对K8s API Server通信,扩大角色实例数量对API Server无影响
阿里云Prometheus部署、数据流架构图
这正是阿里云Prometheus采集探针相对于目前业界众多社区开源或商业版本探针的重要区别,该架构实现保障了采集能力的扩展绝不会对API Server等关键组件造成冲击——并不是直接引入StatefulSet、DaemonSet工作负载模式实现多副本的采集。
特别地,通过“配置管理中心”的服务端能力建设解除了配置分发行为对K8s API Server的依赖,进一步降低访问压力。此外,Master与Worker的自监控指标同步上报云端存储,供云产品工程师实时掌握各采集探针的运行状况、性能水位与异常信息,确保及时对故障集群进行告警与应急。
3.2.2. 适应大规模集群的Exporters优化改造 - 减少负载
完成采集探针自身改造,支撑万级目标稳定高效采集的同时,我们也对Kubernetes+Prometheus社区生态中常见Exporter在大规模集群中的运行稳定性进行了重写与优化。
以kube-state-metrics为例,作为K8s集群资源可观测事实意义上的标准,它在面临海量资源对象(不仅仅Pod,还有ConfigMap、Ingress、Endpoint等)情况下需要更多的内存运行资源来避免OOMKilled,当节点规格限制不足以执行VPA操作时,官方推荐方案仍然是通过StatefulSet工作负载拉起多个kube-state-metrics副本,并且每个实例都对K8s API Server 执行完整的List&Watch访问后,再通过Shard Hash的方式来实现HPA的目标——这同样会酿生本次OpenAI的故障。
为此,我们也积极拥抱大数据社区的前沿技术,如:通用内存列式数据格式,对kube-state-metrics进行重构改写,获得更高的数据压缩比,通过单副本运行即可实现大规模集群中资源状态指标的稳定产出,避免造成对K8s API Server的访问压力。
3.2.3. 实现业务故障隔离的托管采集探针 - 旁路采集方案
传统Prometheus采集探针直接部署在用户容器集群内,对集群内服务发现的采集目标定期抓取指标数据并上报阿里云Prometheus数据网关。
虽然云产品工程师具备对采集探针专业化的运维监控技能,但由于没有直接操作集群环境的权限,以往线上技术支持流程,都依赖将命令发给用户执行操作,不仅效率低,并且可能遇到交流中理解不一致导致的排查方向错误。
另外当灾难发生时,由于采集探针与业务同集群无法正常工作,但灾难阶段又正是用户最需要一双“眼睛”来观测系统与业务的受损程度的时候。
因而,我们着手转向Serverless这种服务模型。Prometheus采集探针本质上是个Probe,不一定要部署在用户集群,只要满足网络打通的条件,所有探针运行在云产品托管的集群池中,即可将采集能力Serverless化,不仅可降低用户集群资源负担,也有助于提升组件运维的灵活度。
在OpenAI本次故障中,由可观测组件引起K8s API Server/ CoreDNS服务不可用,同时导致集群内所有的观测活动陷入瘫痪,在最需要可观测的时刻,却进入了盲区。而使用托管探针模式,则将观测组件本身与集群的故障隔离开来。尽管由于API Server不可用,无法及时发现新的监控目标,但已调度下发的采集任务仍可继续执行,数据上报至云端存储也不再依赖于集群内的CoreDNS。
综上,使用托管形态的阿里云Prometheus容器监控服务可以屏蔽用户环境复杂性,组件运行稳定性得到了更好的保障,为更快的灾难恢复提供架构支撑,在这种灾难性故障下,托管探针模式的阿里云Prometheus容器监控服务依然能够确保一定程度的数据完整性。
3.2.4. 可靠的数据链路 - "out-of-band" 带外数据链路建设
可观测性的另一个挑战是,监控采集组件如果部署在用户集群侧,当集群环境出现问题时,监控系统也会一并宕掉,不能起到观测异常现场的作用。
阿里云容器服务通过建设“带外数据链路(out-of-band)”来解决此问题。
3.2.4.1. ControlPlane组件自身监控数据的带外数据链路
反映集群稳定性的关键指标、ControlPlane组件的监控指标[5]数据,通过托管侧的组件透出数据,不受集群本身环境影响,我们内部称为“带外数据链路(out-of-band)”。当集群本身异常时,只会影响和集群内部环境相关的“带内链路(in-band)”,而不会影响这些“带外数据链路(out-of-band)”。保证了集群稳定性的关键数据的可靠性。
阿里云容器服务托管集群,通过部署单独的组件监控ControlPlane组件自身的监控数据。当关键控制面组件异常时,保证监控数据的可靠性。
3.2.4.2. 容器层以下的节点的虚拟机、硬件、操作系统层问题的带外数据链路
同理,集群关键组件的事件、能感知集群中的节点(ECS)底层异常的主动运维事件,也通过“带外数据链路”(out-of-band)直接写入到用户的SLS事件中心中。
以此方案形成与用户集群环境完全解耦的数据源、数据采集链路,保证监控数据的可靠性。
参考文档:容器服务托管节点池[6]对ECS系统事件[7]的透出。
4. 最佳实践 - 用户如何正确地使用大规模K8s集群
K8s本质是一个非常易用的分布式系统,分布式系统由于PAC原则,永远都存在承载能力的上限。
这里就还是需要K8s集群的用户,不管作为K8s组件开发者,还是K8s运维人员,采用正确的使用方式来使用K8s集群,才能在千级别节点的大规模K8s集群中保证集群的稳定性。
在此经过阿里云容器服务团队的经验沉淀,我们提供涵盖事前预防观测、事后快速定位和恢复的成熟产品能力,帮助用户构建集群稳定性运维体系。
4.1. 集群规模控制(容量规划)&正确的发布流程(安全发布流程)
首先,站在运维的角度来看,我们需要时刻考虑减小爆炸半径。
首先当用户的业务规模还未发展成需要一个大规模K8s集群来承载的程度时,我们建议用户通过合理的容量规划来控制集群规模的大小,如可以通过微服务架构等方式,拆分业务的部署结构在不同的集群上,以此来减小K8s规模。
其次,站在发布的安全生产流程上,我们需要考虑可灰度、可回滚、可监控的安全发布最佳实践。且每批灰度间隔需要充分观测逻辑是否符合预期,且在观测到异常问题后应该马上回滚。
4.2. 事前 - 观测能力与关键报警配置
4.2.1. 成熟的集群控制面观测能力
首先用户可以通过我们阿里云容器服务提供的集群ControlPlane观测能力,清晰感知到集群控制面组件的当前状态。我们提供ACK集群控制面监控大盘[8],以及控制面组件日志监控[9]功能,帮助用户清晰透明地观测集群稳定性问题。
(查看ACK集群控制面监控大盘[8])
在我们遇到的典型大规模集群故障场景,如下图:
场景1:用户侧组件异常请求泛滥,导致APIServer负载过高;
场景2:大请求导致的K8s集群SLB带宽被打满,导致APIServerRT飙升、或者只读请求飙升。
我们可以通过集群控制面观测能力剖析问题,样例如下:
上面可观测能力可以帮助决策出精准的诊断路径:
【问题快速发现】依据API-Server指标水位进行问题定位。
【根因快速定位】依据API-Server访问日志定位问题瓶颈应用,并精准降级,详见本文4.3.4节,如何快速定位对控制面组件造成主要压力的“元凶”请求组件,并快速降级。
【止血/闭环问题】停止/优化应用的List-Watch资源行为、性能,最佳实践参考本文4.2.3节,阿里云的组件稳定性优化。
4.2.2. 经历经验沉淀的关键报警规则
虽然有强大的可观测能力,但用户不可能每时每刻盯着监控大盘,阿里云容器服务报警中心功能[10]为客户提供经过经验沉淀的K8s集群运维关键报警规则模版。
其中包括上文所提到的集群核心组件异常报警规则,可以覆盖如ControlPlane组件异常、APIServer的SLB带宽打满等场景的预警。
非常推荐用户确保这些报警规则开启并订阅通知到负责集群运维的SRE人员。您只需要购买集群时默认开启报警规则;或在容器服务控制台,运维管理->报警配置中开启规则并添加通知对象即可订阅。
4.2.3. 开发K8s组件的最佳实践
4.2.3.1. 规范组件LIST请求
必须使用全量LIST时添加resourceVersion=0,从APIServer cache读取数据,避免一次请求访问全量击穿到etcd;从etcd读取大量数据,需要基于limit使用分页访问。加快访问速度,降低对控制面压力。
4.2.3.2. 序列化编码方式统一
对非CRD资源的API序列化协议不使用JSON,统一使用Protobuf,相比于JSON更节省传输流量。
4.2.3.3. 优选使用Informer机制
大规模场景下,频繁LIST大量资源会对管控面APIServer和etcd产生显著压力。频繁LIST的组件需要切换使用Informer机制。基于Informer的LIST+WATCH机制优雅的访问控制面,提升访问速度,降低对控制面压力。
4.2.3.4. 客户端访问资源频度
客户端控制访问大规模全量资源的频度,降低对管控的资源和带宽压力。
4.2.3.5. 对APIServer访问的中继方案
大规模场景下,对于Daemonset、ECI pod等对APIServer进行访问的场景,可以设计可横向扩容的中继组件,由中继组件统一访问APIServer,其他组件从中继组件获取数据。例如ACK的系统组件poseidon在ECI场景下作为networkpolicy中继使用。降低管控的资源和带宽压力,提升稳定性。
当前阿里云Prometheus也是采用此类中继逻辑来减少的集群负载,从而避免随K8s和部署应用的规模笛卡尔积式地增大对集群的负载,当然此类中继逻辑都需要针对组件定制开发。
4.3. 事后 - 快速恢复与止血
K8s故障的应急处理与快速恢复,不仅需要建立常态化的故障演练和应急支撑机制,还应涵盖从故障发生到恢复的全链路响应流程,包括故障的实时监测、精准定位,以及问题的及时解决。
4.3.1. 定期演练和应急预案
需要通过定期开展演练与评估,持续优化和演进应急预案,可以提升整体故障应对能力和恢复速度,减少故障对业务的影响时长和严重程度。这种系统性的能力建设,将为K8s环境的稳定性和可靠性提供强有力的保障。
从具体应急措施的角度,控制面由于请求压力过大导致出现无响应、OOM甚至雪崩,本质上需要限制请求,尤其是LIST大量资源的请求,这些请求处理的过程中对etcd和apiserver都会带来显著的开销;apiserver作为etcd的一种缓存,集群中全部资源会缓存在apiserver内存中,与此同时请求到达apiserver后,apiserver处理请求过程中产生的编解码也会占用缓存,如果请求频繁而且请求资源数量巨大,都会导致控制面apiserver内存骤增。
同时,在有条件的情况下,尽量扩容Master节点/组件内存和CPU资源。在实际应急中,这个措施出于硬件资源的限制不总是能满足,此时就需要更加依靠限流策略应急。
4.3.2. 应急的限流策略
应急的限流策略包括(1)降低apiserver inflight request参数,需要重启APIServer 或者(2)根据监控发现访问压力过大的请求,下发在故障演练充分验证过的APF限流规则,包括针对指定UA(例如OpenAI案例中的Telemetry组件对应的UA)实现动态生效的限流效果。
4.3.3. CoreDNS缓存策略可能会造成误导
注意,不建议做CoreDNS的永久缓存[11](serve_stale开启),当真实发生集群控制面异常时,延长CoreDNS内的缓存并不能延续业务Pod的正常运转状态,CoreDNS内过期缓存的DNS解析关系会让业务Pod发起的访问触达到完全错误的IP地址。
并且CoreDNS的缓存时长会一定程度上掩盖控制面组件已经异常的现象,造成集群还正常运转的假象,增加排查难度与排查时间。
4.3.4. 快速定位对控制面组件造成主要压力的“元凶”请求组件,并快速降级
如何通过控制面监控大盘,定位到主要压力来源。
通过daemonset方式部署的组件,并对集群控制面有高频率、大范围的list、watch请求是我们所遇到的集群控制面故障的最大元凶。
(阿里云容器服务APIServer监控大盘的客户端粒度分析)
阿里云容器服务APIServer监控大盘,提供追溯调用来源方client/操作资源resource/操作行为verb等细粒度指标。
帮助K8s集群用户在出现控制面高负载预警或故障时能准确定位到大负载压力的来源,并帮助决策快速降级掉“元凶”应用,快速恢复整个集群稳定性。
4.3.5. admission controller(准入控制器) 造成的压力
K8s提供动态准入(Dynamic Admission Control)能力,由admissionwebhook配置以及admissioncontroller组成,是K8s非常杰出的机制,能像AOP一样帮助进行集群资源生命周期的改造行为。
但是admissionwebhook是第二大部分我们遇到的集群控制面故障的元凶,admissionwebhook由于会把用户自定义行为加入到K8s关键的资源生命周期中,可能加大K8s集群本身的不稳定性。同时由于admissionwebhook会拦截所有监听的k8s对象的请求,若定义不当,admissionwebhook在大规模K8s集群下会产生海量的APIServer负担。阿里云容器服务的控制面监控大盘专门设计,希望通过控制面监控大盘,定位到admission controller造成的压力。
(阿里云容器服务APIServer监控大盘的准入控制admissionwebhook负载分析)
5. 总结
K8s是业界主流的基础设施架构,Prometheus也已经成为新一代指标监控的实施标准,我们面对的是巨大的客户体量,超大规模的K8s集群可能遇到的风险及挑战是不可避免的。
我们只有持续关注故障沉淀下来的经验,希望通过一次次故障事件学习并自审,不断优化,才能在应对挑战时更加从容,以求更好地为用户提供更稳定、更可靠的基础设施。
相关链接:
[1]CNCF Survey
https://www.cncf.io/reports/cncf-annual-survey-2023/
[2]Scaling Kubernetes to 7,500 nodes
https://openai.com/index/scaling-kubernetes-to-7500-nodes/
[3]OpenAI故障报告
https://status.openai.com/incidents/ctrsv3lwd797
[4]容器服务K8s控制面观测能力
[5]ControlPlane组件的监控指标
[6]容器服务托管节点池
[7]ECS系统事件
[8]ACK集群控制面监控大盘
[9]控制面组件日志监控
[10]报警中心功能
[11]CoreDNS的永久缓存
我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。
获取关于我们的更多信息~