摘要
“弹性”这个词从云原生火起来之后大家应该都耳熟能详了,这个在k8s里面算是比较核心的技术之一,很多企业之所以选择kubernetes想必这个也是很重要的一个原因,因为它真真的能为企业带来降本提效的作用,下面我这边将从多个方面去看我们是如何使用kubernetes弹性策略的,以及如何扩展它的功能来满足我们的业务需求。
何谓HPA
HPA(HorizontalPodAutoscaler )是kubernetes中的一种资源,主要用于控制自动扩缩容,可以根据服务的CPU、内存使用率或者自定义的指标来自动扩展或者缩容pod的数量。HPA控制器会定期的调整RC或Deployment的副本数,以使观察到的平均CPU利用率与用户指定的目标相匹配。
在生产环境中,可能性很多,比如流量突增,此时现有的pod数量不足以撑起所有的请求量,作为运维人员也不可能24小时守着业务,就算是手动触发,响应时间也不是那么及时,这时我们通过配置HPA可以实现自动扩容pod的副本数量,以应对突增的流量,当流量恢复正常后,HPA自动缩减pod的数量
HPA原理分析
指标获取周期
hpa默认每15s获取一次指标,如果指标超过阈值,就扩容,其周期由--horizontal-pod-autoscaler-sync-period选项自定义指标获取周期
缩容时间(5min)
虽然HPA同时支持扩容和缩容,但是在生产环境上扩容一般来说重要性更高,特别是流量突增的时候,能否快速扩容决定了系统的稳定性,所以HPA的算法里对扩容的时机是没有额外限制的,只要达到扩容条件就会在reconcile里执行扩容(当前一次至少扩容到原来的1.1倍),但是为了避免过早缩导致来回波动(thrashing ),而容影响服务稳定性甚,HPA的算法对缩容的要求比较严格,通过设置一个默认5min(可配置horizontal-pod-autoscaler-downscale-stabilization)的滑动窗口,来记录过去5分钟的期望副本数,只有连续5分钟计算出的期望副本数都比当前副本数小,才执行scale缩容操作,缩容的目标副本数取5分钟窗口的最大值。
总的来说k8s HPA算法的默认扩缩容原则是:快速扩容,谨慎缩容。
缩容时间可通过 --horizontal-pod-autoscaler-downscale-stabilization 进行配置
容忍度(0.1)
有了容忍度,就意味着不是当前指标值超过阈值就扩容,超过一点点是不会扩容的。
以cpu使用率为例:
容忍度默认为 0.1,即 1-当前cpu使用率/cpu使用率阈值 <= 1.1,就不进行扩缩容操作。
即:41/40=1.025,1-1.025=0.025<0.1 此时不扩容,源码解析如下:
通过label匹配pod
以cpu使用率为例:
算哪些pod的当前平均cpu使用率,取决于deployment的selector
如果两个deployment这里写的一样,匹配到的pod就会多,就会有问题,因此在部署workload的时候,尽量让pod的label是不一样的,之前在这一块出现过问题,由于每个应用有多个deployment,为了方便把label设置的一样了,结果每次CPU使用率还没有达到阈值就开始扩容了。
当前指标值---当前CPU使用率计算方法
上面一直以cpu使用率为案例,但是当前pod的平均cpu使用率怎么计算的呢?
算法如下:
通过metric-server获取到的pod的指标和/匹配到的pod中request指标和
注意:被除数是request值,不是limit值
期望副本数算法
hpa要扩容,就要根据当前指标值和阈值,计算出一个副本数的期望值
这个问题和上面问题相关联
期望副本数 = ceil[当前副本数 * ( 当前指标值 / 阈值 )]
源码中
desiredReplicas := int32(math.Ceil(newUsageRatio * float64(len(metrics))))
当前副本数:
不能简单理解当前副本数==deployment中的replicas 当前副本数==deployment的selector匹配到的pod数-FailedPod(staus=failed)
分阶段扩容
上面计算的期望副本数并不是最终的扩容副本数,如果上面计算的期望副本数很大,hpa不会一次性就扩容至那么大,hpa扩容是分阶段,详细解析如下:
- scaleUpLimitFactor = 2.0 // 扩容倍数
- scaleUpLimitMinimum = 4.0 // 扩容个数
扩容副本数最大值=Max(scaleUpLimitFactor*当前副本数, scaleUpLimitMinimum))
也就是说,每次扩容要么扩成原来的2倍,要么扩大4个 pod,二者取大者。
新版本特性
在kubernetes1.18之前,HPA扩缩容是无法调整灵敏度的:
- 对于缩容,由 kube-controller-manager 的 --horizontal-pod-autoscaler-downscale-stabilization-window 参数控制缩容时间窗口,默认 5 分钟,即负载减小后至少需要等 5 分钟才会缩容。
- 对于扩容,由 hpa controller 固定的算法、硬编码的常量因子来控制扩容速度,无法自定义
这样的设计逻辑导致用户无法自定义 HPA 的扩缩容灵敏度,而不同的业务场景对于扩容容灵敏度要求可能是不一样的,比如:
- 对于有流量突发的关键业务,在需要的时候应该快速扩容 (即便可能不需要,以防万一),但缩容要慢 (防止另一个流量高峰)。
- 对于一些需要处理大量数据的离线业务,在需要的时候应该尽快扩容以减少处理时间,不需要那么多资源的时候应该尽快缩容以节约成本。
- 处理常规数据/网络流量的业务,它们可能会以一般的方式扩大和缩小规模,以减少抖动。
HPA 在 K8s 1.18 迎来了一次更新,在之前 v2beta2 版本上新增了扩缩容灵敏度的控制,不过版本号依然保持 v2beta2 不变,这次更新实际就是在 HPA Spec 下新增了一个 behavior 字段,下面有 scaleUp 和 scaleDown 两个字段分别控制扩容和缩容的行为:
behavior: # 这里是重点
scaleUp:
policies:
- type: percent
value: 900%
上面的配置表示扩容时立即新增当前 9 倍数量的副本数,即立即扩容到当前 10 倍的 Pod 数量,当然也不能超过 maxReplicas 的限制。
scaleDown:
policies:
- type: pods
value: 1
periodSeconds: 600 # 每 10 分钟只缩掉 1 个 Pod
上面示例中增加了 scaleDown 的配置,指定缩容时每 10 分钟才缩掉 1 个 Pod,大大降低了缩容速度
实践
原生Hpa的Resource指标类型只能支持CPU指标和Memory指标,不支持获取prometheus中的指标,于是我们引入了开源工具KEDA,即支持CPU和Memory指标、也支持获取prometheus指标。KEDA本身是由红帽和微软共同发布,目前已经贡献给cncf,它是一个以Kubernetes为基础的事件驱动自动扩展器,用户可以根据需要处理的事件数量,来驱动Kubernetes中容器的扩展,KEDA提供用户,通过使用简单一致的API,就能进行自动扩展部署。
主要有以下组件:
Scaler:
连接到选定源(例如prometheus)并获取指标(表达式)的组件。
Metric Adapter :
指定适配器,将由Scaler读取的指标转发到HPA以启用自动缩放。
Controller:
负责监视ScaledObject资源和ScaledJob资源,进行hpa的创建和删除,可为容器提供从0到1(或从1到0)的缩放。
Metric Adapter 和 Controller 是两个独立运行的程序,部署在了k8s
KEDA并没有实现自己的HPA,最终起作用的还是HPA,KEDA只是根据CRD内容生成了HPA对象,这个HPA是External类型。
可以通过如下命令查看:kubectl get apiservice
external.metrics.k8s.io由keda提供,用于获取自定义指标
具体实现自定义指标HPA的架构图如下:
目前已经实现的HPA功能有:
- 内存:根据内存使用率
- CPU:根据CPU使用率
- 流控:服务发生熔断、限流触发
- 定时:指定服务某个时间点触发
- 自定义指标:对接第三方平台,比如prometheus
- 业务指标:可以根据业务暴露的指标触发
最后
关于弹性这块技术点还有很多,尤其在云计算的大背景下,我们如何结合云的能力实现真正的降本提效才是我们需要思考的,这一块大家可以去看下我之前写的一遍关于降本的思考,谢谢大家的阅读。