Skip to content

Commit 51d1c2f

Browse files
committed
implement time limiter for binpacking
1 parent 6ca8414 commit 51d1c2f

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

cluster-autoscaler/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/actuation"
3232
"k8s.io/autoscaler/cluster-autoscaler/core/scaleup/orchestrator"
3333
"k8s.io/autoscaler/cluster-autoscaler/debuggingsnapshot"
34+
"k8s.io/autoscaler/cluster-autoscaler/processors/binpacking"
3435
"k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/checkcapacity"
3536
"k8s.io/autoscaler/cluster-autoscaler/simulator/predicatechecker"
3637
kubelet_config "k8s.io/kubernetes/pkg/kubelet/apis/config"
@@ -196,6 +197,7 @@ var (
196197
writeStatusConfigMapFlag = flag.Bool("write-status-configmap", true, "Should CA write status information to a configmap")
197198
statusConfigMapName = flag.String("status-config-map-name", "cluster-autoscaler-status", "Status configmap name")
198199
maxInactivityTimeFlag = flag.Duration("max-inactivity", 10*time.Minute, "Maximum time from last recorded autoscaler activity before automatic restart")
200+
maxBinpackingTimeFlag = flag.Duration("max-binpacking-time", 5*time.Minute, "Maximum time spend on binpacking for a single scale-up. If binpacking is limited by this, scale-up will continue with the already calculated scale-up options.")
199201
maxFailingTimeFlag = flag.Duration("max-failing-time", 15*time.Minute, "Maximum time from last recorded successful autoscaler run before automatic restart")
200202
balanceSimilarNodeGroupsFlag = flag.Bool("balance-similar-node-groups", false, "Detect similar node groups and balance the number of nodes between them")
201203
nodeAutoprovisioningEnabled = flag.Bool("node-autoprovisioning-enabled", false, "Should CA autoprovision node groups when needed.This flag is deprecated and will be removed in future releases.")
@@ -487,6 +489,7 @@ func buildAutoscaler(debuggingSnapshotter debuggingsnapshot.DebuggingSnapshotter
487489

488490
opts.Processors = ca_processors.DefaultProcessors(autoscalingOptions)
489491
opts.Processors.TemplateNodeInfoProvider = nodeinfosprovider.NewDefaultTemplateNodeInfoProvider(nodeInfoCacheExpireTime, *forceDaemonSets)
492+
opts.Processors.BinpackingLimiter = binpacking.NewCombinedLimiter([]binpacking.BinpackingLimiter{opts.Processors.BinpackingLimiter, binpacking.NewTimeLimiter(*maxBinpackingTimeFlag)})
490493
podListProcessor := podlistprocessor.NewDefaultPodListProcessor(opts.PredicateChecker)
491494

492495
if autoscalingOptions.ProvisioningRequestEnabled {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright 2023 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 binpacking
18+
19+
import (
20+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
21+
"k8s.io/autoscaler/cluster-autoscaler/context"
22+
"k8s.io/autoscaler/cluster-autoscaler/expander"
23+
)
24+
25+
// CombinedLimiter combines the outcome of multiple limiters. It will limit
26+
// binpacking when at least one limiter meets the stop condition.
27+
type CombinedLimiter struct {
28+
limiters []BinpackingLimiter
29+
}
30+
31+
// NewCombinedLimiter returns an instance of a new CombinedLimiter.
32+
func NewCombinedLimiter(limiters []BinpackingLimiter) *CombinedLimiter {
33+
return &CombinedLimiter{
34+
limiters: limiters,
35+
}
36+
}
37+
38+
// InitBinpacking initialises all the underline limiters.
39+
func (l *CombinedLimiter) InitBinpacking(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup) {
40+
for _, limiter := range l.limiters {
41+
limiter.InitBinpacking(context, nodeGroups)
42+
}
43+
}
44+
45+
// MarkProcessed marks the nodegroup as processed in all underline limiters.
46+
func (l *CombinedLimiter) MarkProcessed(context *context.AutoscalingContext, nodegroupId string) {
47+
for _, limiter := range l.limiters {
48+
limiter.MarkProcessed(context, nodegroupId)
49+
}
50+
}
51+
52+
// StopBinpacking returns true if at least one of the underline limiter met the stop condition.
53+
func (l *CombinedLimiter) StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool {
54+
stopCondition := false
55+
for _, limiter := range l.limiters {
56+
stopCondition = limiter.StopBinpacking(context, evaluatedOptions) || stopCondition
57+
}
58+
return stopCondition
59+
}
60+
61+
// FinalizeBinpacking will call FinalizeBinpacking for all the underline limiters.
62+
func (l *CombinedLimiter) FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option) {
63+
for _, limiter := range l.limiters {
64+
limiter.FinalizeBinpacking(context, finalOptions)
65+
}
66+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Copyright 2023 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 binpacking
18+
19+
import (
20+
"time"
21+
22+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
23+
"k8s.io/autoscaler/cluster-autoscaler/context"
24+
"k8s.io/autoscaler/cluster-autoscaler/expander"
25+
"k8s.io/klog/v2"
26+
)
27+
28+
// TimeLimiter limits binpacking based on the total time spends on binpacking.
29+
type TimeLimiter struct {
30+
startTime time.Time
31+
maxBinpackingDuration time.Duration
32+
}
33+
34+
// NewTimeLimiter returns an instance of a new TimeLimiter.
35+
func NewTimeLimiter(maxBinpackingDuration time.Duration) *TimeLimiter {
36+
return &TimeLimiter{
37+
maxBinpackingDuration: maxBinpackingDuration,
38+
}
39+
}
40+
41+
// InitBinpacking initialises the TimeLimiter.
42+
func (b *TimeLimiter) InitBinpacking(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup) {
43+
b.startTime = time.Now()
44+
}
45+
46+
// MarkProcessed marks the nodegroup as processed.
47+
func (b *TimeLimiter) MarkProcessed(context *context.AutoscalingContext, nodegroupId string) {
48+
}
49+
50+
// StopBinpacking returns true if the binpacking time exceeds maxBinpackingDuration.
51+
func (b *TimeLimiter) StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool {
52+
if time.Now().After(b.startTime.Add(b.maxBinpackingDuration)) {
53+
klog.Info("Binpacking is cut short due to maxBinpackingDuration reached.")
54+
return true
55+
}
56+
return false
57+
}
58+
59+
// FinalizeBinpacking is called to finalize the BinpackingLimiter.
60+
func (b *TimeLimiter) FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option) {
61+
}

0 commit comments

Comments
 (0)