作者:殷达
背景
随着 v1.8 的发布,基于 OAM 的应用程序交付项目 KubeVela 已经持续发展了 3 年多。目前已被各种组织采用并部署在生产环境中。无论是与托管定义一起使用还是在多个集群上进行管理,在单个 KubeVela 控制平面上运行数千个应用程序的情况并不少见。用户经常问的一个关键问题是 KubeVela 能否承载一定数量的应用程序。为了回答这个问题,KubeVela 进行了负载测试实验,制定了性能调优策略,并为关心 KubeVela 稳定性和可扩展性的用户总结了本指南,以供各位参考。
概述
若需要寻求一个基准,如下简要的表格仅供参考。
尽管以上数据可能因各种因素(如应用程序大小)而有所不同,但此基准适用于大多数常见场景。
历史
KubeVela 负载测试历史
KubeVela 过去进行了多次负载测试:
1. 2021 年 8 月对简单应用进行了负载测试。这次负载测试验证了节点数量不影响 KubeVela v1.0 的性能。它在一个拥有 1 千个虚拟节点和 1.2 万个应用程序的单个集群上进行了测试。这表明 Kubernetes apiserver 的速率限制是 KubeVela 核心控制器的瓶颈之一。目前为止,负载测试数据被视为标准基准。参考文献[1]。
它有以下几个限制:
a.不包括在 v1.1 中发布的多集群和工作流。
b.仅测试应用程序的创建和状态保持,忽略应用程序的持续更新。
2. 2022 年 2 月 v1.2 版本对基于工作流的应用程序(workflow-based application)进行的负载测试。此次负载测试主要侧重于如何在特定场景中微调应用程序控制器的性能,例如不需要 ApplicationRevision 的情况。开发几个可选的优化标志,并证明去掉某些功能以将性能提高 250% 以上。
3. 2022 年 8 月 v1.6 版本对工作流引擎(workflow engine)进行的负载测试。这次负载测试是在 KubeVela 将 cue 引擎从 v0.2 升级到 v0.4 时完成的。它主要发现了一些不必要的 cue 值初始化操作,这将导致额外的 CPU 使用成本。通过减少 75% 的 CPU 使用量进行修复。
4. 2023 年 2 月对 KubeVela v1.8 进行的负载测试,下面将详细介绍。本次负载测试在简单应用程序、大型应用程序、多个分片、多个集群、持续更新等多个场景下进行。同时应用了几种优化方法来处理一般情况。
全面指南
Part 01. 初期
KubeVela 应用的基本流程
KubeVela 应用的基本流程
KubeVela 应用通常是 KubeVela 生态系统中最常用的对象。它由 vela-core 内部的应用程序控制器处理,并将使用集群网关进行多集群交付。KubeVela 应用正常处理主要流程如下:
- 用户通过 VelaCLI、kubectl、REST API、VelaUX 等向控制平面上的 Kubernetes apiserver 发送创建/更新/删除应用程序请求。
- 请求发送到变异 Webhook 并验证 Webhook 以进行验证和字段自动填充。
- 存储在 etcd 中的应用程序对象。vela-core 的 informer 接收到应用的创建/更新/删除事件,将其推送到队列中。
- vela-core 的应用控制器观察事件并开始协调。
- 对于新的应用程序,应用程序控制器会为其打上 Finalizer。
- 控制器计算应用程序的当前版本并在集群中创建/更新它。
- 控制器执行以工作流程为条件的工作流,运行状态维护和垃圾回收,最后,更新应用程序的状态。
应用程序工作流主要执行资源调度,为分析性能瓶颈并找到应对措施,因此有必要对整个处理流程进行概述。
如何配置高性能和鲁棒的 KubeVela?
除安装 KubeVela 之外,还有一些步骤建议用于设置高性能和鲁棒的 KubeVela 控制平面。请注意,这些步骤对于正常使用 KubeVela 并不是强制的,它们主要用于大规模场景,比如承载 2 千多个应用程序。
1. 启用可观测性插件。要全面了解您的 KubeVela 控制平面,强烈建议安装可观测性插件,包括 kube-state-metrics,prometheus-server 和 grafana。
a.如果您已拥有这些基础设施,并且它们不是由 KubeVela 插件构建的,则可以将 Grafana 仪表板[2]安装到您的 Grafana 中,并获取类似于 KubeVela 系统仪表板的内容。
KubeVela 系统仪表板
b.启用这些插件需要一些额外的计算资源,如 CPU 和内存。
2. 删除 Webhook。目前 KubeVela 的 Webhook 大大增加了应用程序变更请求的响应延迟。如果您已经安装了 KubeVela,请运行 kubectl delete mutatingwebhookconfiguration kubevela-vela-core-admission 和 kubectl delete validatingwebhookconfiguration kubevela-vela-core-admission 否则,添加 --set admissionWebhooks。
此步骤的负面影响包括以下几点:
a.无效应用程序在创建或更新期间不会被拒绝。相反,它将报告应用程序状态中的错误。
b.自动身份验证将会失效。替代方案是在应用程序应用时为其指定身份注释。
c.自动分片调度将会失效。替代方案是在应用程序应用时为其分配预定分片 ID。
3.【可选】启用 KubeVela 的多分片功能。通过使用 --set sharding.enabled=true 安装,并启用 vela-core-shard-manager 插件,可以运行多个分片(从 v1.8.0 开始)。通过分片,您可以对控制平面进行横向扩展,且不影响任何现有应用程序。
a.如果您在第 2 步中删除了 Webhook,则需要手动将应用程序的标签 controller.core.oam.dev/shard-id 设置为分片 ID(默认可以设置为 master 或 shard-0)。如果您没有删除 Webhook,应用程序将被平均分配。
b.vela-core-shard-manager 将使用相同的镜像、参数等从主分片中复制配置。如果您想使用不同参数,可以手动更改。
c.如果您没有大量的应用程序(10k+),并且您不需要对不同的应用程序进行隔离(例如按租户对他们进行分组),则不一定需要此步骤来实现高性能。
KubeVela 控制器分片架构
4.【推荐】如果可能,请在控制平面和托管的集群之间使用内网连接。建议参考此提示,但并不总是可行的。如果您的托管集群与 KubeVela 控制平面处于不同的网络区域(例如不同的地区或不同的云提供商),则无法执行此操作。内网带宽通常比互联网带宽更高,延迟也更低。因此,使用内网连接可以帮您获得更高的吞吐量和更快的跨集群请求响应速度。
如何进行负载测试
使用 KubeVela 进行负载测试的工具
在 Kubernetes 集群上设置 KubeVela 控制平面后,您可以尝试负载测试,查看您的 KubeVela 控制平面配置是否能满足您的业务需求。您可以根据使用场景从以下步骤开始。
1.【可选】设置 kubemark 来模拟 Kubernetes 节点。KubeVela 与 Kubernetes 集群的节点数无关,但如果你想尝试并验证这一点,可以使用 kubemark 模拟 Kubernetes 节点,这样允许你在不实际运行的情况下持有大量的 Pod。
a.每个 kubemark pod 都会向 Kubernetes apiserver 注册一个空节点,并将自己注册为一个节点,在此节点上分配的 Pod 不会实际执行,而是伪装自己正在运行。
b.你需要为这些空节点附加标签,并为负载测试创建的 Pod 设置节点亲和性。否则,它们可能会被分配到真实节点并实际运行,这将在负载测试期间产生巨大工作量,你应该避免这种情况。
c.你还应将污点附加到这些空节点,并为负载测试创建的 Pod 添加污点容忍。这样是为了防止将其他 Pod 分配到这些虚假节点上。例如,如果你在 Kubernetes 集群中将 Prometheus 服务器作为 Pod 运行以构建可观观测性,当它们被分配到空节点时,则不会实际运行,你将一无所获。
d.通常,一个 Kubernetes 集群中的每个节点最多可容纳数百个 Pod(这个数字在不同的配置中也有所不同),因此我们建议不要在每个空节点上运行太多的 Pod。20~40 个 Pod /节点可能是一个不错的选择。根据你想在负载测试中测试的 Pod 数量,应计算所需的空节点数。然后请记住,每个空节点都需要一个真正的 kubemark pod 来运行,因此您需要进一步估计运行一定数量的 kubemark pods 需要多少个真实节点。
e.结论:KubeVela 对一个集群的 1 千个空节点进行了负载测试,并验证了这个数字不会影响 KubeVela 的运行。有关更多实验细节,请参阅报告[3]。
2.【可选】设置 k3d/KinD 以模拟托管集群。KubeVela 与加入到控制平面的集群数也无关,除非您有超过数千个 Kubernetes 集群可供使用。如果您想验证这个事实,你可以设置 k3d 集群或 KinD 集群来模拟托管集群,并将它们加入到你的 KubeVela 控制平面。
a.托管集群使用的主要组件是 apiserver 和 etcd 。如果您不需要同时验证多个 pod 或节点的数量,则只需少量资源即可运行模拟的托管集群。就像您可以在 128 核 256 Gi 服务器上运行 200 多个 k3d 集群,并将它们加入到 KubeVela 控制平面中。
b.您可以为这些已加入的集群附加标签,并在应用程序交付期间测试选择集群的性能。例如,为每 5 个集群分配一个区域 ID。
c.托管集群上不会有实际负载运行(因为 KubeVela 主要关心应用交付,因此在多集群负载测试期间,我们主要调度零副本部署、configmaps 和 secrets)。但网络很重要,集群网关和托管集群的 apiserver 之间将有大量网络请求。因此,最好确保它们之间有足够的网络带宽和相对适中的延迟。否则,您将观察到网络 IO 速率限制。
d.结论:KubeVela 还对 200 个集群进行了负载测试,这些集群被均匀分布在 40 个区域。结果表明,控制平面能够以适当的配置处理这些集群上的应用程序。下面将进一步详细阐述。
3. 使用脚本交付大量应用程序。有一个关于使用官方脚本部署负载测试应用程序的简单指南[4]。这些脚本会自动在多个并发线程中部署多个应用程序。它包含多个要设置的环境变量,包括要读取的应用程序模板、应用程序版本、应用程序的分片 ID 和集群 ID 等。您可以为负载测试创建自己的应用程序模板,并使用脚本进行实验。KubeVela 使用该脚本测试各种示例应用程序,并获取负载测试统计数据。
Part 02. 性能优化
在 KubeVela v1.8 中,我们添加了几种优化方法来提高应用程序控制器的性能。这些方法是通过实验完成的。
状态保持并行化
在 KubeVela v1.8 之前,应用程序的状态保持阶段,应用背后的资源是以逐一的状态保存的。我们将并行化添加到这个过程中,最大并发数为 5 。这会将状态保持的性能提高约 30%+,具体取决于每个应用程序需要保留的资源数量。
优化前后的状态保持时间消耗
参考链接:
https://github.com/kubevela/kubevela/pull/5504
为列表操作索引 AppKey
通过对 5 万多个应用程序的应用控制器进行 pprof 监视,我们发现大部分 CPU 时间都花在列举 ApplicationRevisions 和 ResourceTrackers 上。
KubeVela 核心控制器 CPU 使用情况火焰图
这是因为当我们需要为一个应用程序查找这些资源时,我们用标签选择器选出相匹配的资源。但在控制器运行时 controller-runtime 的 informer下,这是通过匹配每个对象的标签来完成的。我们通过为缓存添加额外的索引来优化它,这大大减少了列出应用程序和资源跟踪器所需的时间成本。对于单列表操作,时间成本从 40 毫秒降至 25 微秒,这是优化前状态保持的一个瓶颈。
优化前和优化后的对账指标
参考链接:
https://github.com/kubevela/kubevela/pull/5572
过滤不必要的更新
当一个应用程序达到稳定状态(已发布且没有进行中的更新或删除)时,每个协调操作的主要工作是探测底层资源并检测意外的偏移。通常,配置偏移不总是发生,因此我们不需要向底层资源发送空的补丁请求。我们观察到这可以将协调时间缩短 20%。
应用程序控制器和每个阶段的对账时间
此外,我们发现应用程序控制器的每次协调都会发出应用修订更新请求。然而,一旦工作流成功执行,应用修订就不会更新。因此,这些更新请求无需删除这些请求就可以在负载测试期间将性能提升 27%。
优化前(19:50之前)和优化后应用程序控制器的对账时间对比
类似的问题是,从 v1.6 开始升级工作流后,状态被压缩,但垃圾回收的执行没有被去重。因此,当删除重复的 gc 进程,应用程序进入稳定状态时,我们进一步实现了 23% 的性能优化,此时优化了应用程序控制器的运行。
参考链接:
直接连接到集群网关
在研究将资源交付到托管集群的应用程序时,我们会发现集群网关(cluster-gateway)成了另一个潜在的瓶颈。默认情况下,多集群请求是沿着 vela-core -> kubernetes apiserver (hub) -> cluster-gateway -> kubernetes apiserver (managed) 的流程进行。这是通过利用 Kubernetes 的聚合 API 机制完成的,我们可以通过允许应用程序控制器直接与集群网关通信来进行优化。
KubeVela 的多集群连接架构
这不仅能减轻 Kubernetes apiserver 的负担,还能减少多集群请求的网络跳数。通过这种方法我们减少了 40% 的延迟。
应用程序控制器的一个多集群请求延迟从 11 毫秒降到 6 毫秒
参考链接:
https://github.com/kubevela/kubevela/pull/5595
Informer cache 减少
默认的 controller-runtime informer 将缓存我们在控制器中使用的所有结构化对象。对 KubeVela 而言,这些对象是Application,ApplicationRevision,ResourceTracker 和 ConfigMap。通过控制器分片,KubeVela 能够将Application,ApplicationRevision 和 ResourceTracker 的缓存分成不同的分片。但随着应用程序的不断更新,累积的ApplicationRevision 会通过 informer cache 占用大量内存,尽管它们并不经常使用且内部有大量重复数据。因此,为了优化 informer cache 的大小,我们采取了以下几种方法。
1. 在将 managedFields 和 kubectl.kubernetes.io/last-applied-configuration 存储在 informer 中时,需要删除 managedFields 和 kubectl.kubernetes.io/last-applied-configuration。特别是对于复杂应用程序,它们可能会很大。它们或许对于 Kubernetes apiserver 和 CLI 工具很有用,但控制器中并不需要。因此在存储到缓存之前,我们需要在控制器中将它们删除。
2. 通过共享 ComponentDefinition、TraitDefinition 和 WorkflowStepDefinition 的存储空间来减少 ApplicationRevision 的内存使用量。大多数应用程序在系统中仅仅使用几个常见定义,如 webservice 或 scaler。这些定义分别存储在 ApplicationRevisions 中,并占用了大量空间。因此,在应用程序控制器中,我们设置了一个横向公共缓存,并让这些 ApplicationRevisions 指向存储在公共缓存中的定义,从而减少内存使用。
KubeVela 的 informer cache 架构
3. 禁用 ConfigMap 的 informer cache。ConfigMap 的缓存是由 Workflow Context 引起的。应用程序工作流将运行上下文存储在 ConfigMap 中,但 ConfigMap 的缓存不仅包含工作流程使用的 ConfigMap,还包括其他未使用的 ConfigMap,这可能会占用大量空间。
优化前后的内存使用情况
在负载测试中,我们发现这些方法结合在一起对内存使用情况进行了重大改进,特别是对于持续更新。以前的负载测试并不重点关注持续更新,但这会忽略应用程序控制器中实际使用的缓存内存的增加。
Go 程序的内存使用情况并不简单。上述内存统计数据是操作系统报告的 RSS 大小。Go 程序的实际内存使用量小于此。Go 未使用的内存并不总是立即返回给操作系统,并且存在内存碎片化,这将阻止它返回所有未使用的内存。因此,这里我们还比较了 Go 报告的 ALLOC 内存和 SYS 内存。ALLOC 内存可以视为实际使用的内存大小,SYS 内存表示 Go 从操作系统获得了多少内存。
通过 3000 个应用程序和 9000 个应用程序修订版本,我们在优化之前得到了 1.02G 的 RSS,897M 的 SYS 和 401M 的 ALLOC。优化后,我们得到了 739M 的 RSS ,707M 的 SYS 和 203M 的 ALLOC。可以看到使用中的内存减少了约 50%。总内存使用情况也减少了约 25%。
参考链接:
https://github.com/kubevela/kubevela/pull/5683
Part 03. 实验
多个分片
设置
KubeVela v1.8.0 支持 controller sharding[5],允许 KubeVela 控制平面进行水平扩展。它通过利用 ListWatch 请求中的标签选择器来实现 ,controller-runtime 将其用作 KubeVela 应用程序控制器后端。它不仅限制了每个分片要处理的应用程序数量,还通过分割减少了内存缓存的大小。
控制器分片的架构
为了验证 KubeVela 控制器的多个分片可以按预期工作,我们比较了以下三种情况性能:
- 单个分片,使用 0.5 核、1 Gi 内存。
- 三个分片,每个分片使用 0.5 核、1 Gi 内存。
- 使用 KubeVela v1.7.5 的单个分片,使用 0.5 核、1 Gi 内存。 我们在单个分片情况下测试了 3000 个小型应用程序(KubeVela 核心控制器的默认配置),在三个分片情况下测试了 9000 个小型应用程序(每个分片包含 3000 个应用程序,与单个分片情况相同)。
分析
实验表明,控制器分片可以横向扩展应用程序容量
与单个分片的情况相比,三个分片中每个分片的资源使用量相似
我们可以看到,在使用三个分片时,每个分片能够处理 3000 个 应用程序且不会互相干扰。在所有应用程序发布之后,每个分片的 RSS 内存使用量增加到 412 MiB,CPU 使用率为 0.1 核(平均协调延迟为 15 毫秒)。目前,该系统总共包含 9000 个应用程序。与单个分片相比,分片模式下的内存使用量和 CPU 使用量相对处于同一水平。运行 3000 个应用程序的单个分片,在所有发布之后使用大约 0.08 核,并使用 320 MiB 的内存。在使用分片和不使用分片之间也没有明显的 Reconcile 延迟差异(发布后约 40〜50 毫秒,发布后为 10〜15 毫秒)。
v1.8.0 的性能比 v1.7.5 好得多
对比优化后的单分片案例与未优化的案例(v1.7.5),我们可以看到平均 Reconcile 时间明显下降,特别是发布后的时间成本(从 55ms 降至 16ms)。发布后的 CPU 使用率从 0.13 核降至 0.08 核。内存使用量从 676 MiB 减少到 320 MiB。
总结
综上所述,我们通过这个实验得出以下结论:
- 与 v1.7.5 相比,v1.8.0 的性能有了很大的优化。
- 部署多个分片可以横向增加系统的应用容量,并且不会引入太多的开销。
具有持续更新的大型应用程序
设置
虽然之前的负载测试主要集中在应用程序的交付上,但在生产案例中,我们看到用户不断升级带有数十个修订版本的应用程序。应用程序的持续更新是否会影响控制平面的稳定性仍然存在疑问。因此,我们进行了这个实验,在部署应用程序之后进行了几次更新。我们发现,在优化之前的 v1.7.5 版本中,对应用程序的持续更新会导致 KubeVela 控制器的内存增加。这会使控制器更快地达到最大内存使用量。例如,部署 3000 个应用程序只用了大约 700 MiB,但将所有应用程序更新一次会使内存升至 900 MiB。将所有应用程序更新两次将导致控制器内存不足并崩溃。对于 v1.6 之前的版本来说,这种情况更糟,因为默认的应用程序修订版本限制很高,并且默认情况下系统中保留了大量修订版本。解决这个问题有多方法,一种是将修订版本限制设置为较小的数字。从 v1.7.0 开始,这个数字被设置为 2,这意味着每个应用程序最多可以保存 2 个历史修订版本。KubeVela 在 v1.8.0 中实现的另一种方法是减少内存消耗,特别是在持续更新期间增加内存使用量。如上面的章节所示,我们可以注意到部署 3000 个轻量级应用程序的内存使用已大大减少。我们测试了优化后的控制器性能,证明 0.5 核 1 Gi KubeVela 控制器可以处理 3000 个大型应用程序(每个应用程序带有 3 个部署、3 个密钥和 4 个配置映射)并持续更新。在实验中,我们在 17:11 部署了 3000 个应用程序,并在大约 1 小时后完成了所有发布。(如果我们增加负载测试客户端的部署速率,可能会更快。)然后我们在 19:05、20:05、20:55、22:00、23:27 更新了所有的应用程序。请注意,每个应用程序的历史修订版本数量的默认设置为 2 。因此,在前两次更新中,会生成新的应用程序修订版本,而在接下来的三次更新中,会创建新的修订版本并回收过时的修订版本。
分析
应用程序更新比应用程序创建慢
应用程序的更新比部署需要更多的时间,因为应用程序还需要处理过时资源的垃圾回收。因此,我们观察到控制器队列的增加,但这并会不持续很长时间。应用程序的平均工作流完成时间仍然保持在 1 分钟以下。
在更新过程中 CPU 使用率很高,成为瓶颈。内存仅在前两次更新时增加
(受 ApplicationRevision 的限制)
当我们查看 KubeVela 控制器的资源使用情况时,我们注意到在更新过程中,CPU 使用率已达到 0.5 核,这是使控制器工作变慢的主要原因之一。控制器只有在应用部署和状态保持资源时才支持并行执行,但目前不支持并行垃圾回收(我们希望在未来添加)。控制器的内存使用量在第一次发布后达到了 470 MiB。在前两次更新后,它上升到 580 MiB 和 654 MiB 。然后对于以下更新,它保持在690 MiB 左右(低于内存限制的 70%),并且没有进一步连续增加。Go 的内存分配机制不会立即将未使用的内存返回给操作系统,也不是所有未使用的内存总是可以被返回。因此,内存的实际使用量(分配的内存)通常远低于常驻集大小。当 KubeVela 控制器的队列中有很高的负载和积累时,内存使用量可能会一度很高,但不会持续下去。有些人可能会问,这样的应用程序模板真的足够大?的确,我们已经看到有用户在一个应用程序中调度了 50 多个 Kubernetes 资源,这至少比我们在这里测试的应用程序的 5 倍。但平均而言,庞大的应用程序只是整个系统的一小部分,并且这里的测试将所有应用程序设置为相同的大小。此外,如果我们将这个实验与上一节中的测试(多分片中的单个分片案例)进行比较,其中,我们对 3000 个小应用程序(3 个资源)使用了相同的控制器配置,我们可以发现在这个实验中使用的应用程序大了 3 倍以上,但控制器的性能仅略有下降。
总结
总之,我们从实验中得知以下几点:
- 在标准控制器设置下,KubeVela 控制器能够容纳 3000 个大型应用程序并对其进行持续更新。
- 应用程序的更新速度比创建速度慢,它会消耗更多的计算资源。
- 与小型应用程序相比,大型应用程序对 KubeVela 控制器的性能影响不大。
多集群
设置
上述负载测试和 KubeVela 在历史上所做的所有负载测试都使用单集群架构来部署和管理应用程序。随着越来越多的用户开始在多个集群中管理应用程序,我们想要了解 KubeVela 在多集群场景下的表现。因此,我们设计了这个实验。在这个实验中,我们也使用了 KubeVela 控制器的默认配置,0.5 核 1 Gi,并使用 v1.8.0 中的集群网关的默认配置(0.5 核 200 MiB)。除了将这些资源部署到远程集群之外,我们还使用了小型应用程序模板(1 个部署,1 个配置映射和 1 个密钥)。我们进行这个实验是为了测试跨地区的性能。KubeVela 的控制平面部署在日本东京,而托管集群则部署在中国杭州。我们还比较了 v1.8.0 和 v1.7.5 的性能。v1.7.5 的集群网关默认配置使用 0.1 核 200 MiB,因此为了进行公平比较,我们将其改进为 v1.8.0 中给出的资源。
分析
v1.7.5 和 v1.8.0 都可以处理 3k 个多集群应用程序,但 v1.8.0 的性能更好
v1.8.0 控制器发送请求更少且速度更快
我们发现,在这两个版本中,KubeVela 都能够在远程集群中处理 3000 个应用程序和管理资源,但 KubeVela v1.8.0 的性能优于 v1.7.5,这要归功于我们上面提到的优化。我们发现 v1.7.5 的控制器队列保持在高状态,这是由于较长的协调时间引起的,这可能会使控制器对应用程序突变的响应变慢。除了针对集群网关的优化外,所有其他针对单个集群情况的优化技巧在多集群情况下也同样适用。
多集群请求比单个集群请求慢得多,主要由控制平面和托管集群间的延迟引起
与单集群情况下的 3000 个小型应用程序相比,在远程集群中部署资源可能会大大增加 KubeVela 控制器的时间成本。从仪表盘中可以看出,控制器的请求延迟平均约为 77 毫秒(单集群情况下为 20 毫秒),而集群网关的延迟平均约为 72 毫秒,与集群间延迟相比开销很小。
CPU 使用率和内存使用率没有差异
尽管延迟比单集群情况更大,但如果我们看 KubeVela 控制器的计算资源使用情况,会发现 CPU 使用率和内存使用率并没有太大的差异。
总结
从这个实验我们一般可以了解到以下几点:
1.在多集群场景下,v1.8.0 KubeVela 的性能表现比 v1.7.5 好。
2.在多集群场景下,KubeVela 控制器的内存消耗接近单集群情况。
3.KubeVela v1.8.0 能够默认 处理包括单集群、大型应用程序、多集群部署、持续更新在内的 3k 个应用程序。
4.如果托管集群与控制平面之间的延迟较大,则 KubeVela 控制器的性能将会更差。
优化方法:
a.通过增加 CPU、并发和 QPS/Burst,提高 KubeVela 控制器的并行性。
b.减少跨集群的延迟。(检查网络带宽并确保没有速率限制)
大规模
设置
在测试了多集群和多个分片之后,我们现在知道 KubeVela 有能力处理跨集群的大量应用程序。我们设计了一个大规模的负载测试,以验证通过适当的配置,单个 KubeVela 控制平面可以满足管理海量集群的需要。我们通过 k3d 在 64 核 256 Gi 虚拟机(阿里云上的 ECS)上模拟了 200 个托管集群。对于控制平面,我们使用了大型 ACK 集群(阿里云上的 Kubernetes),其中有 8 个( 3 个主节点和 5 个工作节点)32 核 128 Gi 节点。我们运行了 5 个分片控制器,每个分片控制器都有 8 核 32 Gi,32 个并发协调器,4000 QPS 6000 Burst。我们又运行了另外 5 个集群网关,每个都有 2 核 1 Gi。请注意,我们允许托管集群位于在同一个 VPC 中,并使用内网连接。这将减少控制平面和托管集群之间的延迟,并增加最大网络吞吐量。我们向系统部署了 40 万个小型应用程序。它们的资源被平均分配到了 200 个集群中。
控制平面上运行的应用程序和控制器数量
分析
这 40 万个应用程序的交付分为了几个阶段。首先,我们部署了 2 万个应用程序,以查看是否一切正常,并检查是否存在任何潜在瓶颈。其次,我们部署了另外 18 万个应用程序,以查看系统是否能够承载 20 万个应用程序。最后,我们将数量增加到 40 万。
随着多集群请求延迟增加,控制器表现不佳。
通过向集群网关添加更多 CPU 并消除 pod 间不平衡的工作负载来解决
当我们部署了 20 万个应用程序时,控制器的性能开始下降,你会发现在大约 2:00 时,控制器队列开始上升,平均调和时间大大增加。这是由 KubeVela 控制器的一个分片重新启动引起的,需要进一步调查。控制器本身的重启并没有影响应用程序的运行,但后来我们发现 cluster-gateway pod 的负载没有均衡分布。大量负载集中在两个 cluster-gateway pod 上,导致它们的 CPU 使用率达到 90% 以上。这大大降低了多集群请求的吞吐量,导致应用程序协调缓慢。大约在 10:00,我们注意到了该异常,并重新启动集群网关 pod 以提高它们的 CPU(4 核1 Gi * 5),这解决了暴露的瓶颈。后来我们又添加了另外 20 万个应用程序,响应延迟始终很低。发布了 40 万个应用程序后,平均调和时间(reconciliation time)为 70 毫秒,但在不同的分片间有所不同。一些分片具有较低的多集群请求延迟,例如每个请求 15 毫秒,而其他分片的延迟更高,可达 30 毫秒。原因还于集群网关的多个副本之间负载不平衡。但总的来说,如果您只有一个 cluster-gateway 副本,您将不会遇到此问题。
由于中心控制平面与托管集群之间的集群网关直接通过内网连接,多集群请求与控制平面内请求具有相似的延迟
同时值得注意的是,在这种大规模实验中,管理集群和控制平面彼此靠近,它们之间的流量传输也非常快。因此,与上面的多集群实验不同,我们可以保持与单集群情况相似的的低调谐时间(reconciliation time)。
随着应用数量的增加,内存和 CPU 的使用率增长的速度比应用数量的增长慢得多
40 万个应用程序并不是 KubeVela 控制平面可容纳的最大数量。每个控制器分片只使用了不到 20% 的 32 Gi 内存,CPU 使用率也远未满负荷。但由于在 hub Kubernetes 的 etcd 中存储了大量对象(Application、ResourceTracker、ApplicationRevision),因此 hub kube-apiserver 和其他原生组件(如 kube-controller-manager)开始使用大量计算资源,例如超过 90 Gi 的内存。系统的瓶颈逐渐落到 Kubernetes 本身。
为了进一步验证上述假设,我们将 KubeVela 控制器的内存从每个分片 8 个 核 32 Gi 缩减到每个分片的 8 核 16 Gi(总共 5 个分 片),并使用一个具有 16 核 4 Gi 的单个集群网关副本来消除工作负载的不平衡。我们将应用程序的数量增加到 50 万,发现所有分片都具有相似的性能。所有控制器分片的 CPU 和内存使用率均正常。控制器分片的工作队列为空,状态保留的平均协调时间平均约为 60 毫秒。群集网关 Pod 的负载较高,但可以将多集群请求的平均延迟保持在较低水平(23 毫秒)。
调整后所有分片的资源使用情况相似
当我们将应用程序数量从 40 万增加到 50 万时,发现对账时间没有显著增加
我们认为,40 万/50 万个应用程序的数量对于几乎所有的 KubeVela 用户已经足够大了,因此我们在这里停止了实验。
总结
总之,这个大规模实验表明,在特定条件下,KubeVela 控制平面具有扩展和容纳极大量应用程序的能力。
总结
通过一系列在不同场景下的实验,KubeVela v1.8.0 展示了其稳定性和可扩展性。与 v1.7.5 相比,其性能有相当大的提升,这为系统运维人员有信心让 KubeVela 管理大规模应用平台。现在,KubeVela 已经发展成为构建应用平台的综合解决方案。虽然进行的负载测试覆盖了 KubeVela 一些最流行的用例,但我们也看到了 KubeVela 许多更高级的用法,比如输入/输出复杂工作流、金丝雀发布、GitOps 等。根据用户使用 KubeVela 的方式,KubeVela 系统的性能可能会暴露出不同的瓶颈。我们总结了一些微不足道的解决方案,如下所示。
诊断系统性能瓶颈及解决方法
有时,整个应用系统的瓶颈并不是 KubeVela 本身。例如,如果托管集群的响应速度缓慢或吞吐量有限,我们可以提高 KubeVela 控制器的吞吐量,但控制器本身无法减少延迟。由于插件带来的系统可观测性,在大多数情况下,您可以在仪表板上找到系统性能不佳的一些线索,并应用适当的策略进行优化。未来,KubeVela 会持续关注整个系统的底层性能,并确保它始终能够为应用程序交付提供稳定、快速的功能。
您可以通过如下材料了解更多关于 KubeVela 以及 OAM 项目的细节:
- 项目代码库:https://github.com/kubevela/kubevela欢迎 Star/Watch/Fork!
- 项目官方主页与文档:kubevela.io,从 1.1 版本开始,已提供中文、英文文档,更多语言文档欢迎开发者进行翻译。
- 项目钉钉群:23310022;Slack:CNCF #kubevela Channel
相关链接:
CNCF 官网的英文博客原文地址: https://www.cncf.io/blog/2023/04/12/stability-and-scalability-assessment-of-kubevela/
[1] 参考文献
https://kubevela.net/blog/2021/08/30/kubevela-performance-test
[2] Grafana 仪表板
https://grafana.com/grafana/dashboards/18200-kubevela-system/
[3] 报告
https://kubevela.net/blog/2021/08/30/kubevela-performance-test
[4] 指南
[5] controller sharding
https://kubevela.net/docs/platform-engineers/system-operation/controller-sharding
点击此处查看 KubeVela 项目官网