黑蓝色笼统科技插画元素现代科技分享中文微信大众号封面.png
文|唐博(诨名:博易 蚂蚁集团技能专家)

​ 谭崇康(诨名:见云 蚂蚁集团高级技能家)

本文 10316 字 阅览 18 分钟

蚂蚁集团运转着全球最大的 Kubernetes*(内部称为 Sigma)* 集群之一。Kubernetes 社区官方以 5K node 作为 Kubernetes 规划化的事实规范,而蚂蚁集团在 2019 年的时分,就现已保护着单集群规划超越 1W node 的 Kubernetes 集群。

这不仅仅是单集群节点量级上的差异,更是事务规划的差异,事务多样化和复杂度上的差异。

一个形象化的比方便是,假如官方以及跟着官方的 Kubernetes 运用者能幻想到的 Kubernetes 的集群规划是泰山,那么蚂蚁集团在官方的处理计划之上现已实现了一个珠穆朗玛峰。

蚂蚁集团的 Kubernetes 的演进,从 2018 年至今现已走过了 3 年多的年月,尽管在 2019 年的时分就构建了万台集群的规划,但时至今日,无论是事务形态仍是集群的服务器都发生了巨大的改变。

首要,其时的集群万台节点,主要仍是偏小规范的服务器,而如今都是大机型,尽管机器的数量也是万台,实践办理的 CPU 数量现已成倍添加。

其次是其时集群里边简直全量是 Long running 的在线事务,Pod 的创立频率每天只要几千个,如今咱们的集群上简直跑满了流式核算和离线核算事务等按需分配的 Pod,因此在 Pod 数量上成倍添加,实践办理的 Pod 数量超越了百万。

最后,是 Serverless 的事务快速开展,Serverless Pod 的生命周期根本在分钟级乃至是秒级,集群每天的 Pod 创立量也超越了几十万,伴跟着许多的 Kubernetes list watch 和 CRUD 恳求,集群的 apiserver 承受了数倍于以往的压力。

因此在事务 Serverless 的大布景下,咱们在蚂蚁启动了大规划 Sigma 集群的功用优化计划,依据事务的添加趋势,咱们设定的方针是,构建 1.4W 个节点规划的集群,一同通过技能优化,希望到达在恳求延迟上不会由于规划的原因有所下降,能够对齐社区规范,即 create/update/delete 恳求的天等级 P99 RT 在 1s 之内。

可想而知,应战是十分巨大的。

PART. 1 大规划集群的应战

毋庸置疑,大规划集群带来了许多应战:

跟着集群规划增大,毛病的爆破半径也将扩大。Sigma 集群承载了蚂蚁集团许多重要应用,保障集群的安稳和事务的安稳是最根底也是优先级最高的要求。

用户许多的 list 操作,包含 list all,list by namespace,list by label 等,均会跟着集群的规划增大而开销变大。这些合理或许不合理的 list 恳求,将让 apiserver 的内存在短时刻内快速添加,呈现 OOM 反常,无法对外呼应恳求。此外,事务方的 list 恳求也会由于 apiserver 无法处理恳求而不断重试,形成 apiserver 重启后因过载不行康复服务才能,影响整个集群的可用性。

许多 List 恳求透过 apiserver 直接拜访 etcd 服务,也会让 etcd 实例的内存剧增而呈现 OOM 反常。

跟着事务量的添加,特别是离线使命的增多,create/update/delete 等恳求的数量也敏捷添加,导致客户端恳求 apiserver 的 RT 极速上升,从而使得调度器和一些控制器由于选主恳求超时而丢主。

事务量添加将加重 etcd 由于 compact 等操作自身存在的功用问题,而使 etcd 的 P99 RT 暴涨,从而导致 apiserver 无法呼应恳求。

集群中的控制器服务,包含 Kubernetes 社区自带的控制器例如 service controller,cronjob controller 以及事务的 operator 等,自身存在的功用问题都将在大规划集群面前被进一步扩大。这些问题将进一步传导到线上事务,导致事务受损。

如核算机学科的陈旧格言所说:

All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection… and the performance problems. 」

大规划集群既是照妖镜,也是试金石。

PART. 2 大规划集群的收益

固然,构建一个大规划的 Kubernetes 集群也供给了许多收益:

为运转大型服务供给更为便利的根底设施 ,便于应对事务扩容时大幅飙升的资源需求。例如在双十一等电商大促活动期间,能够通过扩展现有集群而不是新建其它小集群来应对事务的添加。一同集群办理者能够办理更少的集群,而且以此来简化根底架构办理 。

为大数据和机器学习的离线核算使命供给更多的资源,为分时复用/分时调度等调度手法供给更大的施展空间,让离线的核算使命在在线事务的低峰期时能够运用更多的资源进行核算,享用极致弹性和极速交付。

还有十分重要的一点,在更大的集群中能够通过愈加丰富的编排调度手法来更为有效地进步集群全体的资源利用率。

PART. 3 SigmaApiServer功用优化

Sigma apiserver 组件是 Kubernetes 集群的一切外部恳求拜访进口,以及 Kubernetes 集群内部一切组件的协作枢纽。apiserver 具有了以下几方面的功用:

屏蔽后端数据耐久化组件 etcd 的存储细节,而且引入了数据缓存,在此根底上关于数据供给了更多品种的拜访机制。

通过供给规范 API,使外部拜访客户端能够对集群中的资源进行 CRUD 操作。

供给了 list-watch 原语,使客户端能够实时获取到资源中资源的状态。

咱们关于 apiserver 功用进步来说能够从两个层面进行拆解,分别是 apiserver 的启动阶段和 apiserver 的运转阶段。

apiserver 启动阶段 的功用优化有助于

削减升级改变影响时长/毛病康复时长,削减用户可感知的不行用时刻,给 Sigma 终端用户供给优质的服务体会(面向事务的全体方针是 Sigma 月度可用性 SLO 到达 99.9%,单次毛病不行用时刻 < 10min)。

削减由于发布时客户端从头 list 全量资源而导致的 apiserver 压力过大状况呈现。

apiserver 运转阶段 的功用优化的意义在于:

安稳支撑更大规划的 Kubernetes 集群。

进步 apiserver 在正常平稳运转的状态中,单位资源的服务才能;即进步能够承受的恳求并发和 qps, 降低恳求 RT。

削减客户端的超时以及超时导致的各种问题;在现有资源下供给更多的流量接入才能;

全体优化思路

构建一个大规划的 Kubernetes 集群以及功用优化不是一件简单的事,如 Google Kubernetes Engine K8s 规划化文章所言:

「The scale of a Kubernetes cluster is like a multidimensional object composed of all the cluster’s resources—and scalability is an envelope that limits how much you can stretch that cube. The number of pods and containers, the frequency of scheduling events, the number of services and endpoints in each service—these and many others are good indicators of a cluster’s scale.

The control plane must also remain available and workloads must be able to execute their tasks.

What makes operating at a very large scale harder is that there are dependencies between these dimensions. 」

也便是说,集群的规划化和功用优化需求考虑集群中各个维度的信息,包含 pod、node,configmap、service、endpoint 等资源的数量,pod 创立/调度的频率,集群内各种资源的改变率等等,一同需求考虑这些不同维度之间的相互的依赖联系,不同维度的要素彼此之间构成了一个多维的空间。

图片

为了应对如此多的变量对大规划集群带来的复杂影响,咱们采用了探索问题本质以不变应万变的方法。为了能够全面而且体系化地对 apiserver 进行优化,咱们由下到上把 apiserver 全体分为三个层面,分别为存储层*(storage)、缓存层(cache)、拜访层(registry/handler)*。

底层的 etcd 是 Kubernetes 元数据的存储服务,是 apiserver 的基石。存储层供给 apiserver 对 etcd 拜访功用,包含 apiserver 对 etcd 的 list watch,以及 CRUD 操作。

中间的缓存层相当所以对 etcd 进行了一层封装,供给了一层对来自客户端且消耗资源较大的 list-watch 恳求的数据缓存,以此进步了 apiserver 的服务承载才能。一同,缓存层也供给了按条件查找的才能。

上面的拜访层供给了处理 CRUD 恳求的一些特殊逻辑,一同对客户端供给各种资源操作服务。

针对上面提出的不同层面,一些或许的优化项如下:

图片

一同,为了更好地衡量 apiserver 的功用,咱们为 Kubernetes apiserver 制定了详细的 SLO,包含 create/update/delete 等操作的 P99 RT 方针,list 在不同规划资源状况下的 P99 RT 方针等。

一同,在 SLO 的牵引下对 apiserver 进行优化,让咱们能够在一个更大规划的 Kubernetes 集群下仍然为用户供给更好的 API 服务质量。

缓存层优化

「List 走 watchCache」

由于 apiserver 在从 etcd list 数据时会获取许多数据,而且进行反序列化和过滤操作,因此会消耗许多内存。一些用户的客户端包含了不规范的拜访 apiserver 的行为,例如某些客户端或许每隔几秒就会 list 一次,而且不带有 resourceversion。这些客户端关于 apiserver 形成了很大的内存压力,也曾经险些形成集群毛病。为了应对这些不规范的用户拜访,以及削减 apiserver 的 CPU/memory 消耗,咱们对 list 操作进行了修正,让用户的不规范 list 操作悉数走 watchCache。也便是说,用户在进行 list 操作时,恳求将不会透传到后端的 etcd 服务。

在咱们的一个大规划集群中,apiserver 内存会飙升到 400G 导致几十分钟便会呈现 OOM,期间 apiserver 关于 etcd 的拜访的 RT 也会高达 100s 以上,简直不行用。在让用户悉数 list 操作走 apiserver 的 watchCache 之后,apiserver 的内存根本安稳在 100G 左右,有 4 倍的进步,RT 也能够安稳在 50ms 量级。List 走 watchCache 也是出于 list-watch 原语的终究一致性考虑的,watch 会持续监听相关资源的信息,因此不会有数据一致性的影响。

后续咱们也在考虑是否能够把 get 操作也从 watchCache 进行操作,例如等候 watchCache 必定毫秒的时刻进行数据同步,以此进一步减小 apiserver 对 etcd 的压力,一同也能够继续坚持数据一致性。

「watchCache size 自适应」

在资源改变率*(churn rate)*比较大的集群中,apiserver 的 watchCache 巨细对 apiserver 的全体安稳性和客户端拜访量多少起着很大的作用。

太小的 watchCache 会使得客户端的 watch 操作由于在 watchCache 里边查找不到相对应的 resource vesion 的内容而触发 too old resource version 错误,从而触发客户端进行从头 list 操作。而这些从头 list 操作又会进一步关于 apiserver 的功用发生负面的反应,对全体集群形成影响。极端状况下会触发 list -> watch -> too old resource version -> list 的恶性循环。相应地,太大的 watchCache 又会关于 apiserver 的内存运用形成压力。

因此,动态地调整 apiserver watchCache 的巨细,而且选择一个合适的 watchCache size 的上限关于大规划大规划集群来说十分重要。

咱们关于 watchCache size 进行了动态的调整,依据同一种资源*(pod/node/configmap)* 的改变率*(create/delete/update 操作的频次)* 来动态调整 watchCache 的巨细;而且依据集群资源的改变频率以及 list 操作的耗时核算了 watchCache size 巨细的上限。

在这些优化和改动之后,客户端的 watch error*(too old resource version)*简直消失了。

图片

「添加 watchCache index」

在剖析蚂蚁集团的事务之后发现,新核算*(大数据实时/离线使命,机器学习离线使命)*的事务关于各种资源的 list 有特定的拜访模式,spark 和 blink 等事务方有许多的 list by label 操作,也便是通过标签来查找 pod 的拜访量许多。

通过对 apiserver 日志进行剖析,咱们提取出了各个事务方 list by label 比较多的操作,而且在 watchCache 添加了相应地添加了相关 label 的索引。在对同等规划的资源进行 list by label 操作时,客户端 RT 能够有 4-5 倍的进步。

下图为上述 watchCache 优化内容简介:

图片

存储层优化

在资源更新频率比较快的状况下,GuaranteedUpdate 会进行许多的重试,一同形成不必要的 etcd 的压力。Sigma 给 GuaranteedUpdate 添加了指数退避的重试策略,削减了 update 操作的抵触次数,也削减了 apiserver 关于 etcd 的更新压力。

在大规划高流量集群中,咱们发现 apiserver 的一些不合理的日志输出会形成 apiserver 严峻的功用颤动。例如,咱们调整了 GuaranteedUpdate/delete 等操作在更新或许删去抵触时的日志输出等级。这削减了磁盘 io 操作,降低了客户端拜访 apiserver 的恳求呼应时刻。此外,在集群资源改变率很高的状况下,” fast watch slow processing” 的日志也会十分多。这主要是标明 apiserver 从 etcd watch 事件之后,在缓存里边构建 watchCache 的速率低于从 etcd watch 到事件的速率,在不修正 watchCache 数据结构的状况下暂时是无法改善的。因此咱们也对 slow processing 日志等级进行了调整,削减了日志输出。

接入层优化

Golang profiling 一直是用于对 Go 言语编写的应用的优化利器。在对 apiserver 进行线上 profiling 的时分,咱们也发现了不少热门,并对其进行了优化。

例如:

在用户 list event 时能够看到 events.GetAttrs/ToSelectableFields 会占用许多的 CPU,咱们修正了 ToSelectableFields, 单体函数的 CPU util 进步 30%,这样在 list event 时分 CPU util 会有所进步。

图片

另外,通过 profiling 能够发现,当 metrics 量很大的时分会占用许多 CPU,在削减了 apiserver metrics 的量之后,大幅度降低了 CPU util。

图片

Sigma apiserver 关于鉴权模型采用的是 Node、RBAC、Webhook,关于节点鉴权,apiserver 会在内存当中构建一个相对来说很大的图结构,用来对 Kubelet 对 apiserver 的拜访进行鉴权。

当集群呈现许多的资源*(pod/secret/configmap/pv/pvc)*创立或许改变时,这个图结构会进行更新;当 apiserver 进行重启之后,图结构会进行重建。在大规划集群中,咱们发现在 apiserver 重启过程中,Kubelet 会由于 apiserver 的 node authorizer graph 还在构建当中而导致部分 Kubelet 恳求会由于权限问题而受阻。定位到是 node authorizer 的问题后,咱们也发现了社区的修复计划,并 cherry-pick 回来进行了功用上的修复进步。

etcd 关于每个存储的资源都会有 1.5MB 巨细的限制,并在恳求巨细超出之后回来 etcdserver: request is too large;为了避免 apiserver 将大于限制的资源写入 etcd,apiserver 通过 limitedReadBody 函数关于大于资源限制的恳求进行了限制。咱们对 limitedReadBody 函数进行了改善,从 http header 获取 Content-Length 字段来判断 http request body 是否超越了 etcd 的单个资源*(pod,node 等)*的 1.5MB 的存储上限。

当然也不是一切计划都会有所进步。例如,咱们进行了一些其它编码计划测试,把 encoding/json 替换成为了 jsoniter。比较之下,apiserver 的 CPU util 虽有降低但是 memory 运用有很大的增高,因此会继续运用默认的 encoding/json。

etcd 拆分相关优化

除此之外,etcd 拆分关于客户端拜访 apiserver 的恳求的 RT 也有很大进步。在大规划集群中,咱们采用了多份拆分方式,其间一份 etcd 是 Pod。在 etcd 拆分的过程中,咱们发现拆分出来的 etcd 的 resource version 会小于原有 apiserver 的resource version,因此会导致客户端 list-watch apiserver 时长时刻 hang ,无法收到新的 Pod 相关的事件。

为了处理这个 etcd 拆分时遇到的问题,咱们对 apiserver 的 watch 接口进行了修正,添加了 watch 操作的 timeout 机制。客户端的 watch 操作最多等候 3s,假如 resource version 不匹配,直接回来 error 让 客户端进行从头 list ,以此避免了在 etcd 拆分过程中形成的客户端因 resource version hang 住的问题。

其它优化

除此之外为了保障 apiserver 的高可用,蚂蚁 Kubernetes apiserver 进行了分层分等级的限流,采用了 sentinel-go 加 APF 的限流计划。其间 sentinel-go 来限制总量,进行了 ua 维度,verb 维度等多维度混合限流,避免服务被打垮,APF 来保障不同事务方之间的流量能够公平介入。然而,sentinel-go 中自带了周期性内存采集功用,咱们将其封闭之后带来了必定的 CPU 利用率的进步。

另外,咱们也在和客户端一同优化 apiserver 的拜访行为。截止现在,Sigma 和事务方一同对 blink operator*(flink on K8s)* / tekton pipeline / spark operator 等服务进行了 apiserver 运用方式方法上的代码优化。

优化效果

下图分别为咱们两个集群分钟等级流量的对比,其间一个集群的事务由于事务兼并有了一个跨越式的添加,集群的节点规划范围,超越万台。能够看出来,跟着事务的逐渐上升,集群的压力呈现了数倍的压力进步。各类写恳求都有显着的上升。其间 create 和 delete 恳求比较显着,create 恳求由每分钟 200 个左右上升到了每分钟 1000 个左右,delete 恳求由每分钟 2.7K 个 上升到了 5.9K 个。通过咱们的优化,跟着事务方面的迁移逐步推进,在规划和负载持续上升的布景下,全体集群运转平稳,根本上到达了集群优化的预期。

图片

根底资源

在各类型的流量跟着事务添加有不同程度的上升的状况下,通过优化,apiserver CPU 利用率下降了约 7%。但是在内存上,增多了 20% 左右,这是由于 watchCache 在敞开动态调整后比较之前缓存了更多的不同类别资源*(node/pod等)*的方针。

缓存更多资源方针带来的收益是,削减了客户端的重连而且降低了 list 操作个数,一同也间接削减了客户端各类操作的 RT,进步了全体集群和运转的事务的安稳性。当然后续也会继续针对削减 apiserver 的内存运用量进行优化。

图片

RT

写恳求的 RT 关于集群和事务的安稳性是最要害的方针之一。经优化往后,客户端拜访 apiserver 的各类写恳求的 P99,P90,P50 RT 均有显着的下降,而且数值愈加趋于平稳,标明 apiserver 在向着高效且安稳的方向开展。

图片

(注:RT 对比在包含 etcd 拆分之后进行)

Watch error 和 list 个数

不合理的 watchCache 巨细会使得客户端的 watch 操作由于在 watchCache 里边查找不到相对应的 resource vesion 的内容而触发 too old resource version 错误,也便是下面的 watch error,从而会引发客户端对 apiserver 的从头 list。

在优化之后,pod 的每分钟 watch error 的个数下降约 25%,node 的 watch error 下降为 0;相应的 list 操作个数每分钟也下降了 1000 个以上。

图片

PART. 4 未来之路

总体来说,进步一个分布式体系全体的才能,咱们能够从以下方面下手:

1.进步体系自身架构,进步安稳性与功用

2.办理体系接入方的流量,优化体系接入方的运用方法和架构

3.对体系依赖的服务进行优化

对应到 apiserver 的功用优化来说,未来咱们还将从以下几个方面继续深入:

图片

  1. 针对 apiserver 自身,一些或许的优化点包含:优化 apiserver 启动总时刻,进步 watchCache 构建速度;threadSafeStore 数据结构优化;对 get 操作采用缓存;对 apiserver 存入 etcd 的数据进行紧缩,减小数据巨细,借此进步 etcd 功用 等等。

  2. 除了优化 apiserver 自身之外,蚂蚁 Sigma 团队也在致力于优化 apiserver 上下游的组件。例如 etcd 多 sharding,异步化等高效计划;以及关于各种大数据实时和离线使命的 operator 的全体链路的优化。

  3. 当然 SLO 的牵引必不行少,一同也会在各个方针的量化上进行增强。只要这些协调成为一个有机的全体,才能说咱们有或许到达为运转在根底设施上面的事务方供给了优质的服务。

构建大规划集群道阻且长。

后续咱们会继续在上面列举的各方面进一步投入,而且为更多的在线使命、离线使命、新核算使命供给更好的运转环境。

一同,咱们也将进一步进步方法论,从缓存、异步化、水平拆分/可扩展性、兼并操作、缩短资源创立链路等大方向上进行下一步的优化。跟着集群规划的继续添加,功用优化的重要性也会日益凸显,咱们将朝着构建和保护关于用户来说高效可靠高保障的大规划 Kubernetes 集群这一方针继续尽力,就像 Kubernetes 这个名字的寓意一样,为应用程序保驾护航!

「参考资料」

.【Kubernetes Scalability thresholds】

github.com/kubernetes/…

.【Kubernetes scalability and performance SLIs/SLOs】

github.com/kubernetes/…

.【Watch latency SLI details】

github.com/kubernetes/…

.【Bayer Crop Science seeds the future with 15000-node GKE clusters】

cloud.google.com/blog/produc…

.【Openstack benchmark】

docs.openstack.org/developer/p…

「求贤若渴」

蚂蚁集团 Kubernetes 集群调度体系支撑了蚂蚁集团在线、实时事务的百万级容器资源调度, 向上层各类金融事务供给规范的容器服务及动态资源调度才能, 担负蚂蚁集团资源本钱优化的职责。咱们有业界规划最大 Kubernetes 集群,最深入的云原生实践,最优异的调度技能。欢迎有意在 Kubernetes/云原生/容器/内核阻隔混部/调度/集群办理深耕的同学加入,北京、上海、杭州期待我们的加入。

联系邮箱 xiaoyun.maoxy@antgroup.com

本周引荐阅览

图片SOFAJRaft 在同程旅游中的实践

图片技能风口上的限流

图片蚂蚁集团万级规划 k8s 集群 etcd 高可用建造之路

图片2021 年云原生技能开展现状及未来趋势

图片