Kubernetes的垂直和水平扩缩容的性能评估

译自:Performance evaluation of the autoscaling strategies vertical and horizontal using Kubernetes

可扩展的应用可能会采用水平或垂直扩缩容来动态调整云端资源。为了帮助选择最佳策略,本文主要对比了kubernetes中的水平和垂直扩缩容。通过对 Web 应用程序进行综合负载测量实验,结果表明水平扩缩容的效率更高,对负载变化的响应更快,且对应用程序响应时间的影响更小。

简介

云服务的负载可能会随时间变动,为了实现可扩展,需要依据特定的指标(如CPU)来采取自动扩容策略,以此来扩大应用的处理能力。为此,我们需要均衡应用的QoS和云基础设施的开销,即量入为出。

当前有两种扩缩容类型:水平,即服务的数目会视负载的情况增加或减少;垂直,即服务的资源(CPU或内存)会视负载的情况增加或减少。但即使有了这两种方法,也没有明确定义的标准来决定使用哪种方法。此外,在性能和成本效益方面,还缺乏与垂直自动扩缩容相关的分析,以及如何与水平自动扩缩容进行比较。

因此,为了评估这两种方法的性能,我们使用kubernetes做了一个测量实验,并借助了一个压测工具,该工具可以以一种受控的方式向一个"busy-wait"应用发送请求,并根据负载发生变化后自动扩缩容决策的时间、每个决策上请求的 CPU 的容量以及应用响应时间的影响来对这些机制进行评估。

Kubernetes的自动扩缩容策略

k8s是一个基于Borg的开源项目,聚焦容器编排,并允许在集群中运行容器应用,同时简化了不同环境(生产、开发等)的配置。总之,k8s提供了一组物理和虚拟机(节点),其中,master负责控制和给worker节点分配任务。在k8s中,pod是节点上最小的可分配单元,一个pod可以打包一个或多个容器,并定义执行规则。需要注意的是,要确保节点能够有足够的资源去运行对应的pod。

为了在k8s中创建一个对象,需要创建一个包含所需规格的配置文件。K8s的对象可以用于不同的目的,如监控、网络配置、扩缩容等。因此,需要根据不同的目的来选择不同的类型。此处使用的类型是:

  • horizontal pod autoscaler
  • vertical pod autoscaler

Horizontal Pod Autoscaler

水平自动扩缩容的目的是降低或增加集群中的Pods数目,以便有效地利用资源并满足应用的需求。这种扩缩容方式围绕某些指标,如CPU、内存、自定义指标或外部指标(基于Kubernetes外部的应用负载)。[2] [3]

为了使用水平扩缩容,需要创建一个HorizontalPodAutoscaler配置文件,并定义一个CPU百分比使用限制,如果Pod的利用率达到该限制,则会创建出更多的副本。HPA每15s(可变)会校验是否需要创建新的Pods。

HPA 背后的算法基于 HPA 所watch的所有Pods的当前利用率的平均值(Uₐ),期望利用率(U),以及当前副本数量(Uₐ),因此可以根据如下格式进行计算:

\[N_d=N_a*(U_a/U_d)
\]

为了更好地理解上述格式,我们假设如下场景:

  1. 一个集群中有5个副本(Nₐ = 5),平均利用率为限制100 milicores或0.1 CPU-core(U = 100)。
  2. 在一个负载峰值之后,所有pods的平均利用率上升到200m(U = 200)。
  3. 应用公式,得到N = 5 * (200 / 100) = 10,其中N = 10,就是在保证平均利用率为100m且兼顾到阈值的理想Pods数。

通过以上例子,可以看到HPA会将副本数翻倍,而不是每次仅创建一个副本,这种方式使得HPA非常精准。

HPA有一个默认的延迟(5分钟),在负载降低时进行缩容。该时间仅在利用率低于定义的利用率限制时才会开始计算。

Vertical Pod Autoscaler

垂直扩缩容的目的是增加或降低现有Pods分配的资源(CPU或内存)。在Kubernetes中,它会修改Pod请求的资源容量。[4]

为了使用这种方式,需要创建一个VerticalPodAutoscaler类型的对象,并指定需要自动扩缩容的deployment。这种方式包含3个主要的组件:

  • Updater:充当哨兵,校验Pods是否有做够的资源,否则会使用期望的资源来重启这些Pods。
  • Admission controller:和updater配合,定义合适的pod request资源容量。
  • Recommender:watch资源,基于过去或当前利用率,提供建议来扩大或缩小内存或CPU。

当前VPA提供了3种类型的Recommender

  1. Target:推荐理想的内存和CPU容量
  2. Upper bound:推荐request资源的上限值,如果request大于此限值,考虑到置信因子,将会缩小Pod的规模
  3. Lower bound:推荐request资源的下限值,如果request低于此限制,考虑到置信因子,将会扩大Pod的规模

置信因子是一种使VPA 在自动扩缩容决策上更加保守的一种方法。这种方式会用到如下变量:当前Pod request CPU(Rₐ),下限(Bₗ)及其置信因子 (aₗ),和上限 (Bᵤ)及其置信因子(aᵤ)。

Rₐ > (Bᵤ * aᵤ)时,VPA会减少资源规模,其中置信因子aᵤ会随着 Pod 启动时间的增加而增加,并缓慢收敛到1。上限的置信因子的计算式为aᵤ = (1 + 1/Δₜ),其中Δₜ是Pod创建以来的天数。

另一方面,当Rₐ < (Bₗ * aₗ)时VPA会增加资源规模,其中置信因子aₗ会随着 Pod 启动时间的增加而增加,并缓慢收敛到1。下限的置信因子的计算式为aₗ = (1 + 0.001/Δₜ)^-2。这样,通过置信因子,VPA可以快速做出决策。

为了更好地理解,假设一个pod当前的request CPU为Rₐ = 100,当前下限为Bₗ = 150,启动以来的时间为5分钟,将其转换为天,得到Δₜ = 5 /60/24 = 0.003472。下限的置信因子为aₗ = (1 + 0.001/0.00347)^-2 = 0.6,因此,可以看到100 < 150 * 0.6 ⇒ 100 < 90,结论为false,此时不会增加Pod的容量。为了重新创建Pod,置信因子最少应该为aₗ = 0.67,换句话说,大约需要7分钟才会重建。

验证环境

为了生成并分析实验结果,需要创建一个测试环境,并定义某种方式来生成资源利用率来触发自动扩缩容策略,所有实验都实现了自动化,并保存和组织实验数据。环境的架构和组件如下图所示:

  • Eventos: 事件
  • Aplicação: 应用
  • Legenda: 副标题
  • Green: 分配负载
  • Blue: 采集集群信息
  • Red: 日志存储

容器编排环境使用的是Minikube,生成负载采用的工具是Hey Benchmark Tool,它是使用Go编写的压测工具,能够并发大量请求,此外,它还包含所有所需的参数:

  • 定义了请求执行的时长
  • 定义了并行的workers数目
  • 定义了worker发送的请求速率

为了在Minikube中生成负载,我们开发了一个node.js web应用,该应用会暴露一个REST,其会调用一个busy-wait 函数,使服务在一定毫秒时间段内的CPU-core的利用率达到100%,从下图中可以看出,该函数接收一个服务时间,并在时间结束前让CPU保持繁忙。

评估场景

考虑到垂直扩缩容至少需要一个监控的Pod,因此为了保持配置相似,需要为每个扩缩容策略配置2个初始Pods。此外,每个Pod初始request的CPU为0.15 CPU-cores,限制为1.5CPU-cores。

在所有评估的场景中,服务时间(endpoint处理一个请求的时间)为常数S = 0.175 秒。负载强度受发送的请求速率(λ)以及并发的客户端(每秒发送一个请求)控制。实验的每个场景分为9个阶段,每个阶段包含不同的负载,每个阶段的执行时间为2分钟,每种场景的总执行时间为18分钟。

为了让每个阶段达到期望的CPU利用率,根据队列理论的运算规律定义了请求速率。根据利用率规律,流量强度定义为ρ = λ ∗ S。例如,达到2 cores利用率(ρ = 2)的服务时间为S = 0.1, 此时每秒请求速率为λ = ρ / S = 2 / 0.1 = 20。但如果该请求速率超过40,那么等式不再平衡,因为此时的负载的确需要4个cores。[5]

实验的阶段流程

如上图所示,请求速率为λ = 2(需要ρ = 0.35 CPU-cores);λ = 4 (需要 ρ = 0.7 CPU-cores); λ = 6 (需要 ρ = 1.05 CPU-cores); 和 λ = 8 (需要 ρ = 1.4 CPU-cores),因此,这些情景假设综合了以下几点:

  1. 第一种场景中λ = [2, 2, 4, 6, 8, 6, 4, 2, 2],以一种非激进的方式逐步增加或减少负载
  2. 第二种场景中λ = [2, 2, 8, 8, 8, 2, 2, 2, 2],突然增加负载,并保持3个阶段,然后在剩余的阶段中降低降到最低值
  3. 第三种场景中λ = [2, 2, 8, 8, 2, 2, 2, 2, 2],与第二种类似,但高负载持续时间更少
  4. 第四种场景中λ = [2, 2, 8, 2, 8, 2, 8, 2, 2],使用多个峰值负载

当定义好这些场景之后,就可以使用脚本自动化执行。

在实验执行过程中,Kubernetes API会提供评估所需的关键数据:1)CPU使用情况;2)autoscaler推荐值;3)Pod Request的CPU数。每10秒对这些数据进行一次检索,并保存到日志文件中。因此,使用这些信息,可以判断每个Pod request的CPU随时间的变化情况。

同时,每次执行脚本生成负载(使用Hey工具)时,也会将应用的指标保存到日志文件中,为测试提供应用的行为数据。

结论

每种自动扩缩容策略下都会执行者四种实验场景。每种方式的初始Pods数为2,每个Pod的CPU-core为0.15,并会随时间被扩缩容器所修改。图1和图2展示了实验过程中每个Pod的request CPU。虚线表示在负载的每个阶段达到100% 利用率所需的 CPU 容量。

图1:垂直扩缩容中每个Pod request的CPU

可以看到,在VPA中,重新分配资源是有延迟的,大部分时间停留在 CPU 容量低于所需的情况下(虚线下面的彩色条)。场景1的负载是逐步增加的,其自动扩缩容决策的延迟相对要大,而场景2、3的负载变化比较突然,其延迟也相对较低。场景4的负载峰值较短,只有在阶段8才出现了资源的申请,此外还可以看到,在进行扩容时,VPA request的CPU要大于所需的CPU,在缩容时,VPA也更加保守。

此外,即使在最后5个低强度的负载的阶段中,VPA也没有进行缩容,此时申请的资源要大于所需的资源。这种延迟背后的原因是出于该机制的置信因子,它需要更多的时间来提升推荐的可信度。此外,在某些时候出现3个Pod的原因是,在调整Pod时,VPA会使用期望的资源容量来创建一个新的Pod,并在新的Pod就绪之后结束掉老的Pod。因此,置信因子可以多次减少重建 Pods 带来的开销。

图2:水平扩缩容中每个Pod request的CPU

大部分情况下,HPA都能对工作负载的变化作出有效的反应(尽管请求的 CPU 略高于所需的 CPU)。当负载上升时,其平均扩容决策时间为40秒。

只有在所有场景的第3阶段,以及在场景1的第4和第5阶段中,CPU停留在所需值以下的时间持续了大约1分钟。

HPA能够在5分钟的延迟后进行缩容,而VPA则不会缩容。在场景4中,HPA超量request 了CPU资源,这对于处理短时间的峰值来说这是正向的,但长远来看,有可能会给基础设施成本带来一定影响。

图3:垂直和水平扩缩容下的应用响应时间

图3展示比较了每个场景下的负载阶段对 Web 应用程序所做请求的响应时间。每个框的中间线代表中间值,而点和三角形是每个阶段响应时间的平均值。

在所有场景下下,水平自动扩缩容展示的响应时间非常接近于服务时间(0.175秒),在负载量增加的几个阶段中,只有平均值和第三四分位数略大。另一方面,在各种阶段中,由于调整Pod存在延迟,垂直自动扩缩容展示的响应时间要远大于服务时间(无论平均值和四分位数)。

可以这么说,在使用默认配置对这两种自动扩缩容策略进行评估的过程中表明,HPA是更有效的,它可以更快响应负载的变化,并且有足够数量的 Pods 来处理请求,而 VPA 受到了调整 Pods延迟的负面影响。

总结

本次工作通过测量实验分析了Kubernetes中水平和垂直自动扩缩容的性能。为此,需要某种方式来生成负载并使用压测工具控制负载,以及创建多个场景来分析自动扩缩容方式的行为,主要关注响应时间、Pods的CPU request指标,以及自动扩容时间时间的时间。

从本次的实验中可以看到,水平自动扩缩容相对不保守,但对资源的调整也相对更高效。需要注意的是,这种精度是由水平pod自动扩缩容器算法的客观性决定的,该算法将请求的资源保持在已定义的资源使用限制的平均值内。

相比之下,垂直自动扩缩容在资源申请决策上则更加保守,因为它依赖于随时间增加置信因子的对数。可以得出,在较长时间的实验中,可以生成更多的pod执行的历史数据,垂直自动扩缩容将更有效地执行自动扩缩容决策。

在本次的实验参数和场景下,水平自动扩缩容展现了更高的效率,其决策的精确性提供了资源的灵活性,以及更快的 Web 应用响应时间。需要注意的是,在本次时间结束之时,垂直自动扩缩容还处理beta阶段,仍然会接受日常更新,因此未来有可能会在效率上有所提升。此外,本次实验使用了Kubernetes的默认配置,因此修改参数可能会产生不同的结果。

[1] Borg: The Predecessor to Kubernetes: https://kubernetes.io/blog/2015/04/borg-predecessor-to-kubernetes/

[2] Gandhi, A., Dube, P., Karve, A. et al. Model-driven optimal resource scaling in cloud. Softw Syst Model 17, 509–526 (2018). https://doi.org/10.1007/s10270-017-0584-y

[3] Horizontal Pod Autoscaler: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/

[4] C. Liu, M. Shie, Y. Lee, Y. Lin, and K. Lai. Vertical/horizontal resource scaling mechanism for federated clouds. In 2014 International Conference on Information Science Applications (ICISA), pages 1–4, 2014. doi: 10.1109/ICISA.2014.6847479

[5] Daniel A. Menasce, Lawrence W. Dowdy, and Virgilio A. F. Almeida. Performance by Design: Computer Capacity Planning By Example. Prentice Hall PTR, USA, 2004. ISBN 0130906735.

Kubernetes的垂直和水平扩缩容的性能评估的更多相关文章

  1. 从零入门 Serverless | Serverless Kubernetes 应用部署及扩缩容

    作者 | 邓青琳(轻零) 阿里云技术专家 导读:本文分为三个部分,首先给大家演示 Serverless Kubernetes 集群的创建和业务应用的部署,其次介绍 Serverless Kuberne ...

  2. Kubernetes 监控:Prometheus Adpater =》自定义指标扩缩容

    使用 Kubernetes 进行容器编排的主要优点之一是,它可以非常轻松地对我们的应用程序进行水平扩展.Pod 水平自动缩放(HPA)可以根据 CPU 和内存使用量来扩展应用,前面讲解的 HPA 章节 ...

  3. Airbnb的动态kubernetes集群扩缩容

    Airbnb的动态kubernetes集群扩缩容 本文介绍了Airbnb的集群扩缩容的演化历史,以及当前是如何通过Cluster Autoscaler 实现自定义扩展器的.最重要的经验就是Airbnb ...

  4. 三十三、HPA实现自动扩缩容

    通过HPA实现业务应用的动态扩缩容 HPA控制器介绍 当系统资源过高的时候,我们可以使用如下命令来实现 Pod 的扩缩容功能 $ kubectl -n luffy scale deployment m ...

  5. 如何根据不同业务场景调节 HPA 扩缩容灵敏度

    背景 在 K8s 1.18 之前,HPA 扩容是无法调整灵敏度的: 对于缩容,由 kube-controller-manager 的 --horizontal-pod-autoscaler-downs ...

  6. 通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容

    上一篇我们讲到了dapr提供的bindings,通过绑定可以让我们的程序轻装上阵,在极端情况下几乎不需要集成任何sdk,仅需要通过httpclient+text.json即可完成对外部组件的调用,这样 ...

  7. Docker Swarm(七)Scale 扩(缩)容服务

    扩(缩)容服务 扩容服务 Service还提供了复制(类似kubernetes里的副本)功能.可以通过 docker service scale 命令来设置服务中容器的副本数: docker serv ...

  8. 【kubevirt】VirtualMachineInstanceReplicaSet(vmis)-扩缩容-弹性伸缩

    @ 目录 概述/理解 使用场景 创建vmis 扩缩容 弹性伸缩 方法1 方法2 概述/理解 VirtualMachineInstanceReplicaSet(vmis)确保指定数量的 VirtualM ...

  9. 构建Docker平台【第四篇】创建服务及扩缩容等操作

    第一步:创建服务 1. 配置 nginx 的 yaml 文件 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-ng ...

  10. Knative 基本功能深入剖析:Knative Serving 自动扩缩容 Autoscaler

    Knative Serving 默认情况下,提供了开箱即用的快速.基于请求的自动扩缩容功能 - Knative Pod Autoscaler(KPA).下面带你体验如何在 Knative 中玩转 Au ...

随机推荐

  1. UVA12186 工人的请愿书 Another Crisis (树形DP)

    dp[i]表示要让i向上级发请愿书,最少需要多少个工人递交请愿书,因为要取前T%最小的,所以还要将i的子节点排序(这里用vector实现),取前c个最小的作为dp[i]的值. 这里用dfs可以省去dp ...

  2. 第一个Spring Boot的MVC程序

    最近在学习Spring Boot,记录一下学习过程!!!! Spring Boot中的MVC:M(model模型),C(controller控制器),V(view视图) model:是Java的实体B ...

  3. 如何编写 Pipeline 脚本

    前言 Pipeline 编写较为麻烦,为此,DataKit 中内置了简单的调试工具,用以辅助大家来编写 Pipeline 脚本. 调试 grok 和 pipeline 指定 pipeline 脚本名称 ...

  4. 个人数据保全计划:(1) NAS开箱

    前言 从几年前第一个硬盘故障导致参赛的文件丢失之后,我就开始意识到数据安全的重要性,开始用各种云盘做备份,当时还不是百度云一家独大,我们也都没意识到网盘备份是极其不靠谱的行为,直到因为某些不可抗力因素 ...

  5. Springboot+Vue实现将图片和表单一起提交到后端,同时将图片地址保存到数据库、再次将存储的图片展示到前端vue页面

    文章目录 1.实现的效果 2.Vue前端 3.图片上传 4.字段变量根据自己的字段名自行设置(这里不给出了,哈哈哈) 5.method方法 5.1.图片显示在选择框中,同时返回后端存储的地址 5.2查 ...

  6. 了解 Flutter 开发者们的 IDE 使用情况

    作者 / JaYoung Lee, UX Researcher at Google Google 的 Flutter 团队负责构建和维护 Android Studio (基于 IntelliJ-IDE ...

  7. vue-axios 输入参数获取数据的写法

    <template> <div class="nav"> <input v-model="name" type="tex ...

  8. JS常见问题总结

    1. 什么是 JavaScript ? JavaScript 是一种具有面向对象的.解释型的.基于对象和事件驱动的.跨平台的.弱类型的程序设计语言 2. JavaScript 与 ECMAScript ...

  9. 【Java复健指南09】项目练习全解--房屋出租系统

    一个基于文本界面的综合练习,主要用于串联和回忆知识点,比较简单 各个界面的设计样式 主菜单 =============房屋出租系统菜单============ 1 新 增 房 源 2 查 找 房 屋 ...

  10. 嵌入式-C语言基础:指针数组(和数组指针区分开来)

    指针数组:一个数组,若其元素均为指针类型的数据,称为指针数组,指针数组存放的是指针类型的数据,也就是指针数组的每个元素都存放一个地址.下面定义一个指针数组: int * p[4];//[]的优先级是比 ...