Kubernetes弹性伸缩全场景解读(八) - 定时伸缩组件支持HPA兼容

前言

在之前的文章中,我们介绍了kubernetes-cronhpa-controller是如何通过设置定时的方式触发容器的水平副本伸缩,但是在实际的场景下,虽然定时伸缩对于负载有规律的应用比较友好,但是应用为了防止突发的流量冲击,还是会配置HPA来做最后的保障的。那么CronHPA与HPA之间该怎么选择呢?

定时伸缩组件兼容HPA

在抉择什么时候需要CronHPA,什么时候使用HPA的时候,我们在思考是否可以将CronHPA与HPA一起使用,如果一起使用会有什么需要解决的问题呢?首先我们先看CronHPA的模板定义

apiVersion: autoscaling.alibabacloud.com/v1beta1
kind: CronHorizontalPodAutoscaler
metadata:
  labels:
    controller-tools.k8s.io: "1.0"
  name: cronhpa-sample
spec:
   scaleTargetRef:
      apiVersion: apps/v1
      kind: Deployment
      name: nginx-deployment-basic
   jobs:
   - name: "scale-down"
     schedule: "30 */1 * * * *"
     targetSize: 1
   - name: "scale-up"
     schedule: "0 */1 * * * *"
     targetSize: 3

在CronHPA中是通过scaleTargetRef字段来获取伸缩对象的,并通过jobs的crontab规则定时伸缩实例的副本。

那么我们再来看下HPA的模板定义

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment-basic
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

HPA也是通过scaleTargetRef来定义伸缩的对象,并通过资源利用率来判断伸缩的情况。如果同时设置CronHPA与HPA,那么就会出现HPA与CronHPA同时操作一个scaleTargetRef的场景,而两者之间又相互独立无法感知,这样就会出现两个controller各自工作,后执行的会覆盖先执行的结果。

Kubernetes弹性伸缩全场景解读(八) - 定时伸缩组件支持HPA兼容
这个问题的本质是两个controller无法相互感知,从而造成了异常,当回过头来看这个问题的时候,其实我们可以发现HPA早期也有同样的问题,开发者如果希望通过用两个监控指标同时作用到HPA的时候,如果设置两个HPA对象,会出现类似的问题,在解决这个问题的时候,是通过在HPA对象中定义metrics字段,将多个metrics合并到一个HPA对象中来实现的,例如:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment-basic
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 50

当两个metrics触发弹性的个数不同的时候,会根据稳定性第一的原则,优先弹出更多的副本或者在缩容时保留更多的副本。那么是否CronHPA和HPA也可以通过这个方案进行整合,答案是Yes and No。因为的确可以通过alibaba-cloud-metrics-adapter将定时的数据通过External Metrics的方式进行转换,然后通过HPA中使用External Metrics的方式进行整合和匹配。但是这样会带来的结果就是,我们需要通过HPA的结构去表达CronHPA的规则,然后再通过Metrics Adapter的模型去转换时间信息与副本计算。从模型上来看,这个方式看似兼容了HPA,但是实际上对定时伸缩的可读性、学习成本、出错诊断、审计与离线都带来了新的挑战。

那么是否还有其他的方法可以实现CronHPA与HPA的兼容呢?我们将视角放回scaleTargetRef,还记得HPA是怎么伸缩Deployment的Pod吗,是HPA将Deployment配置在了scaleTargetRef的字段下,然后Deployment通过自身定义查找到了ReplicaSet,在通过ReplicaSet调整了真实的副本数目。
Kubernetes弹性伸缩全场景解读(八) - 定时伸缩组件支持HPA兼容
那么从这个角度出发,我们有了一个大胆的想法,是否可以将scaleTargetRef设置为HPA对象,然后通过HPA对象来寻找真实的scaleTargetRef
Kubernetes弹性伸缩全场景解读(八) - 定时伸缩组件支持HPA兼容

apiVersion: autoscaling.alibabacloud.com/v1beta1
kind: CronHorizontalPodAutoscaler
metadata:
  labels:
    controller-tools.k8s.io: "1.0"
  name: cronhpa-sample
spec:
   scaleTargetRef:
      apiVersion: autoscaling/v1
      kind: HorizontalPodAutoscaler
      name:  nginx-deployment-basic-hpa
   jobs:
   - name: "scale-down"
     schedule: "30 */1 * * * *"
     targetSize: 1
     runOnce: true
   - name: "scale-up"
     schedule: "0 */1 * * * *"
     targetSize: 3
     runOnce: true

这样设计的好处是,首先CronHPA可以感知HPA当前的状态,明确的知晓HPA的min、max、desired的数值,同时也知道HPA scaleTargetRef所对应的当前replicas。那么本着稳定性原则,我们要如何操控HPA呢?

hpa(min/max) cronhpa deployment result 场景
1/10 5 5 hpa(1/10) deployment 5 定时和当前一致,无需变更
1/10 4 5 hpa(1/10) deployment 5 当前高于定时,保留当前副本
1/10 6 5 hpa(6/10) deployment 6 定时高于当前,保留定时副本
定时高于HPA下限,修改HPA下限
5/10 4 5 hpa(4/10) deployment 5 定时低于当前,保留当前副本
定时低于HPA下限,修改HPA下限
5/10 11 5 hpa(11/11) deployment 11 定时高于当前,保留定时副本
定时高于HPA上限,修改HPA上限

如上图所以,CronHPA会通过调整HPA的方式进行感知,CronHPA要达到的副本和当前副本取大值,来判断是否要扩容以及修改HPA的上限。CronHPA要达到的副本和HPA的配置取小值,判断是否要修改HPA的下限。简单而言,CronHPA不会直接调整Deployment的副本数目,而是通过HPA来操作Deployment,这样就可以避免HPA和CronHPA的冲突问题了。

最后

定时伸缩CronHPA和HPA都是在线业务场景下非常重要的功能,不论使用何种的兼容与适配的方式,稳定性第一的原则是不能改变的,开发者如果对CronHPA感兴趣,欢迎提交PR

上一篇:c#中WMI 中的日期和时间转为本地时间


下一篇:linux vi 替换