kubernetes系列随笔04:kubernetes的弹性原理

504 阅读7分钟

摘要

“弹性”这个词从云原生火起来之后大家应该都耳熟能详了,这个在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扩缩容是无法调整灵敏度的:

  1. 对于缩容,由 kube-controller-manager 的 --horizontal-pod-autoscaler-downscale-stabilization-window 参数控制缩容时间窗口,默认 5 分钟,即负载减小后至少需要等 5 分钟才会缩容。
  2. 对于扩容,由 hpa controller 固定的算法、硬编码的常量因子来控制扩容速度,无法自定义

这样的设计逻辑导致用户无法自定义 HPA 的扩缩容灵敏度,而不同的业务场景对于扩容容灵敏度要求可能是不一样的,比如:

  1. 对于有流量突发的关键业务,在需要的时候应该快速扩容 (即便可能不需要,以防万一),但缩容要慢 (防止另一个流量高峰)。
  2. 对于一些需要处理大量数据的离线业务,在需要的时候应该尽快扩容以减少处理时间,不需要那么多资源的时候应该尽快缩容以节约成本。
  3. 处理常规数据/网络流量的业务,它们可能会以一般的方式扩大和缩小规模,以减少抖动。

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
  • 业务指标:可以根据业务暴露的指标触发

最后

关于弹性这块技术点还有很多,尤其在云计算的大背景下,我们如何结合云的能力实现真正的降本提效才是我们需要思考的,这一块大家可以去看下我之前写的一遍关于降本的思考,谢谢大家的阅读。