@@ -37,6 +37,7 @@ import (
37
37
"k8s.io/apimachinery/pkg/runtime/schema"
38
38
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
39
39
"k8s.io/apimachinery/pkg/util/wait"
40
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
40
41
autoscalinginformers "k8s.io/client-go/informers/autoscaling/v2"
41
42
coreinformers "k8s.io/client-go/informers/core/v1"
42
43
"k8s.io/client-go/kubernetes/scheme"
@@ -53,6 +54,7 @@ import (
53
54
metricsclient "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
54
55
"k8s.io/kubernetes/pkg/controller/podautoscaler/monitor"
55
56
"k8s.io/kubernetes/pkg/controller/util/selectors"
57
+ "k8s.io/kubernetes/pkg/features"
56
58
)
57
59
58
60
var (
@@ -86,6 +88,7 @@ type HorizontalController struct {
86
88
hpaNamespacer autoscalingclient.HorizontalPodAutoscalersGetter
87
89
mapper apimeta.RESTMapper
88
90
91
+ tolerance float64
89
92
replicaCalc * ReplicaCalculator
90
93
eventRecorder record.EventRecorder
91
94
@@ -146,6 +149,7 @@ func NewHorizontalController(
146
149
eventRecorder : recorder ,
147
150
scaleNamespacer : scaleNamespacer ,
148
151
hpaNamespacer : hpaNamespacer ,
152
+ tolerance : tolerance ,
149
153
downscaleStabilisationWindow : downscaleStabilisationWindow ,
150
154
monitor : monitor .New (),
151
155
queue : workqueue .NewTypedRateLimitingQueueWithConfig (
@@ -181,7 +185,6 @@ func NewHorizontalController(
181
185
replicaCalc := NewReplicaCalculator (
182
186
metricsClient ,
183
187
hpaController .podLister ,
184
- tolerance ,
185
188
cpuInitializationPeriod ,
186
189
delayOfInitialReadinessStatus ,
187
190
)
@@ -539,8 +542,9 @@ func (a *HorizontalController) computeStatusForObjectMetric(specReplicas, status
539
542
},
540
543
},
541
544
}
545
+ tolerances := a .tolerancesForHpa (hpa )
542
546
if metricSpec .Object .Target .Type == autoscalingv2 .ValueMetricType && metricSpec .Object .Target .Value != nil {
543
- replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetObjectMetricReplicas (specReplicas , metricSpec .Object .Target .Value .MilliValue (), metricSpec .Object .Metric .Name , hpa .Namespace , & metricSpec .Object .DescribedObject , selector , metricSelector )
547
+ 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 )
544
548
if err != nil {
545
549
condition := a .getUnableComputeReplicaCountCondition (hpa , "FailedGetObjectMetric" , err )
546
550
return 0 , timestampProposal , "" , condition , err
@@ -549,7 +553,7 @@ func (a *HorizontalController) computeStatusForObjectMetric(specReplicas, status
549
553
* status = metricStatus
550
554
return replicaCountProposal , timestampProposal , fmt .Sprintf ("%s metric %s" , metricSpec .Object .DescribedObject .Kind , metricSpec .Object .Metric .Name ), autoscalingv2.HorizontalPodAutoscalerCondition {}, nil
551
555
} else if metricSpec .Object .Target .Type == autoscalingv2 .AverageValueMetricType && metricSpec .Object .Target .AverageValue != nil {
552
- replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetObjectPerPodMetricReplicas (statusReplicas , metricSpec .Object .Target .AverageValue .MilliValue (), metricSpec .Object .Metric .Name , hpa .Namespace , & metricSpec .Object .DescribedObject , metricSelector )
556
+ replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetObjectPerPodMetricReplicas (statusReplicas , metricSpec .Object .Target .AverageValue .MilliValue (), metricSpec .Object .Metric .Name , tolerances , hpa .Namespace , & metricSpec .Object .DescribedObject , metricSelector )
553
557
if err != nil {
554
558
condition := a .getUnableComputeReplicaCountCondition (hpa , "FailedGetObjectMetric" , err )
555
559
return 0 , time.Time {}, "" , condition , fmt .Errorf ("failed to get %s object metric: %v" , metricSpec .Object .Metric .Name , err )
@@ -566,7 +570,8 @@ func (a *HorizontalController) computeStatusForObjectMetric(specReplicas, status
566
570
567
571
// computeStatusForPodsMetric computes the desired number of replicas for the specified metric of type PodsMetricSourceType.
568
572
func (a * HorizontalController ) computeStatusForPodsMetric (currentReplicas int32 , metricSpec autoscalingv2.MetricSpec , hpa * autoscalingv2.HorizontalPodAutoscaler , selector labels.Selector , status * autoscalingv2.MetricStatus , metricSelector labels.Selector ) (replicaCountProposal int32 , timestampProposal time.Time , metricNameProposal string , condition autoscalingv2.HorizontalPodAutoscalerCondition , err error ) {
569
- replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetMetricReplicas (currentReplicas , metricSpec .Pods .Target .AverageValue .MilliValue (), metricSpec .Pods .Metric .Name , hpa .Namespace , selector , metricSelector )
573
+ tolerances := a .tolerancesForHpa (hpa )
574
+ replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetMetricReplicas (currentReplicas , metricSpec .Pods .Target .AverageValue .MilliValue (), metricSpec .Pods .Metric .Name , tolerances , hpa .Namespace , selector , metricSelector )
570
575
if err != nil {
571
576
condition = a .getUnableComputeReplicaCountCondition (hpa , "FailedGetPodsMetric" , err )
572
577
return 0 , timestampProposal , "" , condition , err
@@ -588,12 +593,14 @@ func (a *HorizontalController) computeStatusForPodsMetric(currentReplicas int32,
588
593
}
589
594
590
595
func (a * HorizontalController ) computeStatusForResourceMetricGeneric (ctx context.Context , currentReplicas int32 , target autoscalingv2.MetricTarget ,
591
- resourceName v1.ResourceName , namespace string , container string , selector labels.Selector , sourceType autoscalingv2.MetricSourceType ) (replicaCountProposal int32 ,
596
+ resourceName v1.ResourceName , hpa * autoscalingv2. HorizontalPodAutoscaler , container string , selector labels.Selector , sourceType autoscalingv2.MetricSourceType ) (replicaCountProposal int32 ,
592
597
metricStatus * autoscalingv2.MetricValueStatus , timestampProposal time.Time , metricNameProposal string ,
593
598
condition autoscalingv2.HorizontalPodAutoscalerCondition , err error ) {
599
+ namespace := hpa .Namespace
600
+ tolerances := a .tolerancesForHpa (hpa )
594
601
if target .AverageValue != nil {
595
602
var rawProposal int64
596
- replicaCountProposal , rawProposal , timestampProposal , err := a .replicaCalc .GetRawResourceReplicas (ctx , currentReplicas , target .AverageValue .MilliValue (), resourceName , namespace , selector , container )
603
+ replicaCountProposal , rawProposal , timestampProposal , err := a .replicaCalc .GetRawResourceReplicas (ctx , currentReplicas , target .AverageValue .MilliValue (), resourceName , tolerances , namespace , selector , container )
597
604
if err != nil {
598
605
return 0 , nil , time.Time {}, "" , condition , fmt .Errorf ("failed to get %s usage: %v" , resourceName , err )
599
606
}
@@ -610,7 +617,7 @@ func (a *HorizontalController) computeStatusForResourceMetricGeneric(ctx context
610
617
}
611
618
612
619
targetUtilization := * target .AverageUtilization
613
- replicaCountProposal , percentageProposal , rawProposal , timestampProposal , err := a .replicaCalc .GetResourceReplicas (ctx , currentReplicas , targetUtilization , resourceName , namespace , selector , container )
620
+ replicaCountProposal , percentageProposal , rawProposal , timestampProposal , err := a .replicaCalc .GetResourceReplicas (ctx , currentReplicas , targetUtilization , resourceName , tolerances , namespace , selector , container )
614
621
if err != nil {
615
622
return 0 , nil , time.Time {}, "" , condition , fmt .Errorf ("failed to get %s utilization: %v" , resourceName , err )
616
623
}
@@ -630,7 +637,7 @@ func (a *HorizontalController) computeStatusForResourceMetricGeneric(ctx context
630
637
func (a * HorizontalController ) computeStatusForResourceMetric (ctx context.Context , currentReplicas int32 , metricSpec autoscalingv2.MetricSpec , hpa * autoscalingv2.HorizontalPodAutoscaler ,
631
638
selector labels.Selector , status * autoscalingv2.MetricStatus ) (replicaCountProposal int32 , timestampProposal time.Time ,
632
639
metricNameProposal string , condition autoscalingv2.HorizontalPodAutoscalerCondition , err error ) {
633
- replicaCountProposal , metricValueStatus , timestampProposal , metricNameProposal , condition , err := a .computeStatusForResourceMetricGeneric (ctx , currentReplicas , metricSpec .Resource .Target , metricSpec .Resource .Name , hpa . Namespace , "" , selector , autoscalingv2 .ResourceMetricSourceType )
640
+ replicaCountProposal , metricValueStatus , timestampProposal , metricNameProposal , condition , err := a .computeStatusForResourceMetricGeneric (ctx , currentReplicas , metricSpec .Resource .Target , metricSpec .Resource .Name , hpa , "" , selector , autoscalingv2 .ResourceMetricSourceType )
634
641
if err != nil {
635
642
condition = a .getUnableComputeReplicaCountCondition (hpa , "FailedGetResourceMetric" , err )
636
643
return replicaCountProposal , timestampProposal , metricNameProposal , condition , err
@@ -649,7 +656,7 @@ func (a *HorizontalController) computeStatusForResourceMetric(ctx context.Contex
649
656
func (a * HorizontalController ) computeStatusForContainerResourceMetric (ctx context.Context , currentReplicas int32 , metricSpec autoscalingv2.MetricSpec , hpa * autoscalingv2.HorizontalPodAutoscaler ,
650
657
selector labels.Selector , status * autoscalingv2.MetricStatus ) (replicaCountProposal int32 , timestampProposal time.Time ,
651
658
metricNameProposal string , condition autoscalingv2.HorizontalPodAutoscalerCondition , err error ) {
652
- replicaCountProposal , metricValueStatus , timestampProposal , metricNameProposal , condition , err := a .computeStatusForResourceMetricGeneric (ctx , currentReplicas , metricSpec .ContainerResource .Target , metricSpec .ContainerResource .Name , hpa . Namespace , metricSpec .ContainerResource .Container , selector , autoscalingv2 .ContainerResourceMetricSourceType )
659
+ replicaCountProposal , metricValueStatus , timestampProposal , metricNameProposal , condition , err := a .computeStatusForResourceMetricGeneric (ctx , currentReplicas , metricSpec .ContainerResource .Target , metricSpec .ContainerResource .Name , hpa , metricSpec .ContainerResource .Container , selector , autoscalingv2 .ContainerResourceMetricSourceType )
653
660
if err != nil {
654
661
condition = a .getUnableComputeReplicaCountCondition (hpa , "FailedGetContainerResourceMetric" , err )
655
662
return replicaCountProposal , timestampProposal , metricNameProposal , condition , err
@@ -667,8 +674,9 @@ func (a *HorizontalController) computeStatusForContainerResourceMetric(ctx conte
667
674
668
675
// computeStatusForExternalMetric computes the desired number of replicas for the specified metric of type ExternalMetricSourceType.
669
676
func (a * HorizontalController ) computeStatusForExternalMetric (specReplicas , statusReplicas int32 , metricSpec autoscalingv2.MetricSpec , hpa * autoscalingv2.HorizontalPodAutoscaler , selector labels.Selector , status * autoscalingv2.MetricStatus ) (replicaCountProposal int32 , timestampProposal time.Time , metricNameProposal string , condition autoscalingv2.HorizontalPodAutoscalerCondition , err error ) {
677
+ tolerances := a .tolerancesForHpa (hpa )
670
678
if metricSpec .External .Target .AverageValue != nil {
671
- replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetExternalPerPodMetricReplicas (statusReplicas , metricSpec .External .Target .AverageValue .MilliValue (), metricSpec .External .Metric .Name , hpa .Namespace , metricSpec .External .Metric .Selector )
679
+ replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetExternalPerPodMetricReplicas (statusReplicas , metricSpec .External .Target .AverageValue .MilliValue (), metricSpec .External .Metric .Name , tolerances , hpa .Namespace , metricSpec .External .Metric .Selector )
672
680
if err != nil {
673
681
condition = a .getUnableComputeReplicaCountCondition (hpa , "FailedGetExternalMetric" , err )
674
682
return 0 , time.Time {}, "" , condition , fmt .Errorf ("failed to get %s external metric: %v" , metricSpec .External .Metric .Name , err )
@@ -688,7 +696,7 @@ func (a *HorizontalController) computeStatusForExternalMetric(specReplicas, stat
688
696
return replicaCountProposal , timestampProposal , fmt .Sprintf ("external metric %s(%+v)" , metricSpec .External .Metric .Name , metricSpec .External .Metric .Selector ), autoscalingv2.HorizontalPodAutoscalerCondition {}, nil
689
697
}
690
698
if metricSpec .External .Target .Value != nil {
691
- replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetExternalMetricReplicas (specReplicas , metricSpec .External .Target .Value .MilliValue (), metricSpec .External .Metric .Name , hpa .Namespace , metricSpec .External .Metric .Selector , selector )
699
+ replicaCountProposal , usageProposal , timestampProposal , err := a .replicaCalc .GetExternalMetricReplicas (specReplicas , metricSpec .External .Target .Value .MilliValue (), metricSpec .External .Metric .Name , tolerances , hpa .Namespace , metricSpec .External .Metric .Selector , selector )
692
700
if err != nil {
693
701
condition = a .getUnableComputeReplicaCountCondition (hpa , "FailedGetExternalMetric" , err )
694
702
return 0 , time.Time {}, "" , condition , fmt .Errorf ("failed to get external metric %s: %v" , metricSpec .External .Metric .Name , err )
@@ -835,6 +843,7 @@ func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShare
835
843
logger .V (4 ).Info ("Proposing desired replicas" ,
836
844
"desiredReplicas" , metricDesiredReplicas ,
837
845
"metric" , metricName ,
846
+ "tolerances" , a .tolerancesForHpa (hpa ),
838
847
"timestamp" , metricTimestamp ,
839
848
"scaleTarget" , reference )
840
849
@@ -1384,6 +1393,25 @@ func (a *HorizontalController) updateStatus(ctx context.Context, hpa *autoscalin
1384
1393
return nil
1385
1394
}
1386
1395
1396
+ // tolerancesForHpa returns the metrics usage ratio tolerances for a given HPA.
1397
+ // It ignores configurable tolerances set in the HPA spec.behavior field if the
1398
+ // HPAConfigurableTolerance feature gate is disabled.
1399
+ func (a * HorizontalController ) tolerancesForHpa (hpa * autoscalingv2.HorizontalPodAutoscaler ) Tolerances {
1400
+ t := Tolerances {a .tolerance , a .tolerance }
1401
+ behavior := hpa .Spec .Behavior
1402
+ allowConfigurableTolerances := utilfeature .DefaultFeatureGate .Enabled (features .HPAConfigurableTolerance )
1403
+ if behavior == nil || ! allowConfigurableTolerances {
1404
+ return t
1405
+ }
1406
+ if behavior .ScaleDown != nil && behavior .ScaleDown .Tolerance != nil {
1407
+ t .scaleDown = behavior .ScaleDown .Tolerance .AsApproximateFloat64 ()
1408
+ }
1409
+ if behavior .ScaleUp != nil && behavior .ScaleUp .Tolerance != nil {
1410
+ t .scaleUp = behavior .ScaleUp .Tolerance .AsApproximateFloat64 ()
1411
+ }
1412
+ return t
1413
+ }
1414
+
1387
1415
// setCondition sets the specific condition type on the given HPA to the specified value with the given reason
1388
1416
// and message. The message and args are treated like a format string. The condition will be added if it is
1389
1417
// not present.
0 commit comments