ASI 2021 年双十一万级别超大规模集群的高性能提升
发布时间:2025-05-22 00:38:11
作者:益华网络
来源:undefined
浏览量(2)
点赞(2)
摘要:01 缘起统一调度和弹性调度 AliwareASI 是 Alibaba Serverless infrastructure 的缩写,是针对云原生应用设计的统一基础设施。为了实现最大化利用云的能力,通过统一调度,实现了 ASI 成为阿里集团所有业务的底座,包括新加入到统一调度架
缘起统一调度和弹性调度
Aliware
ASI 是 Alibaba Serverless infrastructure 的缩写,是针对云原生应用设计的统一基础设施。为了实现最大化利用云的能力,通过统一调度,实现了 ASI 成为阿里集团所有业务的底座,包括新加入到统一调度架构的搜推广业务,实时计算 Flink 业务,FaaS 业务,中间件,新计算 PAI 等,以及通过统一调度来进行架构升级的 MaxCompute 业务, 泛电商业务等。ASI 通过一套架构成为所有业务和基础服务的底座,带来的是整体阿里集团的站点交付在容器和调度层面完成大一统,今年在云侧的交付效率提升,在 9.15 完成建站压测验收发挥了非常重要的作用。
统一调度架构
Aliware
在线场景下应用服务的资源约束多,追求最佳部署:既要保障应用服务的高可用性,还要保障应用服务的运行时效果,调度吞吐一般比较慢;离线场景下,尤其是以 MaxCompute 为代表的大数据计算场景任务规模大、任务规格小、运行周期短,就要求调度离线任务时需要具备很高的调度吞吐能力(十万级别的调度频次)。针对在线以及搜索的离线我们采用的 K8s 原生的 pod 链路或 pod 兼容链路。针对 MaxCompute 采用自研的 Task 链路。MaxCompute 单集群上万台机器时,每秒十万级别的调度频次带来这样高频的资源流转对 Kube ApiServer 消息转发以及 etcd 造成很大压力,我们通过性能分析以及压测得出,K8s 的原生 pod 链路能力无法满足 MaxCompute 的性能需求。因此 ASI 统一调度在 K8s 原生 pod 链路上,创造出了基于 Pod 和 Task 双链路的统一调度架构。03统一调度架构规模化对 ASI 底座带来的全方位挑战
Aliware
ASI 在统一调度中完成 2 个重要的角色,第一个是供给云原生资源,为业务提供 pod 对应的计算资源;第二个是提供状态数据库,为业务提供状态信息存储。基于 ASI 构建的搜推广等上面跑的离线业务,Blink、PAI、Holo 等新计算平台,APICallLatency 已经成为其计算链路服务 SLO 的关键部分,大促建站,快上快下的在线站点快速交付,APICallLatency 也成为其 Pod 交付 SLO 的关键部分。

性能瓶颈的分析思考
Aliware
统一调度项目对 ASI 单集群的规模化能力是强依赖的。首先是搜推广独立集群年初计划数十个集群即将迁移 ASI,并进一步迁移统一调度协议。它上面的集群需要跑大量离线任务,这些离线任务扩缩容接口 QPS 将预计达到千级别 QPS。同时搜推广业务对管控面的可用性依赖比较高,管控面不可用中断时间过长可能会导致 P1、P2 级别故障,需要将管控面性能抖动或者异常恢复时长控制在安全时间范围内。另外是 ODPS&电商混部场景,快上快下(大促当日在数分钟内完成站点的拉起和释放)第一次在 ASI 场景落地,另外快上快下第一次在 ASI 上进行落地,研发,性能优化和稳定性保障都面临挑战,新的链路对周边系统的系统压力变化也是优先要关注,并推动相关依赖方进行优化,包括实际我们发现快上快下会打满云底层存储云盘,统一调度多了一倍的离线组件,预计增加 1 倍 pod watch 等压力,这些都是需要重点解决的问题。
ASI常态化压测-性能SLO可持续性保障
Aliware
在面对统一调度规模化的需求时,我们首先做的是规模化推演,推演集群的压力,从数据推演压力和潜在的解决方案。但光有这些不够,重要的是建立起全链路的压测环境,这个我们基于 Kubemark 搭建了大规模集群模拟的平台,通过一个容器启动多个Kubemark 进程的方式,使用了 数百 个 4c 的容器模拟万级别节点的 kubelet。同时我们构建了多套稳定持续的压测环境,能够去根据不同的 K8s 版本进行仿真度提升,最终支撑线上的压测场景。



ASI优化实践
Aliware
从管控的核心链路来看,主要的瓶颈点在于中心的组件,主要包括 APIServer,Etcd,Webbook,Controller(Operator)以及业务侧。除了 Etcd 外,其他 ASI 内部组件都具备一定的横向扩展能力。当然 APIServer 也不能无限制扩容,否则也会对 Etcd 造成过多的压力。我们可以从管控 Master 的角度来看,优化的策略是怎么样的:1)客户端侧,包括 webhook,controller,operator,kubelet,各路 daemonset 等。针对客户端可以做 cache 优化,让各个 client 优先访问本地 informer cache,也需要做负载均衡优化,主要包括对 apiserver 的负载均衡,当然要想降低对存储侧的压力,需要控制器和 kubelet 进行优化合并写请求,减小对 ASI 链路的写放大,降低对 etcd 的写压力。同时针对客户端对服务端的各种压力,可以通过组件性能规范来完成改造,在组件启用,准入的时候进行规范校验是否满足,进而推动最佳实践的落地,包括推动组件的合并,下线,以及组件不合理 QPS 整改,json 转 pb 等。2)服务端侧,可以从访问层,缓存层,存储层 3个层次进行优化。在访问层上重点建设了基于 ua limiter 和 api 的精细化限流能力等;在缓存层,我们重点建设了 cache 的索引优化以及 watch 优化,包括今年重点对 watch 的多索引优化,在存储层上重点通过 snappy 压缩算法对 pod 进行数据压缩。3)存储侧,主要指的是 etcd 侧,一方面通过管控的其他组件降低对 etcd 的读写压力外,自身主要通过内核升级外,更多通过 etcd 的数据进一步分片,基于硬件的升级方案改造;其他也包括通过无阻塞 compact,multi boltdb,存储引擎切换来降低 etcd compact 的带来的抖动等。4)业务侧,主要包括 ASI 上层的各个平台,以及具体的业务。这方面主要包括降低 pod 数据量大小,如 mesh 对 pod yaml 的改造,搜推广 C2 平台降低 pod yaml 的大小以及 pod 的个数,并推动业务侧基于 ASI 提供的基础防护能力和业务特征做更符合业务安全特性的防护能力,包括限流,限额等。
APIServer性能优化Tips
Aliware
ASI APIServer Kubernetes 集群的所有外部请求访问入口,以及 Kubernetes 集群内部所有组件的协作枢纽。APIServer 具备了以下几方面的功能:1)屏蔽后端数据持久化组件 etcd 的存储细节,并且引入了数据缓存,在此基础上对于数据提供了更多种类的访问机制。2)通过提供标准 API,使外部访问客户端可以对集群中的资源进行 CRUD 操作。3)提供了 list-watch 原语,使客户端可以实时获取到资源的状态。
数据压缩优化
在 K8s 中,所有的对象最终均会作为 key/value 存入 etcd,当 value 较大时,会对 etcd 造成很大的压力。由于业务的复杂性,阿里巴巴电商的 Pod 相比于社区的 Pod,复杂庞大的多,社区的 pod 存入 etcd 最终可能就1kb,但电商的 Pod 可能达到 20kb,而且由于 Service Mesh 技术的引入,他们会注入额外的 sidecar 容器,最终导致 ASI 的许多 Pod 都达到了 50K。为了降低对 etcd 的压力,我们引入了 snappy 压缩优化:更新创建 pod 时,先用 snappy 压缩 pod 数据再写入 etcd读取 pod 时,从 etcd 读取数据,snappy 解压后再返回给客户端有人或许担心引入 snappy 算法带来的额外的 CPU 消耗和延时,考虑到 K8s 对象本身的序列化开销和访问 etcd 的延时,解压缩的 CPU 消耗和延时几乎可以忽略不计。优化后,一般的pod的大小可以压缩为原来的 1/2,对于注入 sidecar 容器的 pod,由于 pod 内容同质化内容较多,可以压缩为原来的 1/3。引入压缩优化后,由于底层数据格式的变化,如何上线、回滚变的尤为棘手,一般的做法是 关闭集群->数据格式转换->启动集群。为了保证集群不停服,我们采用热升级的方式开启或者关闭优化,确保开启snappy压缩优化后,kube-apiserver 也可以读取未压缩数据,关闭 snappy 压缩优化后,kube-apiserver 可以识别解压 snappy 数据。02watch索引优化
watch 是整个 K8s 体系通信的核心,所有的控制器均是通过 ListWatch 拿到 K8s 对象的最新信息干活。当集群资源变化越大时,watch 的性能压力会越大,而在统一调度中,离线的单机客户端也会 watch pod,这意味着 pod 的 watch 请求直接翻倍。在快上快下场景下,我们碰到了大规模的 event 推送延迟,导致刚开始无法满足客户要求。首先看一下 watch 的推送原理。首先 kube-apiserver 针对每一种资源会发起一个连接到 etcd,同时建立一个 event chan,接受 etcd 的 event,每一个客户端 watch apiserver 时,kube-apiserver 会为每个 client 建立一个 watcher chan,event 被 dispatch 到各个 watcher,各个 watcher 负责 filter 事件并推送出去。
etcd多client并发优化
Kubernetes 针对每一种资源会创建一个 grpc 连接至 etcd,所有的读/写/watch 请求均通过该通道完成,当集群规模变大、QPS 增加到一定程度时,我们发现 etcd 出现较大的访问延时,对比了 etcd 服务端的延时,发现客户端和服务端延时相差较大,客户端延时明显大于服务端延时。我们对所有资源的均创建了多个 client,并发访问 etcd,而针对部分访问需求较多的资源(pods 等)额外加大了并发,降低了 etcd 访问延时。04精细化限流和限流管理
我们引入了精细化限流方式,可以从 user agent(kubelet/kub-scheduler 等)、resource(pods/nodes 等)、verb(get/list/update)等不同维度限流。List 操作是对 kube-apiserver 性能影响最大的操作,数次对 Pod 的全量 List 就可以导致 kube-apiserver 直接 OOM,绝大多数的 List 请求可能只是读取部分符合条件的 pod。因此我们进一步精细化了 List 限流保证 kube-apiserver 的稳定性。此外我们还开启了社区最新的 APF 限流,(https://kubernetes.io/docs/concepts/cluster-administration/flow-control/)可以控制不同等级的组件获得不同的并发访问量,优先保证核心链路组件的访问质量。protobuf 改造
服务器端的优化是一方面,客户端的优化也很重要。protobuf 改造就是是其中一项重要的优化,客户端可以通过 json/yaml/protobuf 多种格式获取 kube-apiserver 数据,protobuf 相比 json 的序列化效率相比快了 5 倍,因此让使用了 protobuf 协议可以更快的全量 List 数据。这在控制器的 ListWatch 场景显得尤为重要,ListWatch 的机制是首先 List 全量数据,然后发起 Watch 请求获取增量数据,假如 List 的 latency 过长,会直接导致 Watch 失败,进而导致 ListWatch 失效,控制器开始反复 List 数据,造成系统雪崩。因此我们通过组件规范,推动所有的客户端以 protobuf 格式访问 kube-apiserver 的核心资源(pods/nodes)。后续我们可能考虑在 http 这一层引入 snappy 压缩算法,来进一步降低客户端和服务器端的 List 延时。06多路 apiserver 架构
核心方案就是通过对 apiserver 进行分组,通过不同的优先级策略进行对待,从而对服务进行差异化 SLO 保障。通过分流以降低主链路 apiserver 压力,针对 P2 及以下组件接入旁路 apiserver,并可以在紧急情况(如自身稳定性收到影响)下,做整体限流。
Etcd优化Tips
Aliware
etcd 是 K8s 集群中存储元数据,是 K8s 的基石,它的性能往往影响着整个集群的响应时间。当集群规模突破一定规模时,曾出现如下性能瓶颈问题:etcd 出现大量的读写延迟,延迟甚至可达分钟级kube-apiserver 查询 pods/nodes/configmap/crd 延时很高,导致 etcd oometcd list-all pods 时长可达 30 分钟以上控制器无法及时感知数据变化,如出现 watch 数据延迟可达 30s 以上等event 压力大影响 lease 数据,进而引起组件频繁选主。01Etcd 内核升级
我们深入研究了 etcd 内部的实现原理,并发现了影响 etcd 扩展性的一个关键问题在底层 bbolt db 的 page 页面分配算法上:随着 etcd 中存储的数据量的增长,bbolt db 中线性查找“连续长度为 n 的 page 存储页面”的性能显著下降。为了解决该问题,我们设计了基于 segregrated hashmap 的空闲页面管理算法,hashmap 以连续 page 大小为 key,连续页面起始 page id 为 value。通过查这个 segregrated hashmap 实现 O(1) 的空闲 page 查找,极大地提高了性能。在释放块时,新算法尝试和地址相邻的 page 合并,并更新 segregrated hashmap。通过这个算法改进,我们可以将 etcd 的存储空间从推荐的 2GB 扩展到 100GB,极大的提高了 etcd 存储数据的规模,并且读写无显著延迟增长。我们也和谷歌工程师协作开发了 etcd raft learner(类 zookeeper observer)/fully concurrent read 等特性,在数据的安全性和读写性能上进行增强。这些改进已贡献开源。02Etcd单资源拆分
进一步,我们通过将 API Server 中不同类型的对象存储到不同的 etcd 集群中。从 etcd 内部看,也就对应了不同的数据目录,通过将不同目录的数据路由到不同的后端 etcd 中,从而降低了单个 etcd 集群中存储的数据总量,提高了扩展性。实际大集群中,我们是这样进行拆分的:1)把访问最核心的资源放在了一起,如 pod,configmap,endpoint,lease 等;CRD 资源放在了第二个 etcd 集群;Event 放在了一个独立集群。
Etcd 硬件升级
etcd 集群对磁盘 IO 时延非常敏感,我们对比了云盘,本地盘(nvme, optane, AEP)的性能,最终发现 optane 和 AEP 的设备性能有很大提升。考虑到长期维护问题,因此我们选择了云上标准的 AEP 机型。但 AEP 机型属于本地盘,它也存一些劣势,在故障迁移时会面临更多挑战,特别是 etcd 的故障迁移,处理时长,冷热备数据的安全性,我们在本地盘基于自研的 operator 增加了这部功能,并配置相应的 1-5-10 能力。03Etcd 参数调优
1)etcd 使用 boltdb 作为底层数据库存储 kv, 它的使用优化对整体性能影响很大。通过调节不同的 batch size 和 interval, 使我们可以根据不同硬件和工作负载优化性能。2)compact的压缩周期04multi boltdb
当这种单一集群 etcd 性能承压的情况下,我们需要水平扩展多个 etcd 集群。目前我们已经使用了 K8s apiserver 的不同资源拆分到不同 etcd 的能力实现了基本的拆分功能,实现了不错的效果。但是我们看到还有一些新的需求例如单一 pod 资源本身就会有非常大的存储压力的场景,我们需要进行进一步的优化,这里我们在 etcd server 层做数据拆分。etcd 内部单一使用一个存储 boltdb 底层数据库,利用单一 WriteTX, ReadTX 进行读写。当数据量超大时,单一 db 压力很大。我们在前期测试中发现 compact 清理时,boltdb 上清理压力很大,进而导致整个集群出现堆积,造成验收不符合预期。因此,我们将 etcd 底层存储层 boltdb 进行水平拆分扩展,类似常见的数据库分库分表,具体内部使用 hash 策略进行存储对象的分配策略。具体设计如下图:
Webhook&Controller优化Tips
Aliware
webhook 承接 api-server 操作完资源后的回调,类似于一个 http 请求所经过的中间件。在 ASI 集群中大量注入逻辑和验证 都是 webhook 做的,例如 pod 的 sidecar 注入, postStartHook 注入,failizer 的注入, 级连删除的校验等。所以,webhook 在集群中属于核心链路组件。webhook 的不可用会导致集群中所有资源的变更都不可用。webhook 的延迟会导致 apiserver 的延迟,因此 webhook 的性能直接影响了 api-server 的性能,从而影响整个集群的规模化能力。01使用 protobuf 协议请求 APIServer
webhook&controller 在请求 API-Server 的时候,如果是 get/list 操作,会优先请求本地 informer。在建立和更新本地 informer 的过程中,webhook&controller 会全量 list APIServer。此时使用 protobuf 协议 相对于 json 协议可以消耗更少的资源,更快的建立 informer 本地缓存。02减少深拷贝操作
通过本地 Informer, webhook&controller 通过 list 方法获取数据时,避免了请求 APIServer,但是依然要对获取到的数据进行 DeepCopy 操作。但是这个 DeepCopy 操作并非一定需要的,只有对资源进行修改操作时,才需要 DeepCopy。为此,webhook&controller 在 List 方法里减少了 DeepCopy 操作,业务在获取到 List 对象之后再按需进行深拷贝操作 。03减少序列化/反序列操作
一次 webhook 回调请求中,会有多个 plugin 针对同一个资源进行 admit 操作。如果每一次操作都需要进行反序列化解析,那么会带来重复性计算。为此,webhook 每一次 admit 操作基于上一次操作的结果,这样反序列就可以减少到一次。在返回给 APIServer 的序列化的过程中,webhook 根据 admit 后的对象跟传入的对象相比,如果没有发生改变,那就直接返回给 APIServer,不需要经过序列化计算。04优化反序列操作
webhook&controller 存在大量的字符串发序化操作,其占据了 65%以上的 cpu 资源, 30%以上的内存资源。为此,我们从业务的角度出发,按需反序列化解析。对于没有使用到的字段,就不进行反序列化。同时,我们采用了 社区的 json-iterator/go 包代替了 golang 提供的 encoding/json 包。这样整体操作下来,json 相关的反序列化操作资源消耗降低到了 30%以下。05合并写请求,减少写放大
频繁请求 Api-Server 的原因在于在处理 reconcile 中存在着多次跟 api-server 交互请求,例如读取 all-namespace. 修改 annotation, 修改 fialnaizer,update 对象时遇到 reconflict 冲突。这些都会导致跟 api-server 的交互请求,我们针对扩缩容核心链路的写请求进行分析降低了 30%。06快速 failover
controller 中存储着近百万的对象,从 API Server 获取这些对象并反序列化的开销是无法忽略的,重 Controller 恢复时可能需要花费几分钟才能完成这项工作,这对于阿里巴巴规模的企业来说是不可接受的。为了减小组件升级对系统可用性的影响,我们需要尽量的减小 controller 单次升级对系统的中断时间,这里通过提前加载数据的方案来解决这个问题:1)预启动备 controller informer,提前加载 controller 需要的数据;2)主 controller 升级时,会主动释放 Leader Lease,触发备立即接管工作。10Kubelet优化Tips
Aliware
01对APIServer负载均衡
高可用集群实际的运行中,可能会出现多个 API Server 之间的负载不均衡,尤其是在集群升级或部分节点发生故障重启的时候。这给集群的稳定性带来了很大的压力,原本计划通过这样高可用的方式分摊 API Server 面临的压力,但在极端情况下所有压力又回到了一个节点,导致系统响应时间变长,甚至击垮该节点继而导致雪崩。在 API Server 测增加 SLB,所有的 kubelets 连接 SLB,这个也是阿里云 ACK 的标准做法。我们针对 client,特别是大量的 kubelet 节点增加一个核心功能:定期地重建连接切换 API Server 完成洗牌;同时在 kubelet 上针对一个时间段内频繁的收到 429 时,尝试重建连接切换 API Server。02降低写放大
K8s pod 写放大问题非常严重,给 apisever/etcd 带来很大压力,泛电商一般场景 23 次,搜推广 2.5 协议大概 5 次,3.0 协议 host 网络 9 次、非 host 网络 15 次,可以通过合并请求优化,在搜索高吞吐场景中,目前 kubelet 侧优化从 5 次降低为 1 次。12业务侧专项改造举例
Aliware
业务侧包括搜索 c2,mesh, faas 等合作方都进行了很多的改造。这里列一下安全为例:1. 安全基于 Mesh 的 Sidecar 改造;
1)对接上文中提到的 APIServer 多路架构中的旁路;2)CR 资源治理,包括把各个资源的数量从 O(n))降低为 O(1)常数级别;3) Pod 资源优化,主要通过合并容器,环境变量,挂载合并等缩小 pod yaml 大小等。ASI 作为云原生的引领实施者,它的高性能,高可用,它的稳定性影响着甚至决定着阿里集团和云产品的业务的发展,真诚希望容器领域感兴趣的同学进行深入交流,更期待大家的加入,一起在云端构建云的新界面,让我们的用户用好云,感兴趣的同学欢迎来撩。扫一扫,关注我们
声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。
2