Kubernetes的HPA原理详解
1. HPA解决的问题
HPA全称是 Horizontal Pod Autoscaler,也就是对k8s的workload的副本数进⾏⾃动⽔平扩缩容(scale)机制,也是k8s⾥使⽤需求最⼴泛的⼀种Autoscaler机制,在开始详细介绍HPA之前,先简单梳理下k8s autoscale的整个⼤背景。
k8s被誉为新⼀代数据中⼼操作系统(DCOS),说到操作系统我们⾃然想到其定义:管理计算机的软硬件资源的系统,k8s也⼀样其核⼼⼯作也是管理整个集的计算资源,并按需合理分配给系统⾥的程序(以Pod为基础的各种workload)。
其本质是解决资源与业务负载之间供需平衡的问题,随着业务需求和部署规模的增⼤,k8s集就要相应扩容计算资源,集扩容的最直接的办法是新增资源,⼀般单机器很难垂直扩展(k8s node也不⽀持),所以⼀般都是直接增加节点。但是随着机器的不断增加成本也不断加⼤,⽽实际上⼤量服务⼤部分时间负载很低导致机器的整体使⽤率很低,⼀⽅⾯业务为了应对每⽇随机流量⾼峰会把副本数尽量扩得很⾼,另⼀⽅⾯业务⽅并不能准确评估服务实际需要的CPU等资源,也出现⼤量浪费。
为了解决业务服务负载时刻存在的巨⼤波动和资源实际使⽤与预估之间差距,就有了针对业务本⾝的“扩缩容”解决⽅案: Horizontal Pod Autoscaler(HPA)和 Vertical Pod Autoscaler(VPA)。
为了充分利⽤集现有资源优化成本,当⼀个资源占⽤已经很⼤的业务需要扩容时,其实可以先尝试优化业务负载⾃⾝的资源需求配置(request与实际的差距),只有当集的资源池确实已经⽆法满⾜负载的实际的资源需求时,再调整资源池的总量保证资源的可⽤性,这样可以将资源⽤到极致。
所以总的来说弹性伸缩应该包括:
1. Cluster-Autoscale: 集容量(node数量)⾃动伸缩,跟⾃动化部署相关的,依赖iaas的弹性伸缩,主要⽤于虚拟机容器集
2. Vertical Pod Autoscaler: ⼯作负载Pod垂直(资源配置)⾃动伸缩,如⾃动计算或调整deployment的Pod模板limit/request,依
赖业务历史负载指标
3. Horizontal-Pod-Autoscaler: ⼯作负载Pod⽔平⾃动伸缩,如⾃动scale deployment的replicas,依赖业务实时负载指标
其中VPA和HPA都是从业务负载⾓度从发的优化,VPA是解决资源配额(Pod的CPU、内存的limit/request)评估不准的问题,HPA则要解决的是业务负载压⼒波动很⼤,需要⼈⼯根据监控报警来不断调整副本数的问题,有了HPA后,被关联上HPA的deployment,后续副本数修改就不⽤⼈⼯管理,
HPA controller将会根据业务忙闲情况⾃动帮你动态调整。当然还有⼀种固定策略的特殊HPA: cronHPA,也就是直接按照cron的格式设定扩容和缩容时间及对应副本数,这种不需要动态识别业务繁忙度属于静态HPA,适⽤于业务流量变化有固定时间周期规律的情况,这种⽐较简单可以算做HPA的⼀种简单特例。
2. 原理架构
既然是⾃动根据业务忙闲来调整业务⼯作负载的副本数,其实HPA的实现思路很容易想到:通过监控业务繁忙情况,在业务忙时,就要对workload扩容副本数;等到业务闲下来时,⾃然⼜要把副本数再缩下去。所以实现⽔平扩缩容的关键就在于:
如何识别业务的忙闲程度
使⽤什么样的副本调整策略
kubernetes提供了⼀种(整个HPA及metrics架构如下图所⽰),HPA controller通过这个统⼀metrics接⼝可以查询到任意⼀个HPA对象关联的deployment业务的繁忙指标metrics数据,不同的业务的繁忙指标均可以⾃定义,只需要在对应的HPA⾥定义关联deployment 对应的metrics即可。
标准的metrics查询接⼝有了,还需要实现metrics API的服务端,并提供各种metrics数据,我们知道k8
s的所有核⼼组件之间都是通过apiserver进⾏通信,所以作为k8s API的扩展,metrics APIserver⾃然选择了基于API Aggregation聚合层,这样HPA controller的metrics查询请求就⾃动通过apiserver的聚合层转发到后端真实的metrics API的服务端(对应下图的Promesheus adapter和Metrics server)。
最早的metrics数据是由metrics-server提供的,只⽀持CPU和内存的使⽤指标,metrics-serve通过将各node端kubelet提供的metrics 接⼝采集到的数据汇总到本地,因为metrics-server是没有持久模块的,数据全在内存中所以也没有保留历史数据,只提供当前最新采集的数据查询,这个版本的metrics对应HPA的版本是autoscaling/v1(HPA v1只⽀持CPU指标)。
后来为了适应更灵活的需求,metrics API开始扩展⽀持⽤户⾃定义metrics指标(custom metrics),⾃定义数据则需要⽤户⾃⾏开发custom metrics server,社区有提供专门的custom adpater框架 ,该框架定义了Custom和External的MetricsProvider接⼝(如下所⽰),需要⾃⾏实现对应的接⼝。
type MetricsProvider interface {
CustomMetricsProvider
ExternalMetricsProvider
}
type CustomMetricsProvider interface {
// GetMetricByName fetches a particular metric for a particular object.
// The namespace will be empty if the metric is root-scoped.
GetMetricByName(name types.NamespacedName, info CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValue, error)
// GetMetricBySelector fetches a particular metric for a set of objects matching
// the given label selector.  The namespace will be empty if the metric is root-scoped.
GetMetricBySelector(namespace string, selector labels.Selector, info CustomMetricInfo, metricSelector labels.Selector) (*custom_metrics.MetricValue List, error)
// ListAllMetrics provides a list of all available metrics at
// the current time.  Note that this is not allowed to return
// an error, so it is reccomended that implementors cache and
group by的用法及原理详解// periodically update this list, instead of querying every time.
ListAllMetrics() []CustomMetricInfo
}
type ExternalMetricsProvider interface {
GetExternalMetric(namespace string, metricSelector labels.Selector, info ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error)
ListAllExternalMetrics() []ExternalMetricInfo
}
⽬前社区已经有⼈基于promethus server的监控数据源,实现看⼀个来提供 custom metrics server服务,如果需要的⾃定义指标数据已经在promesheus⾥有了,可以直接对接使⽤,否则要先把⾃定义的指标数据注⼊到promesheus server⾥才⾏。因为HPA的负载⼀般来源于监控数据,⽽promesheus⼜
是CNCF标准的监控服务,所以这个promesheus adapter基本也可以满⾜我们所有⾃定义metrics的HPA的扩展需求。
讲清楚了metrics,也就解决了识别业务的忙闲程度的问题,那么HPA Controller是怎么利⽤metrics数据进⾏扩缩容控制的呢,也就是使⽤什么样的副本调整机制呢?
如上图右边所⽰,⽤户需要在HPA⾥设置的metrics类型和期望的⽬标metrics数值,HPA Controller会定期(horizontal-pod-autoscaler-sync-period配置,默认15s)reconcile每个HPA对像,reconcile⾥⾯⼜通过metrics的API获取该HPA的metrics实时最新数值(在当前副本数服务情况下),并将它与⽬标期望值⽐较,⾸先根据⽐较的⼤⼩结果确定是要扩缩容⽅向:扩容、缩容还是不变,若不需要要进⾏扩缩容调整就直接返回当前副本数,否则才使⽤HPA metrics ⽬标类型对应的算法来计算出deployment的⽬标副本数,最后调⽤deployment的scale接⼝调整当前副本数,最终实现尽可能将deployment下的每个pod的最终metrics指标(平均值)基本维持到⽤户期望的⽔平。注意HPA的⽬标metrics是⼀个确定值,⽽不是⼀个范围。
3. HPA的metrics的分类
要⽀持最新的custom(包括external)的metrics,也需要使⽤新版本的HPA:autoscaling/v2beta1,⾥⾯增加四种类型的Metrics:Resource、Pods、Object、External,每种资源对应不同的场景,下⾯
分别说明:
Resource⽀持k8s⾥Pod的所有系统资源(包括cpu、memory等),但是⼀般只会⽤cpu,memory因为不太敏感⽽且跟语⾔相关:⼤多数语⾔都有内存池及内置GC机制导致进程内存监控不准确。
Pods类型的metrics表⽰cpu,memory等系统资源之外且是由Pod⾃⾝提供的⾃定义metrics数据,⽐如⽤户可以在web服务的pod ⾥提供⼀个promesheus metrics的⾃定义接⼝,⾥⾯暴露了本pod的实时QPS监控指标,这种情况下就应该在HPA⾥直接使⽤Pods 类型的metrics。
Object类型的metrics表⽰监控指标不是由Pod本⾝的服务提供,但是可以通过k8s的其他资源Object提供metrics查询,⽐如ingress 等,⼀般Object是需要汇聚关联的Deployment下的所有的pods总的指标。
External类型的metrics也属于⾃定义指标,与Pods和Object不同的是,其监控指标的来源跟k8s本⾝⽆关,metrics的数据完全取⾃外部的系统。
在HPA最新的版本 autoscaling/v2beta2 中⼜对metrics的配置和HPA扩缩容的策略做了完善,特别是对 metrics 数据的⽬标指标值的类型定义更通⽤灵活:包括AverageUtilization、AverageValue和Value,但是不是所有的类型的Metrics都⽀持三种⽬标值的,具体对应关系如下表。
HPA⾥的各种类型的Metrics和Metrics Target Type的对应⽀持关系表
Metrics Type \ Target Type AverageUtilization AverageValue Value备注(query metrics)Resource(pod’s cpu/memory etc.)Yes Yes No pods metrics list Pods(pod’s other metrics)No Yes No pods metrics list Object(k8s object)No Yes Yes object metrics External(not k8s object)No Yes Yes external metrics list 4. HPA的使⽤说明
先看个最简单的HPA的定义的例⼦
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
从上⾯的例⼦可以看出,HPA的spec定义由三个必填部分组成:
1. HPA控制的⽬标workload,即scaleTargetRef,理论上HPA可以对任意⽀持scale⼦接⼝( sub-resource )的workload做弹性伸
缩,不过statefulset⼀般代表有状态服务,副本不可随便修改,⽽Job⼀般代表短⽣命周期的,所以基本可以认为HPA⽬前是专门控制deployment的扩缩容的(不建议直接控制RS,否则将⽆法滚动升级)。
2. 弹性扩缩容的上下边界,minReplicas和maxReplicas,也就是说HPA的扩缩容也不能是漫⽆边际,如果计算出的副本数超过max则
统⼀取maxReplicas,maxReplicas是为了保护k8s集的资源被耗尽,minReplicas则相反,⽽且minReplicas必须不⼤于
maxReplicas,但是也要⼤于0(k8s v1.16之后才放开允许Objetct和External类型的metrics的minReplicas为0,需要apiserver 开启–feature-gates mapStringBool ),两者相等就相当于关闭了⾃动伸缩功能了,总的来说minReplicas和maxReplicas边界机制避免metrics数据异常导致的副本数不受
控,特别是HPA在k8s最新的v1.18版本也依然是alpha特性,强烈建议⼤家谨慎设置这两个边界。
3. metrics指标类型和⽬标值,在autoscaling/v1⾥只有targetCPUUtilizationPercentage,autoscaling/v2beta1开始就扩展为
metrics数组了,也就是说⼀个HPA可以同时设置多个类型维度的metrics⽬标指标,如果有多个HPA 将会依次考量各个指标,然后最终HPA Controller选择⼀个会选择扩缩幅度最⼤的那个为最终扩容副本数。在最新的autoscaling/v2beta2版本的HPA
中,metrics type共有4种类型:Resource、Pods、Object、External,target⾥则定义了metrics的⽬标期望值,这⾥target的type也有三种类型Utilization,AverageValue和 Value,不同的metrics type都只能⽀持部分target type(详见上⾯表格)
4. 此外,在autoscaling/v2beta2的HPA的spec⾥还新增了⼀个Behavior可选结构,它是⽤来精确控制HPA的扩容和缩容的速度,下
⾯会专门讲。
完整的HPA的定义可参考。
默认HPA spec⾥不配置任何metrics的话k8s会默认设置cpu的Resouce,且⽬标类型是AverageUtilization value为80%。
5. HPA扩缩容算法具体实现
算法模型
在HPA控制器⾥,针对不同类型的metrics和不同metrics下的target 类型,都有独⽴的计算算法,虽然有很多细节差异,但是总的来说,计算公式可以抽象为:
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
例如如果配置 target value 是100m,当前从metrics接⼝读取到的 metrics value 是 200m,说明最新的副本数应该是当前的
200m/100m=2.0倍, 如果当前副本数为 2,则HPA计算后的期望副本数是2*2.0=4;
⽽如果当前从metrics接⼝读取到的 metrics value是 50m,说明最新的副本数应该是 当前的 50m/100m=0.5倍,也就是最终scale的副本数将为1。
当然实际上当前的metrics value并不⼀定就只有⼀个值,如果是 Resource或者Pods类型的metrics,实际上 GetMetrics 会返回⼀批关联的Pods对应的metrics数据,⼀般需要做⼀个平均后再与target的metrics的做⽐较。
此外,为了保证结果尽量精确,metrics的计算都是浮点数计算,但是最终的副本数肯定要是整数,为了统⼀HPA控制器在最后,都会对计算出的浮点数副本数向上取整,也就是上⾯公式⾥最外层的ceil函数。
扩缩容threshold控制
当然上⾯的公式也只是纯数学模型,实际⼯程实现还要考虑很多现实细节问题:⽐如监控数据可能会有⼀定的误差,如果GetMetrics⾥读到数据不稳定就会造成算出的期望副本数不稳定,导致deployment⼀会扩缩1个副本,⼀会⼜扩容1副本。所以为了避免这种问题kube-controller-manager⾥有个HPA的专属参数 horizontal-pod-autoscaler-tolerance 表⽰HPA可容忍的最⼩副本数变化⽐例,默认是
0.1,如果期望变化的副本倍数在[0.9, 1.1] 之间就直接停⽌计算返回。那么如果相反,某个时间点开始metrics数据⼤幅增长,导致最后计算的副本数变化倍数很⼤,是否HPA控制器会⼀步扩容到位呢? 事实上HPA控制器为了避免副本倍增过快还加了个约束:单次倍增的倍数不能超过2倍,⽽如果原副本数
⼩于2,则可以⼀次性扩容到4副本,注意这⾥的速率是代码写死不可⽤配置的。(这个也是HPA controller 默认的扩缩容速率控制,autoscaling/v2beta2的HPA Behavior属性可以覆盖这⾥的全局默认速率)
缩容冷却机制(cooldown delay)
虽然HPA同时⽀持扩容和缩容,但是在⽣产环境上扩容⼀般来说重要性更⾼,特别是流量突增的时候,能否快速扩容决定了系统的稳定性,所以HPA的算法⾥对扩容的时机是没有额外限制的,只要达到扩容条件就会在reconcile⾥执⾏扩容(当前⼀次⾄少扩容到原来的1.1倍)。但是为了避免过早缩导致来回波动(thrashing ),⽽容影响服务稳定性甚,HPA的算法对缩容的要求⽐较严格,通过设置⼀个默认5min(可配置horizontal-pod-autoscaler-downscale-stabilization)的滑动窗⼝,来记录过去5分钟的期望副本数,只有连续5分钟计算出的期望副本数都⽐当前副本数⼩,才执⾏scale缩容操作,缩容的⽬标副本数取5分钟窗⼝的最⼤值。
总的来说k8s HPA算法的默认扩缩容原则是:快速扩容,谨慎缩容。
Pod的metrics数据过滤检查机制
⼀般情况下HPA的数据指标都来⾃k8s的Pod⾥,但是实际上每次创建deployment、对deployment做扩
缩容,Pod的副本数和状态都会不断变化,这就导致HPA controller在reconcile⾥获取到的metrics的指标肯定会有⼀定的异常,⽐如Pod还没有Running、Pod刚刚启动还在预热期、或者Pod中间临时OOM恰逢采集时刻、或者Pod正处在删除中,这些都可能导致metrics指标缺失。如果有任何 pod 的指标缺失,HPA控制器会采取最保守的⽅式重新计算平均值, 在需要缩⼩时假设这些 pod 消耗了⽬标值的 100%, 在需要放⼤时假设这些pod 消耗了0%⽬标值, 这可以在⼀定程度上抑制伸缩的幅度。
具体来说,HPA算法⾥把deployment下的所有Pod的metrics的指标数据分为三类:
ready pods list, deployment下处于Running状态的Pod且HPA controller成功通过GetMetrics获取的pod metrics的列表
ignore pods list, deployment下处于pending状态的Pods或者(仅对Resouce类似的cpu metrics有效)虽然pod running了但controller成功通过GetMetrics获取的pod metrics,但是pod的启动时间在配置的initial-readiness-delay和cpu-initialization-period 保护期内。
missing pods list,deployment下处于running状态的pod(⾮pending、⾮failed、⾮deleted状态)但是HPA controller通过GetMetrics⽆法获取的pod metrics的列表
在计算pod的平均metrics值的时候,统⼀把 ignore pods的metrics设置为最⼩值0,如果HPA扩缩容的
⽅向是扩容,把missing pods的metrics也设置为最⼩值0,如果是缩容⽅向则把missing pods的metrics也设置为最⼤值(如果是Resouce类型,最⼤值是Pod的request值,否则最⼤值就是target value)。
6. HPA的scale速度控制
讲解完HPA的原理及具体算法后,最后再重点介绍HPA在扩缩容的速率控制机制。在前⾯讲过HPA controller⾥默认扩缩容总原则是:快速扩容,谨慎缩容,扩容上虽然强调快,但是速度却是固定的最⼤于当前副本数的2倍速度,对于想设置其他倍数或者说想精确控制副本数增量的⽅式的需求都是不⾏的;缩容上则仅仅只是靠设置⼀个集全局的窗⼝时间,窗⼝期过后也就失去控制能⼒。
为了能更精准灵活地控制HPA的autoscale速度,从k8s v1.18(依赖HPA autoscaling/v2beta2)开始HPA的spec⾥新增了behavior 结构(如下定义)扩展了HPA的scale速度控制策略,该结构⽀持每个HPA实例独⽴配置扩缩容的速度,⽽且还可以单独配置扩容scaleUp 和缩容scaleDown使⽤不同的策略。
在扩容策略ScalingRules⾥,有个StabilizationWindowSeconds⽤来记录最近计算的期望副本数,效果跟上⾯缩容的cooldown delay机制⼀样,每次都会选择窗⼝⾥所有推荐值的最⼤值,保证结果的稳定性。
Policies是⼀个HPAScalingPolicy数组,每个HPAScalingPolicy才是真正控制速度的部分:扩缩容计算周期和周期内扩缩容变化的最⼤幅度,PeriodSeconds周期单位是秒,Percent是设置副本数每次变化的百分⽐,扩容后副本数是:(1+PercentValue%)* currentReplicas,缩容后副本数是:(1-PercentValue%)* currentReplicas; Pods则是设置每次副本数变化的绝对值。
次外,每个⽅向还可以设置多个策略,多个策略会同时计算最终副本数,最后结果则是通过SelectPolicy:Max/Min/Disabled做聚合,注意在缩容时Max会选择计算副本数最⼩的那个,Min会选择计算的副本数最⼤的那个,Disabled表⽰禁⽌这个⽅向的扩缩容。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。