背景
早期版本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)
}