关于kubernetes:kubernetes中cronhpa与hpa的共存对伸缩的影响

48次阅读

共计 3552 个字符,预计需要花费 9 分钟才能阅读完成。

一. 背景

在 kubernetes 中,hpa(HorizontalPodAutoscaler)能够依据工作负载的指标(cpu/mem 等),主动对其进行伸缩,即主动减少、缩小其 pod 数量。

cronhpa 是一个定时扩缩容的组件,它反对依照 Crontab 表达式的策略,定时地对 workload 进行扩缩容,这里应用 aliyun 的 kubernetes-cronhpa-controller。

cronhpa 的例子如下:

apiVersion: autoscaling.alibabacloud.com/v1beta1
kind: CronHorizontalPodAutoscaler
metadata:
  name: cronhpa-sample
spec:
   scaleTargetRef:
      apiVersion: apps/v1
      kind: Deployment
      name: nginx            // 对 deploy: nginx 进行定时扩缩容;jobs:
   - name: "scale-down"        // 在每分钟的 30s,将 deploy 缩容到 1 个正本;schedule: "30 */1 * * * *"
     targetSize: 1
   - name: "scale-up"        // 在每分钟的 0s,将 deploy 扩容到 3 个正本;schedule: "0 */1 * * * *"
     targetSize: 3

问题

如果 CronHPA 和 HPA,同时操作一个 scaleTargetRef,就会呈现 replicas 被 CronHPA 和 HPA 轮番批改的状况,比方:

  • HPA 依据指标,要将正本扩容为 5 个;
  • CronHPA 依据 crontab 规定,将正本扩容为 3 个;
  • 两个 controller 独立工作,两个扩容操作会互相笼罩;

二. 计划

kubernetes-cronhpa-controller 对该问题的计划:

  • 将 CronHPA 的 scaleTargetRef 设置为 HPA;
  • 再设置 HPA 的 target 为 deployment,从而让 CronHPA 感知 HPA 的以后状态;

最终:

  • CronHPA 能够感知到 HPA 的 minReplicas/maxReplicas/desiredReplicas;
  • CronHPA 能够通过 HPA 的 target,晓得 deploy 的 currentReplicas;

CronHPA 通过调整 hpa.minReplicas/hpa.maxReplicas/deploy.replicas 的形式,与 HPA 一起实现扩缩容操作。

策略调整

在 kubernetes-cronhpa-controller 计划的根底上,能够依据本人的需要进行调整,比方:

  • 规定 cronhpa 不批改 hpa 的参数;
  • 规定 cronhpa 在伸缩时,伸缩范畴为 [hpa.minReplica, hpa.maxReplica],不能超过范畴;

三. 源码

看一下 kubernetes-cronhpa-controller 对 cronhpa 和 hpa 共存伸缩的源码。

1. 源码入口

在执行定时工作的时候,对 cronhpa.TargetRef.Kind=”HPA” 独自解决:

// pkg/controller/cronjob.go
func (ch *CronJobHPA) Run() (msg string, err error) {startTime := time.Now()
    times := 0
    for {now := time.Now()
        ...
        // hpa compatible
        if ch.TargetRef.RefKind == "HorizontalPodAutoscaler" {    // target=HPA
            msg, err = ch.ScaleHPA()
            if err == nil {break}
        } else {msg, err = ch.ScalePlainRef()    // target=Deploy/Sts
            if err == nil {break}
        }
        ...
    }
    return msg, err
}

CronHPA 对 HPA 伸缩时,进行两个操作:

  • 批改 HPA 的 min/maxReplicas;
  • 对 HPA.target(deploy/statefulset) 进行 Scale;

CronHPA 对 HPA 伸缩时,根据的参数:

  • min: hpa.minReplicas
  • max: hpa.maxReplicas
  • deploy: hpa.Status.CurrentReplicas
  • target: cronhpa.job.TargetSize

2. 伸缩逻辑

ScaleHPA() 做的事件:

  • 根据上述不同的场景,更新 hpa.minReplicas 和 maxReplicas;
  • 伸缩 hpa.targetRef:

    • 若 hpa.targetRef 正本数 >= cronhpa.target,则不须要伸缩,间接返回;
    • 否则,将 hpa.targetRef 正本数,伸缩至 cronhpa.target;
// pkg/controller/cronjob.go
func (ch *CronJobHPA) ScaleHPA() (msg string, err error) {
    ...
    // 查问 HPA 对象
    hpa := &autoscalingapi.HorizontalPodAutoscaler{}
    err = ch.client.Get(ctx, types.NamespacedName{Namespace: ch.HPARef.Namespace, Name: ch.TargetRef.RefName}, hpa)
    ...
    mappings, err := ch.mapper.RESTMappings(targetGK)
    // 查问 HPA 的 target,即 scale 对象
    for _, mapping := range mappings {targetGR = mapping.Resource.GroupResource()
        scale, err = ch.scaler.Scales(ch.TargetRef.RefNamespace).Get(context.Background(), targetGR, targetRef.Name, v1.GetOptions{})
        if err == nil {
            found = true
            break
        }
    }
    // 依据不同场景,批改 hpa.minReplicas/maxReplicas
    updateHPA := false
    if ch.DesiredSize > hpa.Spec.MaxReplicas {
        hpa.Spec.MaxReplicas = ch.DesiredSize
        updateHPA = true
    }
    if ch.DesiredSize < *hpa.Spec.MinReplicas {
        *hpa.Spec.MinReplicas = ch.DesiredSize
        updateHPA = true
    }
    if hpa.Status.CurrentReplicas == *hpa.Spec.MinReplicas && ch.DesiredSize < hpa.Status.CurrentReplicas {
        *hpa.Spec.MinReplicas = ch.DesiredSize
        updateHPA = true
    }
    if hpa.Status.CurrentReplicas < ch.DesiredSize {
        *hpa.Spec.MinReplicas = ch.DesiredSize
        updateHPA = true
    }
    // 将 hpa 的批改长久化
    if updateHPA {err = ch.client.Update(ctx, hpa)
        if err != nil {return "", err}
    }
    // 若指标对象不须要伸缩,则间接返回
    if hpa.Status.CurrentReplicas >= ch.DesiredSize {
        // skip change replicas and exit
        return fmt.Sprintf("Skip scale replicas because HPA %s current replicas:%d >= desired replicas:%d.", hpa.Name, scale.Spec.Replicas, ch.DesiredSize), nil
    }
    // 否则,伸缩指标对象到 target 正本数
    scale.Spec.Replicas = int32(ch.DesiredSize)
    _, err = ch.scaler.Scales(ch.TargetRef.RefNamespace).Update(context.Background(), targetGR, scale, metav1.UpdateOptions{})
    return msg, nil
}

参考

1.https://help.aliyun.com/document_detail/151557.html
2.https://github.com/AliyunContainerService/kubernetes-cronhpa-controller

正文完
 0