Skip to content

Commit b19ef3d

Browse files
authored
Merge pull request kubernetes#2866 from bskiba/extensible-prio-new
refactor updater for a better structured update priority processing
2 parents 554b72c + 50a24c4 commit b19ef3d

File tree

8 files changed

+611
-312
lines changed

8 files changed

+611
-312
lines changed

vertical-pod-autoscaler/pkg/updater/logic/updater.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type updater struct {
5858
evictionFactory eviction.PodsEvictionRestrictionFactory
5959
recommendationProcessor vpa_api_util.RecommendationProcessor
6060
evictionAdmission priority.PodEvictionAdmission
61+
priorityProcessor priority.PriorityProcessor
6162
evictionRateLimiter *rate.Limiter
6263
selectorFetcher target.VpaTargetSelectorFetcher
6364
useAdmissionControllerStatus bool
@@ -76,6 +77,7 @@ func NewUpdater(
7677
recommendationProcessor vpa_api_util.RecommendationProcessor,
7778
evictionAdmission priority.PodEvictionAdmission,
7879
selectorFetcher target.VpaTargetSelectorFetcher,
80+
priorityProcessor priority.PriorityProcessor,
7981
) (Updater, error) {
8082
evictionRateLimiter := getRateLimiter(evictionRateLimit, evictionRateBurst)
8183
factory, err := eviction.NewPodsEvictionRestrictionFactory(kubeClient, minReplicasForEvicition, evictionToleranceFraction)
@@ -90,6 +92,7 @@ func NewUpdater(
9092
recommendationProcessor: recommendationProcessor,
9193
evictionRateLimiter: evictionRateLimiter,
9294
evictionAdmission: evictionAdmission,
95+
priorityProcessor: priorityProcessor,
9396
selectorFetcher: selectorFetcher,
9497
useAdmissionControllerStatus: useAdmissionControllerStatus,
9598
statusValidator: status.NewValidator(
@@ -212,11 +215,14 @@ func getRateLimiter(evictionRateLimit float64, evictionRateLimitBurst int) *rate
212215

213216
// getPodsUpdateOrder returns list of pods that should be updated ordered by update priority
214217
func (u *updater) getPodsUpdateOrder(pods []*apiv1.Pod, vpa *vpa_types.VerticalPodAutoscaler) []*apiv1.Pod {
215-
priorityCalculator := priority.NewUpdatePriorityCalculator(vpa.Spec.ResourcePolicy, vpa.Status.Conditions, nil, u.recommendationProcessor)
216-
recommendation := vpa.Status.Recommendation
218+
priorityCalculator := priority.NewUpdatePriorityCalculator(
219+
vpa,
220+
nil,
221+
u.recommendationProcessor,
222+
u.priorityProcessor)
217223

218224
for _, pod := range pods {
219-
priorityCalculator.AddPod(pod, recommendation, time.Now())
225+
priorityCalculator.AddPod(pod, time.Now())
220226
}
221227

222228
return priorityCalculator.GetSortedPods(u.evictionAdmission)

vertical-pod-autoscaler/pkg/updater/logic/updater_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
3333
target_mock "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/mock"
3434
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/eviction"
35+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/priority"
3536
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status"
3637
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test"
3738
)
@@ -178,6 +179,7 @@ func testRunOnceBase(
178179
selectorFetcher: mockSelectorFetcher,
179180
useAdmissionControllerStatus: true,
180181
statusValidator: statusValidator,
182+
priorityProcessor: priority.NewProcessor(),
181183
}
182184

183185
if expectFetchCalls {

vertical-pod-autoscaler/pkg/updater/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
2626
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
2727
updater "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/logic"
28+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/updater/priority"
2829
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
2930
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics"
3031
metrics_updater "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/updater"
@@ -97,6 +98,7 @@ func main() {
9798
vpa_api_util.NewCappingRecommendationProcessor(limitRangeCalculator),
9899
nil,
99100
targetSelectorFetcher,
101+
priority.NewProcessor(),
100102
)
101103
if err != nil {
102104
klog.Fatalf("Failed to create updater: %v", err)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package priority
18+
19+
import (
20+
"math"
21+
22+
apiv1 "k8s.io/api/core/v1"
23+
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
24+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/annotations"
25+
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
26+
"k8s.io/klog"
27+
)
28+
29+
// PriorityProcessor calculates priority for pod updates.
30+
type PriorityProcessor interface {
31+
GetUpdatePriority(pod *apiv1.Pod, vpa *vpa_types.VerticalPodAutoscaler,
32+
recommendation *vpa_types.RecommendedPodResources) podPriority
33+
}
34+
35+
// NewProcessor creates a new default PriorityProcessor.
36+
func NewProcessor() PriorityProcessor {
37+
return &defaultPriorityProcessor{}
38+
}
39+
40+
type defaultPriorityProcessor struct {
41+
}
42+
43+
func (*defaultPriorityProcessor) GetUpdatePriority(pod *apiv1.Pod, _ *vpa_types.VerticalPodAutoscaler,
44+
recommendation *vpa_types.RecommendedPodResources) podPriority {
45+
outsideRecommendedRange := false
46+
scaleUp := false
47+
// Sum of requests over all containers, per resource type.
48+
totalRequestPerResource := make(map[apiv1.ResourceName]int64)
49+
// Sum of recommendations over all containers, per resource type.
50+
totalRecommendedPerResource := make(map[apiv1.ResourceName]int64)
51+
52+
hasObservedContainers, vpaContainerSet := parseVpaObservedContainers(pod)
53+
54+
for _, podContainer := range pod.Spec.Containers {
55+
if hasObservedContainers && !vpaContainerSet.Has(podContainer.Name) {
56+
klog.V(4).Infof("Not listed in %s:%s. Skipping container %s priority calculations",
57+
annotations.VpaObservedContainersLabel, pod.GetAnnotations()[annotations.VpaObservedContainersLabel], podContainer.Name)
58+
continue
59+
}
60+
recommendedRequest := vpa_api_util.GetRecommendationForContainer(podContainer.Name, recommendation)
61+
if recommendedRequest == nil {
62+
continue
63+
}
64+
for resourceName, recommended := range recommendedRequest.Target {
65+
totalRecommendedPerResource[resourceName] += recommended.MilliValue()
66+
lowerBound, hasLowerBound := recommendedRequest.LowerBound[resourceName]
67+
upperBound, hasUpperBound := recommendedRequest.UpperBound[resourceName]
68+
if request, hasRequest := podContainer.Resources.Requests[resourceName]; hasRequest {
69+
totalRequestPerResource[resourceName] += request.MilliValue()
70+
if recommended.MilliValue() > request.MilliValue() {
71+
scaleUp = true
72+
}
73+
if (hasLowerBound && request.Cmp(lowerBound) < 0) ||
74+
(hasUpperBound && request.Cmp(upperBound) > 0) {
75+
outsideRecommendedRange = true
76+
}
77+
} else {
78+
// Note: if the request is not specified, the container will use the
79+
// namespace default request. Currently we ignore it and treat such
80+
// containers as if they had 0 request. A more correct approach would
81+
// be to always calculate the 'effective' request.
82+
scaleUp = true
83+
outsideRecommendedRange = true
84+
}
85+
}
86+
}
87+
resourceDiff := 0.0
88+
for resource, totalRecommended := range totalRecommendedPerResource {
89+
totalRequest := math.Max(float64(totalRequestPerResource[resource]), 1.0)
90+
resourceDiff += math.Abs(totalRequest-float64(totalRecommended)) / totalRequest
91+
}
92+
return podPriority{
93+
outsideRecommendedRange: outsideRecommendedRange,
94+
scaleUp: scaleUp,
95+
resourceDiff: resourceDiff,
96+
}
97+
}

0 commit comments

Comments
 (0)