hpa
# 1.副本计算
# 1.1.resourceMetric
a.computeStatusForResourceMetric()基于基于Pod资源使用量(CPU/Mem)计算需要的副本数,再结合当前副本数计算合适的目标副本。// computeStatusForResourceMetric computes the desired replicas for the specified metric of type ResourceMetric. func (a *HorizontalController) computeStatusForResourceMetric(...) (...) { // 执行通用副本计算 rc, metricValueStatus, ts, metricName, condition, err := a.computeStatusForResourceMetricGeneric(...) ... // 更新指标状态 *status = autoscalingv2.MetricStatus{ Type: autoscalingv2.ResourceMetricSourceType, Resource: &autoscalingv2.ResourceMetricStatus{ Name: metricSpec.Resource.Name, Current: *metricValueStatus, }, } return rc, ts, metricName, condition, nil } // 基于resourceMetric计算副本 func (a *HorizontalController) computeStatusForResourceMetricGeneric(...) (...) { // 基于绝对值计算 if target.AverageValue != nil { ... // 获取metric计算replicas rc, rawProposal, ts, err := a.replicaCalc.GetRawResourceReplicas(ctx, currentReplicas, target.AverageValue.MilliValue(), resourceName, namespace, selector, container) ... metricNameProposal = fmt.Sprintf("%s resource", resourceName.String()) status := autoscalingv2.MetricValueStatus{ AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI), } return rc, &status, ts, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil } ... // 基于百分比计算 targetUtilization := *target.AverageUtilization // 获取指标计算replicas rc, percent, rawProposal, ts, err := a.replicaCalc.GetResourceReplicas(ctx, currentReplicas, targetUtilization, resourceName, namespace, selector, container) ... metricNameProposal = fmt.Sprintf("%s resource utilization (percentage of request)", resourceName) if sourceType == autoscalingv2.ContainerResourceMetricSourceType { metricNameProposal = fmt.Sprintf("%s container resource utilization (percentage of request)", resourceName) } status := autoscalingv2.MetricValueStatus{ AverageUtilization: &percent, AverageValue: resource.NewMilliQuantity(rawProposal, resource.DecimalSI), } return rc, &status, ts, metricNameProposal, autoscalingv2.HorizontalPodAutoscalerCondition{}, nil }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
注意
a.computeStatusForResourceMetricGeneric()根据配置的值或百分比获取指标进行预期副本数计算
# 1.2.rawResReplicas
a.GetRawResourceReplicas()会先获取metrics,其次获取当前底层的Pod,基于当前Pod资源使用情况与预期限制进行预期副本数计算。// GetRawResourceReplicas calculates the desired replica count based on a target resource usage. func (c *ReplicaCalculator) GetRawResourceReplicas(...) (rc int32, usage int64, ts time.Time, err error) { // 访问/metrics.k8s.io/v1beta1获取Pod/container metric metrics, timestamp, err := c.metricsClient.GetResourceMetric(ctx, resource, namespace, selector, container) ... // 基于metric计算预期副本 replicaCount, usage, err = c.calcPlainMetricReplicas(metrics, currentReplicas, targetUsage, namespace, selector, resource) return replicaCount, usage, timestamp, err } // calcPlainMetricReplicas calculates the desired replicas for plain (i.e. non-utilization percentage) metrics. func (c *ReplicaCalculator) calcPlainMetricReplicas(...) (replicaCount int32, usage int64, err error) { // 获取hpa匹配的Pod podList, err := c.podLister.Pods(namespace).List(selector) ... // 基于Pod状态及metric实时性拆分Pod为ready、unready、missing及ignore readyPodCount, unreadyPods, missingPods, ignoredPods := groupPods(podList, metrics, resource, c.cpuInitializationPeriod, c.delayOfInitialReadinessStatus) // 移除unready/ignore Pod的metric removeMetricsForPods(metrics, ignoredPods) removeMetricsForPods(metrics, unreadyPods) ... // usageRatio=curTotal/(len(metrics)*targetUsage) usage=curTotal usageRatio, usage := metricsclient.GetMetricUsageRatio(metrics, targetUsage) ... // 没有unready Pod或非扩容+所有Pod都有指标 if !scaleUpWithUnready && len(missingPods) == 0 { // 容忍度范围内,replicas不调整 if math.Abs(1.0-usageRatio) <= c.tolerance { return currentReplicas, usage, nil } // dsr = readyPodCount*usageRatio = curTotal/targetUsage return int32(math.Ceil(usageRatio * float64(readyPodCount))), usage, nil } // 部分Pod没有指标 if len(missingPods) > 0 { // 缩容 if usageRatio < 1.0 { // missingPod的metric重置为target for podName := range missingPods { metrics[podName] = metricsclient.PodMetric{Value: targetUsage} } // 扩容 } else { // missingPod的metric重置为0 for podName := range missingPods { metrics[podName] = metricsclient.PodMetric{Value: 0} } } } // 扩容且没有unreadyPod if scaleUpWithUnready { // unreadyPod metric重置为0 for podName := range unreadyPods { metrics[podName] = metricsclient.PodMetric{Value: 0} } } // usageRatio=(curTotal+m*T)/(len(metrics)*targetUsage) newUsageRatio, _ := metricsclient.GetMetricUsageRatio(metrics, targetUsage) // 浮动太小 || 缩容变扩容 || 扩容变缩容 if math.Abs(1.0-newUsageRatio) <= c.tolerance || (usageRatio < 1.0 && newUsageRatio > 1.0) || (usageRatio > 1.0 && newUsageRatio < 1.0) { return currentReplicas, usage, nil } // newReplicas = (curTotal+m*T)/targetUsage,扩容条件m*T==0 // 扩容时newReplicas = curTotal/targetUsage,上述的补齐不会影响结果 // 缩容时newReplicas = (curTotal+m*T)/targetUsage,这里多出的m*T是missing Pod,意为缩容时保留 newReplicas := int32(math.Ceil(newUsageRatio * float64(len(metrics)))) // 非法缩容或非法扩容 if (newUsageRatio < 1.0 && newReplicas > currentReplicas) || (newUsageRatio > 1.0 && newReplicas < currentReplicas) { // return the current replicas if the change of metrics length would cause a change in scale direction return currentReplicas, usage, nil } return newReplicas, usage, nil }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
注意
tolerance一般取0.1作为容忍,避免小幅度波动导致的频繁扩缩容,此外dsr副本计算会检查扩缩容方向,避免误建或误删
# 1.3.resourceReplicas
a.GetResourceReplicas()基于metrics及资源利用率计算目标副本,检查扩缩方向及副本的合法性,避免方向错误或metric缺失导致的误判。// GetResourceReplicas calculates the desired replica count based on a target resource utilization percent. func (c *ReplicaCalculator) GetResourceReplicas(...) (...) { // 获取Pod/Container metric metrics, timestamp, err := c.metricsClient.GetResourceMetric(ctx, resource, namespace, selector, container) ... // 获取Pod podList, err := c.podLister.Pods(namespace).List(selector) ... // 拆分为ready、unready、missing及igngore readyPodCount, unreadyPods, missingPods, ignoredPods := groupPods(podList, metrics, resource, c.cpuInitializationPeriod, c.delayOfInitialReadinessStatus) // 清理ignorePod/unreadyPod metric removeMetricsForPods(metrics, ignoredPods) removeMetricsForPods(metrics, unreadyPods) ... // 获取各pod的resource request requests, err := calculatePodRequests(podList, container, resource) ... // usageRatio = (metricsTotal*100/requestsTotal)/targetUtilization // utilization = metricsTotal*100/requestsTotal // rawUtilization = metricsTotal/podSize usageRatio, utilization = , rawUtilization, err := metricsclient.GetResourceUtilizationRatio(metrics, requests, targetUtilization) ... // 没有unready Pod或非扩容+所有Pod都有指标 if !scaleUpWithUnready && len(missingPods) == 0 { // 容忍度内 if math.Abs(1.0-usageRatio) <= c.tolerance { // return the current replicas if the change would be too small return currentReplicas, utilization, rawUtilization, timestamp, nil } // dsr = usageRatio*readyPodCount return int32(math.Ceil(usageRatio * float64(readyPodCount))),utilization, rawUtilization, timestamp, nil } // 部分Pod没有metric if len(missingPods) > 0 { // 缩容 if usageRatio < 1.0 { // missingPod metric重置 fallbackUtilization := int64(max(100, targetUtilization)) for podName := range missingPods { metrics[podName] = metricsclient.PodMetric{Value: requests[podName] * fallbackUtilization / 100} } // 扩容 } else if usageRatio > 1.0 { // missingPod metric重置为0 for podName := range missingPods { metrics[podName] = metricsclient.PodMetric{Value: 0} } } } // 带unready扩容 if scaleUpWithUnready { // unreadyPod metric重置为0 for podName := range unreadyPods { metrics[podName] = metricsclient.PodMetric{Value: 0} } } // newUsageRatio = (metricsTotal*100/requestsTotal)/targetUtilization newUsageRatio, _, _, err := metricsclient.GetResourceUtilizationRatio(metrics, requests, targetUtilization) ... // 容忍度内 || 扩缩容方向变化 if math.Abs(1.0-newUsageRatio) <= c.tolerance || (usageRatio < 1.0 && newUsageRatio > 1.0) || (usageRatio > 1.0 && newUsageRatio < 1.0) { return currentReplicas, utilization, rawUtilization, timestamp, nil } // dsr = newUsageRatio*len(metrics) newReplicas := int32(math.Ceil(newUsageRatio * float64(len(metrics)))) // 扩缩容方向非法 if (newUsageRatio < 1.0 && newReplicas > currentReplicas) || (newUsageRatio > 1.0 && newReplicas < currentReplicas) { // return the current replicas if the change of metrics length would cause a change in scale direction return currentReplicas, utilization, rawUtilization, timestamp, nil } return newReplicas, utilization, rawUtilization, timestamp, nil }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
注意
a.GetResourceReplicas()与a.GetRawResourceReplicas()类似,只是加入Pod Requests计算usageRatio
# 2.副本调整
# 2.1.stabilize
a.normalizeDesiredReplicas()负责对计算的期望副本进行规范化处理,驱动期望副本满足自动伸缩约束条件,确保扩缩容的副本出于合法边界。// normalizeDesiredReplicas takes the metrics desired replicas value and normalizes it. func (a *HorizontalController) normalizeDesiredReplicas(...) int32 { // 基于窗口防抖检查 stabilizedRecommendation := a.stabilizeRecommendation(key, prenormalizedDesiredReplicas) // 标记ableToScale condition是否触发防抖 if stabilizedRecommendation != prenormalizedDesiredReplicas { setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ScaleDownStabilized", "recent recommendations were higher than current one, applying the highest recent recommendation") } else { setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ReadyForNewScale", "recommended size matches current size") } // 约束检查 desiredReplicas, condition, reason := convertDesiredReplicasWithRules(currentReplicas, stabilizedRecommendation, minReplicas, hpa.Spec.MaxReplicas) // 标记scalingLimited condition是否触发约束 if desiredReplicas == stabilizedRecommendation { setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionFalse, condition, reason) } else { setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionTrue, condition, reason) } return desiredReplicas } // stabilizeRecommendation: // - replaces old recommendation with the newest recommendation, // - returns max of recommendations that are not older than downscaleStabilisationWindow. func (a *HorizontalController) stabilizeRecommendation(key string, prenormalizedDesiredReplicas int32) int32 { // 临时将期望副本作为窗口最大值 maxRecommendation := prenormalizedDesiredReplicas ... // 窗口起始时间 cutoff := time.Now().Add(-a.downscaleStabilisationWindow) a.recommendationsLock.Lock() defer a.recommendationsLock.Unlock() // 遍历历史推荐 for i, rec := range a.recommendations[key] { // 窗口内推荐过期 if rec.timestamp.Before(cutoff) { foundOldSample = true oldSampleIndex = i // 未过期且找到更大的,更新 } else if rec.recommendation > maxRecommendation { maxRecommendation = rec.recommendation } } // 替换窗口外不需要的旧数据为最新一次的推荐值 if foundOldSample { a.recommendations[key][oldSampleIndex] = timestampedRecommendation{prenormalizedDesiredReplicas, time.Now()} // 申请新的槽位 } else { a.recommendations[key] = append(a.recommendations[key], timestampedRecommendation{prenormalizedDesiredReplicas, time.Now()}) } return maxRecommendation } // convertDesiredReplicas performs the actual normalization. func convertDesiredReplicasWithRules(cur, dsr, min, max int32) (int32, string, string) { ... minimumAllowedReplicas = hpaMinReplicas // 扩容上限=max(2*cur,4) scaleUpLimit := calculateScaleUpLimit(currentReplicas) // maxReplicas超出上限 if hpaMaxReplicas > scaleUpLimit { // 重置maxReplicas为上限 maximumAllowedReplicas = scaleUpLimit possibleLimitingCondition = "ScaleUpLimit" possibleLimitingReason = "the desired replica count is increasing faster than the maximum scale rate" // 未超出 } else { // maxReplicas设为hpa要求最大副本 maximumAllowedReplicas = hpaMaxReplicas possibleLimitingCondition = "TooManyReplicas" possibleLimitingReason = "the desired replica count is more than the maximum replica count" } // dsr低于最小副本 if desiredReplicas < minimumAllowedReplicas { possibleLimitingCondition = "TooFewReplicas" possibleLimitingReason = "the desired replica count is less than the minimum replica count" // 重置dsr为minReplicas return minimumAllowedReplicas, possibleLimitingCondition, possibleLimitingReason // dsr超出maxReplicas } else if desiredReplicas > maximumAllowedReplicas { // 重置为maxReplicas return maximumAllowedReplicas, possibleLimitingCondition, possibleLimitingReason } // 合法边界,保留dsr return desiredReplicas, "DesiredWithinRange", "the desired count is within the acceptable range" }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
注意
a.normalizeDesiredReplicas()基于内置行为约束dsr副本边界,确保扩缩容的目标副本合法性
# 2.2.behavior
a.normalizeDesiredReplicasWithBehaviors()基于自定义的behavior约束目标副本,确保扩缩容的副本满足预期边界。// normalizeDesiredReplicasWithBehaviors takes the metrics desired replicas value and normalizes it. func (a *HorizontalController) normalizeDesiredReplicasWithBehaviors(...) int32 { // 初始化behavior缩容窗口为5min a.maybeInitScaleDownStabilizationWindow(hpa) ... stabilizedRecommendation, reason, message := a.stabilizeRecommendationWithBehaviors(normalizationArg) // 调整期望副本数 normalizationArg.DesiredReplicas = stabilizedRecommendation // 标记是否触发防抖 if stabilizedRecommendation != prenormalizedDesiredReplicas { // "ScaleUpStabilized" || "ScaleDownStabilized" setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, reason, message) } else { setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ReadyForNewScale", "recommended size matches current size") } // 边界约束 desiredReplicas, reason, message := a.convertDesiredReplicasWithBehaviorRate(normalizationArg) // 标记scalingLimited condition是否触发约束 if desiredReplicas == stabilizedRecommendation { setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionFalse, reason, message) } else { setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionTrue, reason, message) } return desiredReplicas } // stabilizeRecommendationWithBehaviors: // - replaces old recommendation with the newest recommendation, // - returns {max,min} of recommendations that are not older than constraints.Scale{Up,Down}.DelaySeconds func (a *HorizontalController) stabilizeRecommendationWithBehaviors(args NormalizationArg) (...) { ... // 扩容相关 upRecommendation := args.DesiredReplicas upDelaySeconds := *args.ScaleUpBehavior.StabilizationWindowSeconds upCutoff := now.Add(-time.Second * time.Duration(upDelaySeconds)) // 缩容相关 downRecommendation := args.DesiredReplicas downDelaySeconds := *args.ScaleDownBehavior.StabilizationWindowSeconds downCutoff := now.Add(-time.Second * time.Duration(downDelaySeconds)) a.recommendationsLock.Lock() defer a.recommendationsLock.Unlock() // 遍历历史推荐 for i, rec := range a.recommendations[args.Key] { // 扩容取历史推荐窗口最小值——保守扩容 if rec.timestamp.After(upCutoff) { upRecommendation = min(rec.recommendation, upRecommendation) } // 缩容取历史推荐窗口的最大值——避免骤降 if rec.timestamp.After(downCutoff) { // 缩容期望副本数取最大 downRecommendation = max(rec.recommendation, downRecommendation) } // 历史推荐过期,标记复用 if rec.timestamp.Before(upCutoff) && rec.timestamp.Before(downCutoff) { foundOldSample = true oldSampleIndex = i } } recommendation := args.CurrentReplicas // 扩容向上取到upRecommendation if recommendation < upRecommendation { recommendation = upRecommendation } // 缩容向下取到downRecommendation if recommendation > downRecommendation { recommendation = downRecommendation } // 记录最新一次期望值 if foundOldSample { a.recommendations[args.Key][oldSampleIndex] = timestampedRecommendation{args.DesiredReplicas, time.Now()} } else { a.recommendations[args.Key] = append(a.recommendations[args.Key], timestampedRecommendation{args.DesiredReplicas, time.Now()}) } ... return recommendation, reason, message } // convertDesiredReplicasWithBehaviorRate performs the actual normalization. func (a *HorizontalController) convertDesiredReplicasWithBehaviorRate(args NormalizationArg) (...) { ... // 扩容行为 if args.DesiredReplicas > args.CurrentReplicas { ... // 计算扩容上限 scaleUpLimit := calculateScaleUpLimitWithScalingRules(args.CurrentReplicas, a.scaleUpEvents[args.Key], a.scaleDownEvents[args.Key], args.ScaleUpBehavior) // 扩容上限低于当前副本,历史时间未清理,不允许扩容 if scaleUpLimit < args.CurrentReplicas { // We shouldn't scale up further until the scaleUpEvents will be cleaned up scaleUpLimit = args.CurrentReplicas } maximumAllowedReplicas := args.MaxReplicas // 修正最大允许副本 if maximumAllowedReplicas > scaleUpLimit { maximumAllowedReplicas = scaleUpLimit possibleLimitingReason = "ScaleUpLimit" possibleLimitingMessage = "the desired replica count is increasing faster than the maximum scale rate" } else { possibleLimitingReason = "TooManyReplicas" possibleLimitingMessage = "the desired replica count is more than the maximum replica count" } // 预期副本超出最大允许副本,裁剪多余部分 if args.DesiredReplicas > maximumAllowedReplicas { return maximumAllowedReplicas, possibleLimitingReason, possibleLimitingMessage } // 缩容行为 } else if args.DesiredReplicas < args.CurrentReplicas { ... // 计算缩容下限 scaleDownLimit := calculateScaleDownLimitWithBehaviors(args.CurrentReplicas, a.scaleUpEvents[args.Key], a.scaleDownEvents[args.Key], args.ScaleDownBehavior) // 缩容下限不能超出当前副本 if scaleDownLimit > args.CurrentReplicas { // We shouldn't scale down further until the scaleDownEvents will be cleaned up scaleDownLimit = args.CurrentReplicas } minimumAllowedReplicas := args.MinReplicas // 修正最小允许副本 if minimumAllowedReplicas < scaleDownLimit { minimumAllowedReplicas = scaleDownLimit possibleLimitingReason = "ScaleDownLimit" possibleLimitingMessage = "the desired replica count is decreasing faster than the maximum scale rate" } else { possibleLimitingMessage = "the desired replica count is less than the minimum replica count" possibleLimitingReason = "TooFewReplicas" } // 期望副本不能低于最小允许副本 if args.DesiredReplicas < minimumAllowedReplicas { return minimumAllowedReplicas, possibleLimitingReason, possibleLimitingMessage } } return args.DesiredReplicas, "DesiredWithinRange", "the desired count is within the acceptable range" }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
注意
a.normalizeDesiredReplicasWithBehaviors()基于历史推荐计算初始预期副本,进一步利用历史事件计算扩缩容上限,实现预期副本修正
# 2.3.scaleLimit
calculateScale{Up/Down}LimitWithScalingRules()负责基于历史事件计算扩缩容上限,进一步实现预期副本修正,缓解扩缩容行为的抖动。// calculateScaleUpLimitWithScalingRules returns the maximum number of pods that could be added for given hpa. func calculateScaleUpLimitWithScalingRules(...) int32 { ... // 禁止扩容 if *scalingRules.SelectPolicy == autoscalingv2.DisabledPolicySelect { return currentReplicas // 选最小的预期 } else if *scalingRules.SelectPolicy == autoscalingv2.MinChangePolicySelect { result = math.MaxInt32 selectPolicyFn = min // For scaling up, the lowest change ('min' policy) produces a minimum value // 选最大的预期 } else { result = math.MinInt32 selectPolicyFn = max // Use the default policy otherwise to produce a highest possible change } // 遍历Policy for _, policy := range scalingRules.Policies { // 过去N秒扩了多少 replicasAddedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleUpEvents) // 过去N秒缩了多少 replicasDeletedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleDownEvents) // period时刻副本数 periodStartReplicas := currentReplicas - replicasAddedInCurrentPeriod + replicasDeletedInCurrentPeriod // 值策略 if policy.Type == autoscalingv2.PodsScalingPolicy { // 允许扩容的副本累加到periodStartReplicas proposed = periodStartReplicas + policy.Value // 百分比扩容 } else if policy.Type == autoscalingv2.PercentScalingPolicy { //基于百分比调整periodStartReplicas proposed = int32(math.Ceil(float64(periodStartReplicas) * (1 + float64(policy.Value)/100))) } // 取最小或最大 result = selectPolicyFn(result, proposed) } return result } // calculateScaleDownLimitWithBehavior returns the maximum number of pods that could be deleted for given hpa. func calculateScaleDownLimitWithBehaviors(...) int32 { ... // 禁止缩容 if *scalingRules.SelectPolicy == autoscalingv2.DisabledPolicySelect { return currentReplicas // 选最小的预期 } else if *scalingRules.SelectPolicy == autoscalingv2.MinChangePolicySelect { result = math.MinInt32 selectPolicyFn = max // 取最大的预期 } else { result = math.MaxInt32 selectPolicyFn = min } // 遍历Policy for _, policy := range scalingRules.Policies { // 过去N秒扩容副本 replicasAddedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleUpEvents) // 过去N秒缩容副本 replicasDeletedInCurrentPeriod := getReplicasChangePerPeriod(policy.PeriodSeconds, scaleDownEvents) // period时刻副本数 periodStartReplicas := currentReplicas - replicasAddedInCurrentPeriod + replicasDeletedInCurrentPeriod // 值缩容 if policy.Type == autoscalingv2.PodsScalingPolicy { // 直接扣减 proposed = periodStartReplicas - policy.Value // 百分比缩容 } else if policy.Type == autoscalingv2.PercentScalingPolicy { // 基于百分比调整periodStartReplicas proposed = int32(float64(periodStartReplicas) * (1 - float64(policy.Value)/100)) } // 取最小或最大预期 result = selectPolicyFn(result, proposed) } return result }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
注意
scaleLimit计算的是扩缩容上下限,基于上下限修正预期副本