Skip to content

Commit

Permalink
Merge pull request #6556 from damikag/binpacking-time-limiter
Browse files Browse the repository at this point in the history
implement time limiter for binpacking
  • Loading branch information
k8s-ci-robot authored Jun 17, 2024
2 parents 60ddf7a + 8a6822d commit 160af8b
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 28 deletions.
3 changes: 3 additions & 0 deletions cluster-autoscaler/config/autoscaling_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ type AutoscalingOptions struct {
// MaxNodeGroupBinpackingDuration is a maximum time that can be spent binpacking a single NodeGroup. If the threshold
// is exceeded binpacking will be cut short and a partial scale-up will be performed.
MaxNodeGroupBinpackingDuration time.Duration
// MaxBinpackingTime is the 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.
MaxBinpackingTime time.Duration
// NodeDeletionBatcherInterval is a time for how long CA ScaleDown gather nodes to delete them in batch.
NodeDeletionBatcherInterval time.Duration
// SkipNodesWithSystemPods tells if nodes with pods from kube-system should be deleted (except for DaemonSet or mirror pods)
Expand Down
2 changes: 1 addition & 1 deletion cluster-autoscaler/core/test/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func NewTestProcessors(context *context.AutoscalingContext) *processors.Autoscal
return &processors.AutoscalingProcessors{
PodListProcessor: podlistprocessor.NewDefaultPodListProcessor(context.PredicateChecker),
NodeGroupListProcessor: &nodegroups.NoOpNodeGroupListProcessor{},
BinpackingLimiter: binpacking.NewDefaultBinpackingLimiter(),
BinpackingLimiter: binpacking.NewTimeLimiter(context.MaxNodeGroupBinpackingDuration),
NodeGroupSetProcessor: nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}),
ScaleDownSetProcessor: nodes.NewCompositeScaleDownSetProcessor([]nodes.ScaleDownSetProcessor{
nodes.NewMaxNodesProcessor(),
Expand Down
2 changes: 2 additions & 0 deletions cluster-autoscaler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ var (
writeStatusConfigMapFlag = flag.Bool("write-status-configmap", true, "Should CA write status information to a configmap")
statusConfigMapName = flag.String("status-config-map-name", "cluster-autoscaler-status", "Status configmap name")
maxInactivityTimeFlag = flag.Duration("max-inactivity", 10*time.Minute, "Maximum time from last recorded autoscaler activity before automatic restart")
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.")
maxFailingTimeFlag = flag.Duration("max-failing-time", 15*time.Minute, "Maximum time from last recorded successful autoscaler run before automatic restart")
balanceSimilarNodeGroupsFlag = flag.Bool("balance-similar-node-groups", false, "Detect similar node groups and balance the number of nodes between them")
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.")
Expand Down Expand Up @@ -419,6 +420,7 @@ func createAutoscalingOptions() config.AutoscalingOptions {
RecordDuplicatedEvents: *recordDuplicatedEvents,
MaxNodesPerScaleUp: *maxNodesPerScaleUp,
MaxNodeGroupBinpackingDuration: *maxNodeGroupBinpackingDuration,
MaxBinpackingTime: *maxBinpackingTimeFlag,
NodeDeletionBatcherInterval: *nodeDeletionBatcherInterval,
SkipNodesWithSystemPods: *skipNodesWithSystemPods,
SkipNodesWithLocalStorage: *skipNodesWithLocalStorage,
Expand Down
26 changes: 0 additions & 26 deletions cluster-autoscaler/processors/binpacking/binpacking_limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,3 @@ type BinpackingLimiter interface {
StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool
FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option)
}

// NoOpBinpackingLimiter returns true without processing expansion options.
type NoOpBinpackingLimiter struct {
}

// NewDefaultBinpackingLimiter creates an instance of NoOpBinpackingLimiter.
func NewDefaultBinpackingLimiter() BinpackingLimiter {
return &NoOpBinpackingLimiter{}
}

// InitBinpacking initialises the BinpackingLimiter.
func (p *NoOpBinpackingLimiter) InitBinpacking(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup) {
}

// MarkProcessed marks the nodegroup as processed.
func (p *NoOpBinpackingLimiter) MarkProcessed(context *context.AutoscalingContext, nodegroupId string) {
}

// StopBinpacking is used to make decsions on the evaluated expansion options.
func (p *NoOpBinpackingLimiter) StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool {
return false
}

// FinalizeBinpacking is called to finalize the BinpackingLimiter.
func (p *NoOpBinpackingLimiter) FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option) {
}
66 changes: 66 additions & 0 deletions cluster-autoscaler/processors/binpacking/combined_limiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package binpacking

import (
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
"k8s.io/autoscaler/cluster-autoscaler/context"
"k8s.io/autoscaler/cluster-autoscaler/expander"
)

// CombinedLimiter combines the outcome of multiple limiters. It will limit
// binpacking when at least one limiter meets the stop condition.
type CombinedLimiter struct {
limiters []BinpackingLimiter
}

// NewCombinedLimiter returns an instance of a new CombinedLimiter.
func NewCombinedLimiter(limiters []BinpackingLimiter) *CombinedLimiter {
return &CombinedLimiter{
limiters: limiters,
}
}

// InitBinpacking initialises all the underline limiters.
func (l *CombinedLimiter) InitBinpacking(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup) {
for _, limiter := range l.limiters {
limiter.InitBinpacking(context, nodeGroups)
}
}

// MarkProcessed marks the nodegroup as processed in all underline limiters.
func (l *CombinedLimiter) MarkProcessed(context *context.AutoscalingContext, nodegroupId string) {
for _, limiter := range l.limiters {
limiter.MarkProcessed(context, nodegroupId)
}
}

// StopBinpacking returns true if at least one of the underline limiter met the stop condition.
func (l *CombinedLimiter) StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool {
stopCondition := false
for _, limiter := range l.limiters {
stopCondition = limiter.StopBinpacking(context, evaluatedOptions) || stopCondition
}
return stopCondition
}

// FinalizeBinpacking will call FinalizeBinpacking for all the underline limiters.
func (l *CombinedLimiter) FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option) {
for _, limiter := range l.limiters {
limiter.FinalizeBinpacking(context, finalOptions)
}
}
61 changes: 61 additions & 0 deletions cluster-autoscaler/processors/binpacking/time_limiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package binpacking

import (
"time"

"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
"k8s.io/autoscaler/cluster-autoscaler/context"
"k8s.io/autoscaler/cluster-autoscaler/expander"
"k8s.io/klog/v2"
)

// TimeLimiter limits binpacking based on the total time spends on binpacking.
type TimeLimiter struct {
startTime time.Time
maxBinpackingDuration time.Duration
}

// NewTimeLimiter returns an instance of a new TimeLimiter.
func NewTimeLimiter(maxBinpackingDuration time.Duration) *TimeLimiter {
return &TimeLimiter{
maxBinpackingDuration: maxBinpackingDuration,
}
}

// InitBinpacking initialises the TimeLimiter.
func (b *TimeLimiter) InitBinpacking(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup) {
b.startTime = time.Now()
}

// MarkProcessed marks the nodegroup as processed.
func (b *TimeLimiter) MarkProcessed(context *context.AutoscalingContext, nodegroupId string) {
}

// StopBinpacking returns true if the binpacking time exceeds maxBinpackingDuration.
func (b *TimeLimiter) StopBinpacking(context *context.AutoscalingContext, evaluatedOptions []expander.Option) bool {
now := time.Now()
if now.After(b.startTime.Add(b.maxBinpackingDuration)) {
klog.Infof("Binpacking is cut short after %v seconds due to exceeding maxBinpackingDuration", now.Sub(b.startTime).Seconds())
}
return false
}

// FinalizeBinpacking is called to finalize the BinpackingLimiter.
func (b *TimeLimiter) FinalizeBinpacking(context *context.AutoscalingContext, finalOptions []expander.Option) {
}
2 changes: 1 addition & 1 deletion cluster-autoscaler/processors/processors.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func DefaultProcessors(options config.AutoscalingOptions) *AutoscalingProcessors
return &AutoscalingProcessors{
PodListProcessor: pods.NewDefaultPodListProcessor(),
NodeGroupListProcessor: nodegroups.NewDefaultNodeGroupListProcessor(),
BinpackingLimiter: binpacking.NewDefaultBinpackingLimiter(),
BinpackingLimiter: binpacking.NewTimeLimiter(options.MaxBinpackingTime),
NodeGroupSetProcessor: nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{
MaxAllocatableDifferenceRatio: config.DefaultMaxAllocatableDifferenceRatio,
MaxCapacityMemoryDifferenceRatio: config.DefaultMaxCapacityMemoryDifferenceRatio,
Expand Down

0 comments on commit 160af8b

Please sign in to comment.