开发者学堂课程【云原生实践公开课:第四步K8s集群的弹性伸缩问题】学习笔记,与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/698/detail/12269
第四步K8s集群的弹性伸缩问题
内容介绍:
一、 弹性伸缩概述
二、 调度层弹性伸缩
三、 资源层弹性伸缩
四、 典型场景介绍
一、弹性伸缩概述
1. 弹性伸缩的本质–容量与成本的博弈
- 此图为容量和成本之间的一个关系,绿色线表示的是一个K8集群里边所提供的
这个容量的一个曲线,有时,会进行扩容,有时,可能会进行缩容,红色线,是表示实际的、真实的集群里面所需要的这个容量是多少。
- 有三个虚线画的这个图第一个包表示的就是真实的资源的使用量,其实已经超
过实际集群所能够提供的容量,那么此时表示的意思就是容量不足,资源不足,那这个时候实际上是需要伸缩。
- 第二个包,表示真实的这个使用量远远低于集群所需要的这个容量,叫做资源
浪费,那此时需要进行就是缩容。
- 第三个包,可以看到绿色线是一直大大超过这个红色的这个线儿,在非常短的
一个时间之内,这个红色线突破绿线,然后有一个比较尖的一个尖锋,通常是一个激增的峰值容量,这个场景,就是所谓的这个峰值流量的这个到来,然后可能需要去做弹性伸缩。这个部分其实是大部分开发者理解,弹性伸缩最适用的一个场景。
2. 弹性伸缩的本质是一场容量和成本的博弈,对于不同角色的人员,有着不同的意义:
- 开发人员希望通过弹性伸缩使应用获得高可用的保障
- 运维人员希望通过弹性伸缩降低基础设施的管理成本
- 架构师希望通过弹性伸缩得到灵活弹性的架构应对突发的激增峰值。
- 弹性伸缩有很多不同的组件和方案,选择适合自己业务场景的弹性方案,是建
设可伸缩架构的核心因素
- 开发人员在上线应用的时候,首先要做的是做容量规划,比如说有一个应用是
一个电话本,或者在传统架构里面就是一个多副本的一个应用。那在传统架构上,可能会申请两台四核8G的ECS,把应用部署上去,在容器的场景之下,可能会生成一个两个raid卡的department。然后,分布在一个比较大的一个K8S集群之内,实现这个英文的部署。
- 当遇到这样两个峰值的时候,那实际上的应用是需要去做扩容的。传统场景之
下的开发人员,通过监控发现超过一定的这个资源使用量的时候,会进行手动的扩容,然后,当低于这个资源使用量的时候,可能会去进行手动的缩容或弹性伸缩帮助开发者解决这个问题,就是说这个手动的这个过程可以直接避免掉的,只需要配置一个HPAA policy,或者是其的这个弹性的一个策略, 就可以实现自动的这个弹性伸缩,做到无人值守的这个高科技架构的一个保障,这个是对于开发人员的一个比较大的一个优势。
- 第二个是对于运维人员,因为知道开发人员选择去扩缩容一个应用的时候,要
申请对应的这个机器,或者是要扩容对应的这个k8S集群,而在缩容的时候需要通过人工的方式去把这个节点缩回来,都是运维人员手动帮助开发人员来去实现的,之后,其实这一部分,就可以直接通过自动化的方式来解决了,可以降低整个基础设施的这个管理成本。
- 对于整个项目的这个核心人物架构师而言的,弹性伸缩带来的是更多的在架构
上的一些可能性,比如说可以通过可伸缩性架构的一些原理,去使用一些中间件,去架构更flexible的一个应用程序,这个是对于架构师的这个核心价值。
3. 弹性伸缩的架构–调度与资源解耦的两层架构
Kubernetes 的弹性伸缩架构分为调度层和资源层两个维度∶调度层弹性与资源层弹性。调度层弹性和资源层弹性通过 Pod 进行解耦。
Kubernetes 弹性伸缩组件可以从伸缩方向和伸缩对象两个维度进行解读,其中HPAA 与 cluster-autoscaler 是开发者最常组合使用的弹性伸缩组件。HPAA 负责容器的水平伸缩,cluster-autoscaler 负责节点的水平伸缩。
- 和传统弹性伸缩相比,在容器之下的弹性伸缩会略有不同。
- 那传统概念上,通常会把这个资源和调度是融在一起来讲的,例如:一个集群水
位超过多少的时候,调出一个node就可以了,然后,集群的水位低于多少的时候,缩容一个node就可以了。表面上看上去是没有错误,但在容器场景之下,真正的这个负载,实际上是pod,pod的总量不变的时候,不论是弹出多少个node,都是没有办法降低整个集群的负载状况的,这个是核心的一个问题。
- 所以在容器的场景之下,核心要解决的是两个问题,一个是在段落层开头怎么
做,核心就是怎么生出更多的pod,或者说怎么能够让pod的request和limit能够做到一个变化,这个是调研层的探索。
- 资源整合探索的是许多pod会对应申请很多的request,里面的电路水位超过一
定阈值了之后,需要在底层的这个资源层能够给一个更好的一个保障。
- 这是在K8S场景之下弹性伸缩和传统场景之下的弹性伸缩是最大的一个差别,是
一个两层结偶的一个架构,然后不同的层面可以有不同的这个软件来去实现对应的能力,那比如说这是社区里边典型的一个架构上层的,调度层面来说,主要是由HPA来组成的。
- 对HPA的解析,知道可以通过metrics-server拿一些对应的监控指标,或者说可
以走三条标准的链路来进行弹性伸缩,获取到对应的接口指标,然后判断说当前这个整个的pod利润比较高的时候,会扩容这方面的里边的副本。
- 此时,核心价值是说把整个汲取的这个调度水位冲高了,当这个pop出现了这个
无法调的这个状态,那会触发底层弹性,比如cluster-autoscaler节点水平伸缩组件,实际上是看pod什么时候出现无法调度,再根据这个pod状态来去模拟底层的伸缩组,到底从哪个深度里面弹出对应的这个节点,然后去生成这个新的开发思路,再去把这个pod调到对应的这个node上来去承载对应的应用的这个容量,或者是用的上层的流量来实现这个真实的整个负载的一个降低,那通过这种方式,K8s实现自己的这个弹性伸缩。
二、调度层弹性伸缩
1. 调度层弹性–容器水平伸缩( HPAA)
- 容器水平伸缩(HPAA)依托于 metrics server、prometheus、external
metrics adapter,通过metrics提供上层HPA弹性伸缩的判断,再去调用不同负载的
scaier 接口,来实现上层建筑Deployment业务的伸缩。
metrics 是一个监控数据的一个标准,标准化的一个获取方式。
- scaler也是上层的一个 K8S 的标准接口。
这张图是弹性伸缩的一个核心的计算公式。会从监控监控的软件里面,或者是监控的接口里面,来获取每个 pod上的这个资源的这个使用量,然后再除以对应的这个
阈值,来判断对应所需要的量,这个就是简单的HPA的一个核心的一个公式。
2. 容器水平伸缩(HPAA)是Kubernetes中最常用的弹性伸缩方式,使用时需要重点注意如下三个问题:
- Request的合理设置:首先是request要做合理的设置一定要注意usage,
usage并不是真实的使用量,而是真实的使用量除以request的比值,是usage,这就是为什么Request需要的合理设置。
- 弹性指标的合理选择:HPA 的场景之下可以有三种不同的这个监控指标来去做
伸缩判断判断,而且 HPA,在V2beta2 或者是 V2beta1的这个版本里面,支持多个审核指标同时生效。核心保障的内容是总的业务的稳定性。
- 业务状态的弹性支持:发生 HPA 的时候,可能会出现一些业务的这个数据丢
失,或者说出现一些业务的 pod超时,这些问题核心就是应用价格可能和HPA之间还没有那么匹配,这时可能会出现的一个原因就是 K8S发起了一个 signalkill。然后把这个信号量传给了对应的 container,一旦 container 接管了这个 signalkill,会直接销毁,如果业务里面补货的 signalkill 的时候,可以做一些退出之前的一个额外的处理,通过这种方式可以解决很多针对于弹性场景之下的一些异常的一些问题。所以,对于用HPA的这个场景,首先要判断这个业务是不是一个完全无状态的,还是说业务里面有一些状态,然后这个状态,就是偏原则性,如果是有这种实现的逻辑的话,尽可能去捕获一下 singlekill,然后来实现一个优雅退出,会更合适。
3. 调度层弹性–容器定时水平伸缩(CronHPAA)
- 容器定时水平伸缩( CronHPAA)是容器水平伸缩
- (HPAA)的一种补充,对于有明显的周期性负载,可以通过容器定时水平伸缩
(CronHPAA)的方式提供预弹的能力,减少流量毛刺的冲击。
- 此外容器定时水平伸缩(CronHPAA)可以和容器水平伸缩(HPAA)一起使用,从
而进一步提高应用的健壮性。
- 容器定时水平伸缩( CronHPAA),更多的是应对尖峰流量。对于很多业务来讲,
更常见的是有周期性的复杂的一个状况。
- HOA并不一定能够及时的满足上层的一个业务数据,而且很多时候HPA内层无
法做到核心的保障,会由于一些资源的生产速度问题,或者是由于底层的这个库存问题,造成Pod无法生产。
- 证券行业,游戏行业有非常明确的这个流量激增的场景,会采用类似像一些定
时的策略来去做推测,那针对这个场景,阿里云推出了一个叫做cronHPA的一个组件,可以去提供弹性场景之下的一个定时伸缩的一个策略。
- 想要通过HD来去做峰值流量的保障,又想要通过cronHPA去做到周期性的这个
弹性怎么办?
cronHPA也同时对HPA做了兼容,可以把一个HPA的对象配置在cronHPA。
然后,通过HPA发现底下的应用Deployment,再去做对应的扩充,而此时HPA的一些行为会和cronHPA做完整的一个整合,来实现一个更高可用的一个保障。
三、资源层弹性伸缩
1. 资源层弹性–节点自动伸缩( Cluster-Autoscaler)
- 资源层弹性伸缩主要有两种,传统的节点自动伸缩( Cluster-Autoscaler)和无服
务器弹性伸缩( Virtual-Node)。
- 节点自动伸缩( Cluster-Autoscaler),整体的兼容性是最高的,应用场景也是最
丰富的,缺点是弹性速度慢。
- 节点自动伸缩( Cluster-Autoscaler)原理:当有一个作业提交,或者depend出
现叛逆的pop时,会到APIserver上。APIserver会先来看当前集群里面转的容量状态,如果资源不足,无法调入状态,那会由Cluster-Autoscaler监听到对应的无法调度的一个事件,然后触发Cluster-Autoscaler弹性调度的一个判断。提供的官方文档会比较清楚的介绍这一部分的逻辑。
- 资源生产:已经判断要生成node,就要弹出对应的ECS。
- 生成ECS里面会涉及到的过程:首先需要有环境的初始化、网络初始化、各种镜
像的这个初始化,然后会变成一个K8s node,然后当不需要的时候,进行缩容。如果是完整的创建和删除的模型,可能会导致弹性伸缩速度变慢,所以阿里云也提供叫极速模式的一个伸缩计划。
- 伸缩计划的大致的一个概念,就是第一次的时候生成一个node,然后在移除的
时候,就把node停机,此时只收存储的费用,费用是低廉,到后续的时候,有扩容的这个策略,就把这个东西弹出来,然后变成一个运形态的逻辑。相当于把中间的环境初始化的过程都缓存掉了,做到比较快速的一个扩散。
2. 资源层弹性–无服务器弹性伸缩( Virtual-Node)
虚拟节点(virtual Node)是一种无服务器弹性伸缩的方案,具有弹出速度快、使用简单的特点。结合无服务器自动伸缩组组件
virtual-kubelet-autoscaler 与 Kubernetes-Elastic-Workload可以实现在传统Kubernetes集群中自动伸缩Serverless Pod的能力
- virtual Node:传统的K8S里面一个Node是有容量上限,然后这个Node上面,
会有会运行一些pod,当pod的这个调度单元超过了node的时候,就需要弹出新的node或者扩容新的node去承载无法部署的这个pod,然后实现这个新的容量的扩缩容。
- 虚拟node的概念:就是依托于一个抽象的这个概念,一个无限容量的node,只
要把对应的pop调到这个虚拟机之上,就会通过轻量级的ECI容器来去承载对应的pop,实现这个无服务器弹性的一个能力。
- 无服务器弹性伸缩( Virtual-Node)具有弹出速度快、使用简单的特点。
- 很多业务有时候需要在ECI上部署pod,又需要在这个借助于Virtual-Node去应
对一些突增的一个流量。比如,典型的一些电商的场景之下,进行快速的扩散,容量规划里面可能会有一些资源的优先会用ECS,ECS资源不足了之后,用Virtual-Node来提供一个无线的容量池,提供整体弹性伸缩。针对于这种场景,提供了无服务器架构1.0和2.0策略。
3. 无服务器伸缩架构1.0
- 无服务器伸缩架构1.0,当pod的出现资源不足,无法调动的时候,会有一个叫
做vk-autoscaler的组件,去监听无法调动的pod,然后直接介入调度的流程,把对应的pod绑定到Virtual-Node上,此时Virtual-Node就能够把这个pod调动起来。
- 绑定好pod之后,就会把对应的业务在区节点上运行起来,因为区节点也是一个
pod,区节点上层的比如接入的是一个service或者其,也是当成一个标准的判断的结果,可以无缝的把流量掌握起来。
- 弊端:
- 第一个弊端,全局生效,没有办法定向的开启或者是关闭,就是要想开vk-
autoscaler,基本上原始的节点伸缩没办法直接使用。第二个弊端,虽然可以把pod快速扩散,但是在虚拟node的上的pod没有办法优先于ECS回收。第三个弊端,无法与节点弹性伸缩混用。
弊端总结:
- 全局生效,无法定向开启或关闭
- 无法优先回收弹性资源
- 无法与节点弹性伸缩混用
4. 无服务器伸缩架构2.0
- 无服务器伸缩架构2.0,通过弹性负载去弥补了上面的这三个问题。弹性负载是
一个类似像HPA一样的CID,可以被HPA去接管,当HPA接管弹性负载之后,会把原始的应用切割成两个单位,一个叫做原始应用,一个叫做弹性单元。
- 弹性负载会去切割原始应用上的这个副本的数目和弹性单元上面的这个副本数
目,去进行优先级的扩缩容。
四、典型场景介绍
典型场景介绍
- 在线业务
存在资源使用率的波峰波谷的场景,可以通过弹性伸缩节省资源。存在突增流量毛刺的场景,可以通过弹性伸缩提高健壮性。
典型例子:网站、API服务、机器学习推理、大数据流式处理【Flink】等。
- 离线计算
离线业务通常规模会比较大,可以通过弹性伸缩的方式获取海量算力,并且容器化的交付方式可以降低管理成本。
典型例子: Spark、机器学习训练、多媒体转码等。
- 定时任务
定时任务通常运行频度不高,因次整体的资源需求是在容量规划之外的,可以通过弹性伸缩的方式降低容量管理的复杂度。
典型例子:离线定时ETL、数据分析与报表等。
- 特殊场景
某些业务对于稳定性有极高的要求,通过配置弹性伸缩的方式来保证更高的稳定性。
典型例子:异地多活弹性、混合云弹性等。
通过两个调度层弹性的例子来给回顾一下HPA和CronHPA。
- HPA和CronHPA比较:CronHPA的yaml,除了apiversion 和kind不太一样,其
地方比较类似。job分成两个部分,schedule和targetSize,targetSize是修改CronHPA的数目,schedule里面是一个类似像状态的一个策略,第一个字段是秒,第二个字段是分,第三个是小时,第四是天,下一个是月,下一个是周,然后这是标志CronHPA的一个结构,就是多了一个秒,一个字段,可以达到一个更精确的伸缩的一个策略。
- 例子里面比较简单就是,每一分钟的第一秒,缩容每一分钟的第30秒,去做一
个扩容。那在使用CronHPA之前,可以看到,这个地方现在用的是Deployment去做伸缩,没有用刚才提到的HPA去做伸缩。会带来什么问题?
- 输入
workshop vim cronhpa .yaml
workshop kubectl apply -f cronhpa . yaml
.
workshop kubectl get cronhpa.
workshop watch -n 1 kubectl describe cronhpa cronhpa-sample.
workshop kubectl describe cronhpa cronhpa-sample.
- 可以看到,此时的时候,上面这个已经任务已经到达succeed ,这个任务是
submitted的状态。 message是说当前currentreplicas: 1,desired replicas: 3。
- 如果get Deployment,可以看到,此时这个Deployment这个副本已经从一个
副本改成了三个副本,会触发底层的这个pod的增加。
- 可以看到,这三个部分已经都运行起来,可以看到对应的这个状态。
- 这个时候立马又缩回成一个副本,那这个时候,其实是由CronHPA接管了整个
HPA的一个状态。
- 那此时,这个业务上面还有一个HPA在运行,输入kubectl describe hpa nginx-
deployment-basic-hpa,可以看到,因为这个news3并不是由HPA触发,而是由CronHPA触发,此时的HPA发现有三个副本,资源力度是零,正常来讲,HPA会在缩容阈值到达了之后,去触发整体的这个作用,那此时是冷却的一个状态,如果刚才把定时申诉的策略时间,设置不是在一分钟之内去完成整个收缩的话,那会发现当前的这个HPA的状态,实际上是扩容了之后,立马被缩容。解决这个问题的方法,就是HPA兼容的那个方法,就是可以通过获取一个HPA对象,然后把同样的这个任务再下载一次。
- 可以看到这个时候还是可以执行成功的,只是说在这个策略上面,已经把的这
个HPA的这个内容拷进来了,这个地方,有一个明显不同的一个策略,Skip scale replicas because HPA nginx-deployment-basic-hpa current replicas:3 >= desired replicas:1,因为刚才的时候把这个副本数从一个副本扩到三个副本。也就是说HPA当前维持的副本状态是三个部分,那接下来,如果此时触发缩容,希望从三个副本变成一个副本的时候,CronHPA这边是由三个副本变成一个副本,但是HPA那边如果还是维持三个副本的话,可能会出现震荡。
- 建议,发现HPA那边的分数比CronHPA高的时候,那其实就的这个基本全交给
HPA。HPA数值比CronHPA低的时候,去接管对应的这个策略,然后并且调整HPA的一个命值,通过这种方式,可以达到一个更稳定的一个弹性。
- 此时的department,即便刚才下发了缩容的任务,但是由于稳定性的原因,
department仍旧保持着3,所以保证了当前使用CronHPA可以和HPA进行一个比较好的一个兼容。