Configurable difference ratios
This commit is contained in:
		
							parent
							
								
									3b2e3db941
								
							
						
					
					
						commit
						0f8ed0b81f
					
				|  | @ -46,6 +46,33 @@ type NodeGroupAutoscalingOptions struct { | |||
| 	ScaleDownUnreadyTime time.Duration | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	// DefaultMaxAllocatableDifferenceRatio describes how Node.Status.Allocatable can differ between groups in the same NodeGroupSet
 | ||||
| 	DefaultMaxAllocatableDifferenceRatio = 0.05 | ||||
| 	// DefaultMaxFreeDifferenceRatio describes how free resources (allocatable - daemon and system pods)
 | ||||
| 	DefaultMaxFreeDifferenceRatio = 0.05 | ||||
| 	// DefaultMaxCapacityMemoryDifferenceRatio describes how Node.Status.Capacity.Memory
 | ||||
| 	DefaultMaxCapacityMemoryDifferenceRatio = 0.015 | ||||
| ) | ||||
| 
 | ||||
| // NodeGroupDifferenceRatios contains various ratios used to determine if two NodeGroups are similar and makes scaling decisions
 | ||||
| type NodeGroupDifferenceRatios struct { | ||||
| 	// MaxAllocatableDifferenceRatio describes how Node.Status.Allocatable can differ between groups in the same NodeGroupSet
 | ||||
| 	MaxAllocatableDifferenceRatio float64 | ||||
| 	// MaxFreeDifferenceRatio describes how free resources (allocatable - daemon and system pods) can differ between groups in the same NodeGroupSet
 | ||||
| 	MaxFreeDifferenceRatio float64 | ||||
| 	// MaxCapacityMemoryDifferenceRatio describes how Node.Status.Capacity.Memory can differ between groups in the same NodeGroupSetAutoscalingOptions
 | ||||
| 	MaxCapacityMemoryDifferenceRatio float64 | ||||
| } | ||||
| 
 | ||||
| func NewDefaultNodeGroupDifferenceRatios() NodeGroupDifferenceRatios { | ||||
| 	return NodeGroupDifferenceRatios{ | ||||
| 		MaxAllocatableDifferenceRatio:    DefaultMaxAllocatableDifferenceRatio, | ||||
| 		MaxFreeDifferenceRatio:           DefaultMaxFreeDifferenceRatio, | ||||
| 		MaxCapacityMemoryDifferenceRatio: DefaultMaxCapacityMemoryDifferenceRatio, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // AutoscalingOptions contain various options to customize how autoscaling works
 | ||||
| type AutoscalingOptions struct { | ||||
| 	// NodeGroupDefaults are default values for per NodeGroup options.
 | ||||
|  | @ -213,4 +240,6 @@ type AutoscalingOptions struct { | |||
| 	NodeDeleteDelayAfterTaint time.Duration | ||||
| 	// ParallelDrain is whether CA can drain nodes in parallel.
 | ||||
| 	ParallelDrain bool | ||||
| 	// NodeGroupSetRatio is a collection of ratios used by CA used to make scaling decisions.
 | ||||
| 	NodeGroupSetRatios NodeGroupDifferenceRatios | ||||
| } | ||||
|  |  | |||
|  | @ -140,7 +140,7 @@ func NewTestProcessors(context *context.AutoscalingContext) *processors.Autoscal | |||
| 			podlistprocessor.NewFilterOutSchedulablePodListProcessor(context.PredicateChecker), | ||||
| 		), | ||||
| 		NodeGroupListProcessor: &nodegroups.NoOpNodeGroupListProcessor{}, | ||||
| 		NodeGroupSetProcessor:  nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}), | ||||
| 		NodeGroupSetProcessor:  nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}), | ||||
| 		ScaleDownSetProcessor:  nodes.NewPostFilteringScaleDownNodeProcessor(), | ||||
| 		// TODO(bskiba): change scale up test so that this can be a NoOpProcessor
 | ||||
| 		ScaleUpStatusProcessor:     &status.EventingScaleUpStatusProcessor{}, | ||||
|  |  | |||
|  | @ -213,6 +213,9 @@ var ( | |||
| 	nodeDeleteDelayAfterTaint          = flag.Duration("node-delete-delay-after-taint", 5*time.Second, "How long to wait before deleting a node after tainting it") | ||||
| 	scaleDownSimulationTimeout         = flag.Duration("scale-down-simulation-timeout", 5*time.Minute, "How long should we run scale down simulation.") | ||||
| 	parallelDrain                      = flag.Bool("parallel-drain", false, "Whether to allow parallel drain of nodes.") | ||||
| 	maxCapacityMemoryDifferenceRatio   = flag.Float64("memory-difference-ratio", config.DefaultMaxCapacityMemoryDifferenceRatio, "Maximum difference in memory capacity between two similar node groups to be considered for balancing. Value is a ratio of the smaller node group's memory capacity.") | ||||
| 	maxFreeDifferenceRatio             = flag.Float64("max-free-difference-ratio", config.DefaultMaxFreeDifferenceRatio, "Maximum difference in free resources between two similar node groups to be considered for balancing. Value is a ratio of the smaller node group's free resource.") | ||||
| 	maxAllocatableDifferenceRatio      = flag.Float64("max-allocatable-difference-ratio", config.DefaultMaxAllocatableDifferenceRatio, "Maximum difference in allocatable resources between two similar node groups to be considered for balancing. Value is a ratio of the smaller node group's allocatable resource.") | ||||
| ) | ||||
| 
 | ||||
| func createAutoscalingOptions() config.AutoscalingOptions { | ||||
|  | @ -314,6 +317,11 @@ func createAutoscalingOptions() config.AutoscalingOptions { | |||
| 		NodeDeleteDelayAfterTaint:          *nodeDeleteDelayAfterTaint, | ||||
| 		ScaleDownSimulationTimeout:         *scaleDownSimulationTimeout, | ||||
| 		ParallelDrain:                      *parallelDrain, | ||||
| 		NodeGroupSetRatios: config.NodeGroupDifferenceRatios{ | ||||
| 			MaxCapacityMemoryDifferenceRatio: *maxCapacityMemoryDifferenceRatio, | ||||
| 			MaxAllocatableDifferenceRatio:    *maxAllocatableDifferenceRatio, | ||||
| 			MaxFreeDifferenceRatio:           *maxFreeDifferenceRatio, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -395,7 +403,7 @@ func buildAutoscaler(debuggingSnapshotter debuggingsnapshot.DebuggingSnapshotter | |||
| 		} else if autoscalingOptions.CloudProviderName == cloudprovider.ClusterAPIProviderName { | ||||
| 			nodeInfoComparatorBuilder = nodegroupset.CreateClusterAPINodeInfoComparator | ||||
| 		} | ||||
| 		nodeInfoComparator = nodeInfoComparatorBuilder(autoscalingOptions.BalancingExtraIgnoredLabels) | ||||
| 		nodeInfoComparator = nodeInfoComparatorBuilder(autoscalingOptions.BalancingExtraIgnoredLabels, autoscalingOptions.NodeGroupSetRatios) | ||||
| 	} | ||||
| 
 | ||||
| 	opts.Processors.NodeGroupSetProcessor = &nodegroupset.BalancingNodeGroupSetProcessor{ | ||||
|  |  | |||
|  | @ -17,13 +17,14 @@ limitations under the License. | |||
| package nodegroupset | ||||
| 
 | ||||
| import ( | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
| ) | ||||
| 
 | ||||
| // CreateAwsNodeInfoComparator returns a comparator that checks if two nodes should be considered
 | ||||
| // part of the same NodeGroupSet. This is true if they match usual conditions checked by IsCloudProviderNodeInfoSimilar,
 | ||||
| // even if they have different AWS-specific labels.
 | ||||
| func CreateAwsNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator { | ||||
| func CreateAwsNodeInfoComparator(extraIgnoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeInfoComparator { | ||||
| 	awsIgnoredLabels := map[string]bool{ | ||||
| 		"alpha.eksctl.io/instance-id":    true, // this is a label used by eksctl to identify instances.
 | ||||
| 		"alpha.eksctl.io/nodegroup-name": true, // this is a label used by eksctl to identify "node group" names.
 | ||||
|  | @ -42,6 +43,6 @@ func CreateAwsNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator | |||
| 	} | ||||
| 
 | ||||
| 	return func(n1, n2 *schedulerframework.NodeInfo) bool { | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, awsIgnoredLabels) | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, awsIgnoredLabels, ratioOpts) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -19,12 +19,13 @@ package nodegroupset | |||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/context" | ||||
| 	. "k8s.io/autoscaler/cluster-autoscaler/utils/test" | ||||
| ) | ||||
| 
 | ||||
| func TestIsAwsNodeInfoSimilar(t *testing.T) { | ||||
| 	comparator := CreateAwsNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateAwsNodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	node1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	node2 := BuildTestNode("node2", 1000, 2000) | ||||
| 
 | ||||
|  | @ -176,6 +177,6 @@ func TestIsAwsNodeInfoSimilar(t *testing.T) { | |||
| func TestFindSimilarNodeGroupsAwsBasic(t *testing.T) { | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 	ni1, ni2, ni3 := buildBasicNodeGroups(context) | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAwsNodeInfoComparator([]string{})} | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAwsNodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{})} | ||||
| 	basicSimilarNodeGroupsTest(t, context, processor, ni1, ni2, ni3) | ||||
| } | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ limitations under the License. | |||
| package nodegroupset | ||||
| 
 | ||||
| import ( | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
| ) | ||||
| 
 | ||||
|  | @ -44,7 +45,7 @@ func nodesFromSameAzureNodePoolLegacy(n1, n2 *schedulerframework.NodeInfo) bool | |||
| // CreateAzureNodeInfoComparator returns a comparator that checks if two nodes should be considered
 | ||||
| // part of the same NodeGroupSet. This is true if they either belong to the same Azure agentpool
 | ||||
| // or match usual conditions checked by IsCloudProviderNodeInfoSimilar, even if they have different agentpool labels.
 | ||||
| func CreateAzureNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator { | ||||
| func CreateAzureNodeInfoComparator(extraIgnoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeInfoComparator { | ||||
| 	azureIgnoredLabels := make(map[string]bool) | ||||
| 	for k, v := range BasicIgnoredLabels { | ||||
| 		azureIgnoredLabels[k] = v | ||||
|  | @ -60,6 +61,6 @@ func CreateAzureNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparat | |||
| 		if nodesFromSameAzureNodePool(n1, n2) { | ||||
| 			return true | ||||
| 		} | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, azureIgnoredLabels) | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, azureIgnoredLabels, ratioOpts) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import ( | |||
| 
 | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider" | ||||
| 	testprovider "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/test" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/context" | ||||
| 	. "k8s.io/autoscaler/cluster-autoscaler/utils/test" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
|  | @ -29,7 +30,7 @@ import ( | |||
| ) | ||||
| 
 | ||||
| func TestIsAzureNodeInfoSimilar(t *testing.T) { | ||||
| 	comparator := CreateAzureNodeInfoComparator([]string{"example.com/ready"}) | ||||
| 	comparator := CreateAzureNodeInfoComparator([]string{"example.com/ready"}, config.NodeGroupDifferenceRatios{}) | ||||
| 	n1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	n1.ObjectMeta.Labels["test-label"] = "test-value" | ||||
| 	n1.ObjectMeta.Labels["character"] = "thing" | ||||
|  | @ -75,12 +76,12 @@ func TestIsAzureNodeInfoSimilar(t *testing.T) { | |||
| func TestFindSimilarNodeGroupsAzureBasic(t *testing.T) { | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 	ni1, ni2, ni3 := buildBasicNodeGroups(context) | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAzureNodeInfoComparator([]string{})} | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAzureNodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{})} | ||||
| 	basicSimilarNodeGroupsTest(t, context, processor, ni1, ni2, ni3) | ||||
| } | ||||
| 
 | ||||
| func TestFindSimilarNodeGroupsAzureByLabel(t *testing.T) { | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAzureNodeInfoComparator([]string{})} | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateAzureNodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{})} | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 
 | ||||
| 	n1 := BuildTestNode("n1", 1000, 1000) | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import ( | |||
| 
 | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider" | ||||
| 	testprovider "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/test" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/context" | ||||
| 	. "k8s.io/autoscaler/cluster-autoscaler/utils/test" | ||||
| 
 | ||||
|  | @ -84,7 +85,7 @@ func basicSimilarNodeGroupsTest( | |||
| func TestFindSimilarNodeGroups(t *testing.T) { | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 	ni1, ni2, ni3 := buildBasicNodeGroups(context) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	basicSimilarNodeGroupsTest(t, context, processor, ni1, ni2, ni3) | ||||
| } | ||||
| 
 | ||||
|  | @ -94,7 +95,7 @@ func TestFindSimilarNodeGroupsCustomLabels(t *testing.T) { | |||
| 	ni1.Node().Labels["example.com/ready"] = "true" | ||||
| 	ni2.Node().Labels["example.com/ready"] = "false" | ||||
| 
 | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{"example.com/ready"}) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{"example.com/ready"}, config.NodeGroupDifferenceRatios{}) | ||||
| 	basicSimilarNodeGroupsTest(t, context, processor, ni1, ni2, ni3) | ||||
| } | ||||
| 
 | ||||
|  | @ -112,7 +113,7 @@ func TestFindSimilarNodeGroupsCustomComparator(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestBalanceSingleGroup(t *testing.T) { | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 
 | ||||
| 	provider := testprovider.NewTestCloudProvider(nil, nil) | ||||
|  | @ -132,7 +133,7 @@ func TestBalanceSingleGroup(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestBalanceUnderMaxSize(t *testing.T) { | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 
 | ||||
| 	provider := testprovider.NewTestCloudProvider(nil, nil) | ||||
|  | @ -182,7 +183,7 @@ func TestBalanceUnderMaxSize(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestBalanceHittingMaxSize(t *testing.T) { | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}) | ||||
| 	processor := NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 
 | ||||
| 	provider := testprovider.NewTestCloudProvider(nil, nil) | ||||
|  |  | |||
|  | @ -17,13 +17,14 @@ limitations under the License. | |||
| package nodegroupset | ||||
| 
 | ||||
| import ( | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
| ) | ||||
| 
 | ||||
| // CreateClusterAPINodeInfoComparator returns a comparator that checks if two nodes should be considered
 | ||||
| // part of the same NodeGroupSet. This is true if they match usual conditions checked by IsCloudProviderNodeInfoSimilar,
 | ||||
| // even if they have different infrastructure provider-specific labels.
 | ||||
| func CreateClusterAPINodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator { | ||||
| func CreateClusterAPINodeInfoComparator(extraIgnoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeInfoComparator { | ||||
| 	capiIgnoredLabels := map[string]bool{ | ||||
| 		"topology.ebs.csi.aws.com/zone":                 true, // this is a label used by the AWS EBS CSI driver as a target for Persistent Volume Node Affinity
 | ||||
| 		"topology.diskplugin.csi.alibabacloud.com/zone": true, // this is a label used by the Alibaba Cloud CSI driver as a target for Persistent Volume Node Affinity
 | ||||
|  | @ -41,6 +42,6 @@ func CreateClusterAPINodeInfoComparator(extraIgnoredLabels []string) NodeInfoCom | |||
| 	} | ||||
| 
 | ||||
| 	return func(n1, n2 *schedulerframework.NodeInfo) bool { | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, capiIgnoredLabels) | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, capiIgnoredLabels, ratioOpts) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -19,12 +19,13 @@ package nodegroupset | |||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/context" | ||||
| 	. "k8s.io/autoscaler/cluster-autoscaler/utils/test" | ||||
| ) | ||||
| 
 | ||||
| func TestIsClusterAPINodeInfoSimilar(t *testing.T) { | ||||
| 	comparator := CreateClusterAPINodeInfoComparator([]string{}) | ||||
| 	comparator := CreateClusterAPINodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{}) | ||||
| 	node1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	node2 := BuildTestNode("node2", 1000, 2000) | ||||
| 
 | ||||
|  | @ -92,6 +93,6 @@ func TestIsClusterAPINodeInfoSimilar(t *testing.T) { | |||
| func TestFindSimilarNodeGroupsClusterAPIBasic(t *testing.T) { | ||||
| 	context := &context.AutoscalingContext{} | ||||
| 	ni1, ni2, ni3 := buildBasicNodeGroups(context) | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateClusterAPINodeInfoComparator([]string{})} | ||||
| 	processor := &BalancingNodeGroupSetProcessor{Comparator: CreateClusterAPINodeInfoComparator([]string{}, config.NodeGroupDifferenceRatios{})} | ||||
| 	basicSimilarNodeGroupsTest(t, context, processor, ni1, ni2, ni3) | ||||
| } | ||||
|  |  | |||
|  | @ -21,22 +21,11 @@ import ( | |||
| 
 | ||||
| 	apiv1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/api/resource" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/utils/scheduler" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// MaxAllocatableDifferenceRatio describes how Node.Status.Allocatable can differ between
 | ||||
| 	// groups in the same NodeGroupSet
 | ||||
| 	MaxAllocatableDifferenceRatio = 0.05 | ||||
| 	// MaxFreeDifferenceRatio describes how free resources (allocatable - daemon and system pods)
 | ||||
| 	// can differ between groups in the same NodeGroupSet
 | ||||
| 	MaxFreeDifferenceRatio = 0.05 | ||||
| 	// MaxCapacityMemoryDifferenceRatio describes how Node.Status.Capacity.Memory can differ between
 | ||||
| 	// groups in the same NodeGroupSet
 | ||||
| 	MaxCapacityMemoryDifferenceRatio = 0.015 | ||||
| ) | ||||
| 
 | ||||
| // BasicIgnoredLabels define a set of basic labels that should be ignored when comparing the similarity
 | ||||
| // of two nodes. Customized IgnoredLabels can be implemented in the corresponding codes of
 | ||||
| // specific cloud provider and the BasicIgnoredLabels should always be considered part of them.
 | ||||
|  | @ -92,7 +81,7 @@ func compareLabels(nodes []*schedulerframework.NodeInfo, ignoredLabels map[strin | |||
| } | ||||
| 
 | ||||
| // CreateGenericNodeInfoComparator returns a generic comparator that checks for node group similarity
 | ||||
| func CreateGenericNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator { | ||||
| func CreateGenericNodeInfoComparator(extraIgnoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeInfoComparator { | ||||
| 	genericIgnoredLabels := make(map[string]bool) | ||||
| 	for k, v := range BasicIgnoredLabels { | ||||
| 		genericIgnoredLabels[k] = v | ||||
|  | @ -102,7 +91,7 @@ func CreateGenericNodeInfoComparator(extraIgnoredLabels []string) NodeInfoCompar | |||
| 	} | ||||
| 
 | ||||
| 	return func(n1, n2 *schedulerframework.NodeInfo) bool { | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, genericIgnoredLabels) | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, genericIgnoredLabels, ratioOpts) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -111,7 +100,8 @@ func CreateGenericNodeInfoComparator(extraIgnoredLabels []string) NodeInfoCompar | |||
| // somewhat arbitrary, but generally we check if resources provided by both nodes
 | ||||
| // are similar enough to likely be the same type of machine and if the set of labels
 | ||||
| // is the same (except for a set of labels passed in to be ignored like hostname or zone).
 | ||||
| func IsCloudProviderNodeInfoSimilar(n1, n2 *schedulerframework.NodeInfo, ignoredLabels map[string]bool) bool { | ||||
| func IsCloudProviderNodeInfoSimilar( | ||||
| 	n1, n2 *schedulerframework.NodeInfo, ignoredLabels map[string]bool, ratioOpts config.NodeGroupDifferenceRatios) bool { | ||||
| 	capacity := make(map[apiv1.ResourceName][]resource.Quantity) | ||||
| 	allocatable := make(map[apiv1.ResourceName][]resource.Quantity) | ||||
| 	free := make(map[apiv1.ResourceName][]resource.Quantity) | ||||
|  | @ -136,7 +126,7 @@ func IsCloudProviderNodeInfoSimilar(n1, n2 *schedulerframework.NodeInfo, ignored | |||
| 		} | ||||
| 		switch kind { | ||||
| 		case apiv1.ResourceMemory: | ||||
| 			if !resourceListWithinTolerance(qtyList, MaxCapacityMemoryDifferenceRatio) { | ||||
| 			if !resourceListWithinTolerance(qtyList, ratioOpts.MaxCapacityMemoryDifferenceRatio) { | ||||
| 				return false | ||||
| 			} | ||||
| 		default: | ||||
|  | @ -150,10 +140,10 @@ func IsCloudProviderNodeInfoSimilar(n1, n2 *schedulerframework.NodeInfo, ignored | |||
| 	} | ||||
| 
 | ||||
| 	// For allocatable and free we allow resource quantities to be within a few % of each other
 | ||||
| 	if !resourceMapsWithinTolerance(allocatable, MaxAllocatableDifferenceRatio) { | ||||
| 	if !resourceMapsWithinTolerance(allocatable, ratioOpts.MaxAllocatableDifferenceRatio) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !resourceMapsWithinTolerance(free, MaxFreeDifferenceRatio) { | ||||
| 	if !resourceMapsWithinTolerance(free, ratioOpts.MaxFreeDifferenceRatio) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import ( | |||
| 
 | ||||
| 	apiv1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/api/resource" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/utils/gpu" | ||||
| 	. "k8s.io/autoscaler/cluster-autoscaler/utils/test" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
|  | @ -41,14 +42,14 @@ func checkNodesSimilarWithPods(t *testing.T, n1, n2 *apiv1.Node, pods1, pods2 [] | |||
| } | ||||
| 
 | ||||
| func TestIdenticalNodesSimilar(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 	n1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	n2 := BuildTestNode("node2", 1000, 2000) | ||||
| 	checkNodesSimilar(t, n1, n2, comparator, true) | ||||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousRequirements(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 	n1 := BuildTestNode("node1", 1000, 2000) | ||||
| 
 | ||||
| 	// Different CPU capacity
 | ||||
|  | @ -74,7 +75,7 @@ func TestNodesSimilarVariousRequirements(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousRequirementsAndPods(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 	n1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	p1 := BuildTestPod("pod1", 500, 1000) | ||||
| 	p1.Spec.NodeName = "node1" | ||||
|  | @ -100,22 +101,22 @@ func TestNodesSimilarVariousRequirementsAndPods(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousMemoryRequirements(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 	n1 := BuildTestNode("node1", 1000, 1000) | ||||
| 
 | ||||
| 	// Different memory capacity within tolerance
 | ||||
| 	n2 := BuildTestNode("node2", 1000, 1000) | ||||
| 	n2.Status.Capacity[apiv1.ResourceMemory] = *resource.NewQuantity(1000-(1000*MaxCapacityMemoryDifferenceRatio)+1, resource.DecimalSI) | ||||
| 	n2.Status.Capacity[apiv1.ResourceMemory] = *resource.NewQuantity(1000-(1000*config.DefaultMaxCapacityMemoryDifferenceRatio)+1, resource.DecimalSI) | ||||
| 	checkNodesSimilar(t, n1, n2, comparator, true) | ||||
| 
 | ||||
| 	// Different memory capacity exceeds tolerance
 | ||||
| 	n3 := BuildTestNode("node3", 1000, 1000) | ||||
| 	n3.Status.Capacity[apiv1.ResourceMemory] = *resource.NewQuantity(1000-(1000*MaxCapacityMemoryDifferenceRatio)-1, resource.DecimalSI) | ||||
| 	n3.Status.Capacity[apiv1.ResourceMemory] = *resource.NewQuantity(1000-(1000*config.DefaultMaxCapacityMemoryDifferenceRatio)-1, resource.DecimalSI) | ||||
| 	checkNodesSimilar(t, n1, n3, comparator, false) | ||||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousLargeMemoryRequirementsM5XLarge(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 
 | ||||
| 	// Use realistic memory capacity (taken from real nodes)
 | ||||
| 	// 15944120 KB ~= 16GiB (m5.xLarge)
 | ||||
|  | @ -137,7 +138,7 @@ func TestNodesSimilarVariousLargeMemoryRequirementsM5XLarge(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousLargeMemoryRequirementsM516XLarge(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 
 | ||||
| 	// Use realistic memory capacity (taken from real nodes)
 | ||||
| 	// 257217528 KB ~= 256GiB (m5.16xLarge)
 | ||||
|  | @ -159,7 +160,7 @@ func TestNodesSimilarVariousLargeMemoryRequirementsM516XLarge(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestNodesSimilarVariousLabels(t *testing.T) { | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{"example.com/ready"}) | ||||
| 	comparator := CreateGenericNodeInfoComparator([]string{"example.com/ready"}, config.NewDefaultNodeGroupDifferenceRatios()) | ||||
| 	n1 := BuildTestNode("node1", 1000, 2000) | ||||
| 	n1.ObjectMeta.Labels["test-label"] = "test-value" | ||||
| 	n1.ObjectMeta.Labels["character"] = "winnie the pooh" | ||||
|  |  | |||
|  | @ -17,13 +17,14 @@ limitations under the License. | |||
| package nodegroupset | ||||
| 
 | ||||
| import ( | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
| ) | ||||
| 
 | ||||
| // CreateGceNodeInfoComparator returns a comparator that checks if two nodes should be considered
 | ||||
| // part of the same NodeGroupSet. This is true if they match usual conditions checked by IsCloudProviderNodeInfoSimilar,
 | ||||
| // even if they have different GCE-specific labels.
 | ||||
| func CreateGceNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator { | ||||
| func CreateGceNodeInfoComparator(extraIgnoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeInfoComparator { | ||||
| 	gceIgnoredLabels := map[string]bool{ | ||||
| 		"topology.gke.io/zone": true, | ||||
| 	} | ||||
|  | @ -37,6 +38,6 @@ func CreateGceNodeInfoComparator(extraIgnoredLabels []string) NodeInfoComparator | |||
| 	} | ||||
| 
 | ||||
| 	return func(n1, n2 *schedulerframework.NodeInfo) bool { | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, gceIgnoredLabels) | ||||
| 		return IsCloudProviderNodeInfoSimilar(n1, n2, gceIgnoredLabels, ratioOpts) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ import ( | |||
| 	"fmt" | ||||
| 
 | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/context" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/utils/errors" | ||||
| 	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" | ||||
|  | @ -70,8 +71,8 @@ func (n *NoOpNodeGroupSetProcessor) BalanceScaleUpBetweenGroups(context *context | |||
| func (n *NoOpNodeGroupSetProcessor) CleanUp() {} | ||||
| 
 | ||||
| // NewDefaultNodeGroupSetProcessor creates an instance of NodeGroupSetProcessor.
 | ||||
| func NewDefaultNodeGroupSetProcessor(ignoredLabels []string) NodeGroupSetProcessor { | ||||
| func NewDefaultNodeGroupSetProcessor(ignoredLabels []string, ratioOpts config.NodeGroupDifferenceRatios) NodeGroupSetProcessor { | ||||
| 	return &BalancingNodeGroupSetProcessor{ | ||||
| 		Comparator: CreateGenericNodeInfoComparator(ignoredLabels), | ||||
| 		Comparator: CreateGenericNodeInfoComparator(ignoredLabels, ratioOpts), | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ limitations under the License. | |||
| package processors | ||||
| 
 | ||||
| import ( | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/config" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/processors/actionablecluster" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/processors/customresources" | ||||
| 	"k8s.io/autoscaler/cluster-autoscaler/processors/nodegroupconfig" | ||||
|  | @ -67,7 +68,11 @@ func DefaultProcessors() *AutoscalingProcessors { | |||
| 	return &AutoscalingProcessors{ | ||||
| 		PodListProcessor:       pods.NewDefaultPodListProcessor(), | ||||
| 		NodeGroupListProcessor: nodegroups.NewDefaultNodeGroupListProcessor(), | ||||
| 		NodeGroupSetProcessor:      nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}), | ||||
| 		NodeGroupSetProcessor: nodegroupset.NewDefaultNodeGroupSetProcessor([]string{}, config.NodeGroupDifferenceRatios{ | ||||
| 			MaxAllocatableDifferenceRatio:    config.DefaultMaxAllocatableDifferenceRatio, | ||||
| 			MaxCapacityMemoryDifferenceRatio: config.DefaultMaxCapacityMemoryDifferenceRatio, | ||||
| 			MaxFreeDifferenceRatio:           config.DefaultMaxFreeDifferenceRatio, | ||||
| 		}), | ||||
| 		ScaleUpStatusProcessor:     status.NewDefaultScaleUpStatusProcessor(), | ||||
| 		ScaleDownNodeProcessor:     nodes.NewPreFilteringScaleDownNodeProcessor(), | ||||
| 		ScaleDownSetProcessor:      nodes.NewPostFilteringScaleDownNodeProcessor(), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue