从源码看k8s 1.33中对hpa的优化

背景

早期版本hpa扩缩容的容忍度默认都是10%,剋用通过配置horizontal-pod-autoscaler-tolerance修改,但是无法区分扩还是缩,也就是全局配置
新增特性开关HPAConfigurableTolerance,控制hpa在扩缩容时候的容忍度(如果不配置则使用全局的配置)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demo
spec:
    ...
    behavior:
        scaleDown:
            ...
            tolerance:0.2
        scaleUp:
            ...
            tolerance: 0

源码

pkg/controller/podautoscaler/horizontal.go中

根据指标计算副本数
func (a *HorizontalController) computeReplicasForMetric(ctx context.Context, hpa *autoscalingv2.HorizontalPodAutoscaler, spec autoscalingv2.MetricSpec,
    specReplicas, statusReplicas int32, selector labels.Selector, status *autoscalingv2.MetricStatus) (replicaCountProposal int32, metricNameProposal string,
    timestampProposal time.Time, condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
        ...
    以objectmetrics为例
    switch spec.Type {
    case autoscalingv2.ObjectMetricSourceType:
        ,,,
        replicaCountProposal, timestampProposal, metricNameProposal, condition, err = a.computeStatusForObjectMetric(specReplicas, statusReplicas, spec, hpa, selector, status, metricSelector)
        ...
    ...
}


根据objectmetrics计算状态
func (a *HorizontalController) computeStatusForObjectMetric(specReplicas, statusReplicas int32, metricSpec autoscalingv2.MetricSpec, hpa *autoscalingv2.HorizontalPodAutoscaler, selector labels.Selector, status *autoscalingv2.MetricStatus, metricSelector labels.Selector) (replicas int32, timestamp time.Time, metricName string, condition autoscalingv2.HorizontalPodAutoscalerCondition, err error) {
    ...
    计算容忍度
    tolerances := a.tolerancesForHpa(hpa)
    ...
    以ValueMetricType为例计算状态
    if metricSpec.Object.Target.Type == autoscalingv2.ValueMetricType && metricSpec.Object.Target.Value != nil {
        replicaCountProposal, usageProposal, timestampProposal, err := a.replicaCalc.GetObjectMetricReplicas(specReplicas, metricSpec.Object.Target.Value.MilliValue(), metricSpec.Object.Metric.Name, tolerances, hpa.Namespace, &metricSpec.Object.DescribedObject, selector, metricSelector)
        if err != nil {
            condition := a.getUnableComputeReplicaCountCondition(hpa, "FailedGetObjectMetric", err)
            return 0, timestampProposal, "", condition, err
        }
        metricStatus.Object.Current.Value = resource.NewMilliQuantity(usageProposal, resource.DecimalSI)
        *status = metricStatus
        return replicaCountProposal, timestampProposal, fmt.Sprintf("%s metric %s", metricSpec.Object.DescribedObject.Kind, metricSpec.Object.Metric.Name), autoscalingv2.HorizontalPodAutoscalerCondition{}, nil
    ...
}


计算容忍度
func (a *HorizontalController) tolerancesForHpa(hpa *autoscalingv2.HorizontalPodAutoscaler) Tolerances {
    t := Tolerances{a.tolerance, a.tolerance}
    behavior := hpa.Spec.Behavior
    allowConfigurableTolerances := utilfeature.DefaultFeatureGate.Enabled(features.HPAConfigurableTolerance)
    如果没打开开关或者没配置behavior,则使用全局配置
    if behavior == nil || !allowConfigurableTolerances {
        return t
    }
    if behavior.ScaleDown != nil && behavior.ScaleDown.Tolerance != nil {
        t.scaleDown = behavior.ScaleDown.Tolerance.AsApproximateFloat64()
    }
    if behavior.ScaleUp != nil && behavior.ScaleUp.Tolerance != nil {
        t.scaleUp = behavior.ScaleUp.Tolerance.AsApproximateFloat64()
    }
    return t
}

pkg/controller/podautoscaler/replica_calculator.go中

func (c *ReplicaCalculator) GetObjectMetricReplicas(currentReplicas int32, targetUsage int64, metricName string, tolerances Tolerances, namespace string, objectRef *autoscaling.CrossVersionObjectReference, selector labels.Selector, metricSelector labels.Selector) (replicaCount int32, usage int64, timestamp time.Time, err error) {
    ...
    计算相对于目标值的百分比
    usageRatio := float64(usage) / float64(targetUsage)
    计算副本数
    replicaCount, timestamp, err = c.getUsageRatioReplicaCount(currentReplicas, usageRatio, tolerances, namespace, selector)
    ...
}


func (c *ReplicaCalculator) getUsageRatioReplicaCount(currentReplicas int32, usageRatio float64, tolerances Tolerances, namespace string, selector labels.Selector) (replicaCount int32, timestamp time.Time, err error) {
    如果当前副本数非0且相对于目标值的百分比的偏差在容忍度范围内则不进行扩缩容
    if currentReplicas != 0 {    
        if tolerances.isWithin(usageRatio) {
    ...
}

pkg/controller/podautoscaler/replica_calculator.go中

容忍度配置
type Tolerances struct {
    scaleDown float64
    scaleUp   float64
}

在容忍度范围内
func (t Tolerances) isWithin(usageRatio float64) bool {
    return (1.0-t.scaleDown) <= usageRatio && usageRatio <= (1.0+t.scaleUp)
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容