乐趣区

关于开源:攀登规模化的高峰-蚂蚁集团大规模-Sigma-集群-ApiServer-优化实践

文|唐博(花名:博易 蚂蚁团体技术专家)

​ 谭崇康(花名:见云 蚂蚁团体高级技术家)

本文 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】

https://github.com/kubernetes…

.【Kubernetes scalability and performance SLIs/SLOs】

https://github.com/kubernetes…

.【Watch latency SLI details】

https://github.com/kubernetes…

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

https://cloud.google.com/blog…

.【Openstack benchmark】

https://docs.openstack.org/de…

「求贤若渴」

蚂蚁团体 Kubernetes 集群调度零碎撑持了蚂蚁团体在线、实时业务的百万级容器资源调度, 向下层各类金融业务提供规范的容器服务及动静资源调度能力, 肩负蚂蚁团体资源老本优化的责任。咱们有业界规模最大 Kubernetes 集群,最深刻的云原生实际,最优良的调度技术。欢送无意在 Kubernetes/ 云原生 / 容器 / 内核隔离混部 / 调度 / 集群治理深耕的同学退出,北京、上海、杭州期待大家的退出。

分割邮箱 xiaoyun.maoxy@antgroup.com

本周举荐浏览

SOFAJRaft 在同程游览中的实际

技术风口上的限流

蚂蚁团体万级规模 k8s 集群 etcd 高可用建设之路

2021 年云原生技术倒退现状及将来趋势

退出移动版