From 408c9da2a6b16092626cfa14292d711d061e4ae4 Mon Sep 17 00:00:00 2001 From: zouyee Date: Sat, 19 Oct 2019 20:49:05 +0800 Subject: [PATCH 1/2] LeastRequestedPriority/MostRequestedPriority/BalancedResourceAllocation as Score plugins Signed-off-by: Zou Nengren --- .../api/compatibility/compatibility_test.go | 83 ++-- .../framework/plugins/default_registry.go | 39 +- .../framework/plugins/noderesources/BUILD | 19 +- .../noderesources/balance_allocated.go | 66 +++ .../noderesources/balance_allocated_test.go | 407 ++++++++++++++++++ .../plugins/noderesources/least_allocated.go | 65 +++ .../noderesources/least_allocated_test.go | 253 +++++++++++ .../plugins/noderesources/most_allocated.go | 65 +++ .../noderesources/most_allocated_test.go | 216 ++++++++++ .../plugins/noderesources/test_util.go | 39 ++ test/integration/scheduler/scheduler_test.go | 44 +- 11 files changed, 1224 insertions(+), 72 deletions(-) create mode 100644 pkg/scheduler/framework/plugins/noderesources/balance_allocated.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/least_allocated.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/least_allocated_test.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/most_allocated.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/most_allocated_test.go create mode 100644 pkg/scheduler/framework/plugins/noderesources/test_util.go diff --git a/pkg/scheduler/api/compatibility/compatibility_test.go b/pkg/scheduler/api/compatibility/compatibility_test.go index eb61fb8bba3..6b5dd430bef 100644 --- a/pkg/scheduler/api/compatibility/compatibility_test.go +++ b/pkg/scheduler/api/compatibility/compatibility_test.go @@ -96,7 +96,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { "TestLabelsPresence", ), wantPrioritizers: sets.NewString( - "LeastRequestedPriority", "ServiceSpreadingPriority", "TestServiceAntiAffinity", "TestLabelPreference", @@ -109,6 +108,9 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "VolumeRestrictions"}, {Name: "TaintToleration"}, }, + "ScorePlugin": { + {Name: "NodeResourcesLeastAllocated", Weight: 1}, + }, }, }, @@ -141,8 +143,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "TestServiceAntiAffinity", "TestLabelPreference", @@ -157,6 +157,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "VolumeRestrictions"}, {Name: "TaintToleration"}, }, + "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + }, }, }, @@ -198,8 +202,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "TestServiceAntiAffinity", "TestLabelPreference", @@ -216,7 +218,9 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "VolumeZone"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, }, }, @@ -264,8 +268,6 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", ), @@ -282,7 +284,9 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "TaintToleration", Weight: 2}, }, @@ -335,11 +339,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ "FilterPlugin": { @@ -354,7 +355,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -417,11 +421,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ "FilterPlugin": { @@ -436,7 +437,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -512,11 +516,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ "FilterPlugin": { @@ -531,7 +532,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -608,11 +612,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ "FilterPlugin": { @@ -628,7 +629,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -710,11 +714,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ "FilterPlugin": { @@ -730,7 +731,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -824,11 +828,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", "RequestedToCapacityRatioPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -845,7 +846,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -940,11 +944,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", "RequestedToCapacityRatioPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -962,7 +963,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -1057,11 +1061,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", "RequestedToCapacityRatioPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -1079,7 +1080,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -1178,11 +1182,8 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { ), wantPrioritizers: sets.NewString( "EqualPriority", - "LeastRequestedPriority", - "BalancedResourceAllocation", "SelectorSpreadPriority", "InterPodAffinityPriority", - "MostRequestedPriority", "RequestedToCapacityRatioPriority", ), wantPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -1200,7 +1201,10 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 2}, {Name: "ImageLocality", Weight: 2}, + {Name: "NodeResourcesLeastAllocated", Weight: 2}, + {Name: "NodeResourcesMostAllocated", Weight: 2}, {Name: "NodeAffinity", Weight: 2}, {Name: "NodePreferAvoidPods", Weight: 2}, {Name: "TaintToleration", Weight: 2}, @@ -1241,10 +1245,13 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { "InterPodAffinity": "MatchInterPodAffinity", } scoreToPriorityMap := map[string]string{ - "ImageLocality": "ImageLocalityPriority", - "NodeAffinity": "NodeAffinityPriority", - "NodePreferAvoidPods": "NodePreferAvoidPodsPriority", - "TaintToleration": "TaintTolerationPriority", + "ImageLocality": "ImageLocalityPriority", + "NodeAffinity": "NodeAffinityPriority", + "NodePreferAvoidPods": "NodePreferAvoidPodsPriority", + "TaintToleration": "TaintTolerationPriority", + "NodeResourcesLeastAllocated": "LeastRequestedPriority", + "NodeResourcesBalancedAllocation": "BalancedResourceAllocation", + "NodeResourcesMostAllocated": "MostRequestedPriority", } for v, tc := range schedulerFiles { diff --git a/pkg/scheduler/framework/plugins/default_registry.go b/pkg/scheduler/framework/plugins/default_registry.go index 38d818a6d65..5f051e441c7 100644 --- a/pkg/scheduler/framework/plugins/default_registry.go +++ b/pkg/scheduler/framework/plugins/default_registry.go @@ -53,15 +53,18 @@ type RegistryArgs struct { // runs custom plugins, can pass a different Registry when initializing the scheduler. func NewDefaultRegistry(args *RegistryArgs) framework.Registry { return framework.Registry{ - imagelocality.Name: imagelocality.New, - tainttoleration.Name: tainttoleration.New, - noderesources.Name: noderesources.New, - nodename.Name: nodename.New, - nodeports.Name: nodeports.New, - nodepreferavoidpods.Name: nodepreferavoidpods.New, - nodeaffinity.Name: nodeaffinity.New, - podtopologyspread.Name: podtopologyspread.New, - nodeunschedulable.Name: nodeunschedulable.New, + imagelocality.Name: imagelocality.New, + tainttoleration.Name: tainttoleration.New, + noderesources.Name: noderesources.New, + nodename.Name: nodename.New, + nodeports.Name: nodeports.New, + nodepreferavoidpods.Name: nodepreferavoidpods.New, + nodeaffinity.Name: nodeaffinity.New, + podtopologyspread.Name: podtopologyspread.New, + nodeunschedulable.Name: nodeunschedulable.New, + noderesources.BalancedAllocationName: noderesources.NewBalancedAllocation, + noderesources.MostAllocatedName: noderesources.NewMostAllocated, + noderesources.LeastAllocatedName: noderesources.NewLeastAllocated, volumebinding.Name: func(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) { return volumebinding.NewFromVolumeBinder(args.VolumeBinder), nil }, @@ -190,6 +193,24 @@ func NewDefaultConfigProducerRegistry() *ConfigProducerRegistry { return }) + registry.RegisterPriority(priorities.MostRequestedPriority, + func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) { + plugins.Score = appendToPluginSet(plugins.Score, noderesources.MostAllocatedName, &args.Weight) + return + }) + + registry.RegisterPriority(priorities.BalancedResourceAllocation, + func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) { + plugins.Score = appendToPluginSet(plugins.Score, noderesources.BalancedAllocationName, &args.Weight) + return + }) + + registry.RegisterPriority(priorities.LeastRequestedPriority, + func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) { + plugins.Score = appendToPluginSet(plugins.Score, noderesources.LeastAllocatedName, &args.Weight) + return + }) + return registry } diff --git a/pkg/scheduler/framework/plugins/noderesources/BUILD b/pkg/scheduler/framework/plugins/noderesources/BUILD index fd9ac774519..0f1f47da301 100644 --- a/pkg/scheduler/framework/plugins/noderesources/BUILD +++ b/pkg/scheduler/framework/plugins/noderesources/BUILD @@ -2,15 +2,24 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["node_resources.go"], + srcs = [ + "balance_allocated.go", + "least_allocated.go", + "most_allocated.go", + "node_resources.go", + "test_util.go", + ], importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources", visibility = ["//visibility:public"], deps = [ "//pkg/scheduler/algorithm/predicates:go_default_library", + "//pkg/scheduler/algorithm/priorities:go_default_library", "//pkg/scheduler/framework/plugins/migration:go_default_library", "//pkg/scheduler/framework/v1alpha1:go_default_library", "//pkg/scheduler/nodeinfo:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", ], ) @@ -31,7 +40,12 @@ filegroup( go_test( name = "go_default_test", - srcs = ["node_resources_test.go"], + srcs = [ + "balance_allocated_test.go", + "least_allocated_test.go", + "most_allocated_test.go", + "node_resources_test.go", + ], embed = [":go_default_library"], deps = [ "//pkg/apis/core/v1/helper:go_default_library", @@ -42,6 +56,7 @@ go_test( "//pkg/scheduler/nodeinfo:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", diff --git a/pkg/scheduler/framework/plugins/noderesources/balance_allocated.go b/pkg/scheduler/framework/plugins/noderesources/balance_allocated.go new file mode 100644 index 00000000000..5e1996f61c9 --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/balance_allocated.go @@ -0,0 +1,66 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities" + "k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" +) + +// BalancedAllocation is a score plugin that calculates the difference between the cpu and memory fraction +// of capacity, and prioritizes the host based on how close the two metrics are to each other. +type BalancedAllocation struct { + handle framework.FrameworkHandle +} + +var _ = framework.ScorePlugin(&BalancedAllocation{}) + +// BalancedAllocationName is the name of the plugin used in the plugin registry and configurations. +const BalancedAllocationName = "NodeResourcesBalancedAllocation" + +// Name returns name of the plugin. It is used in logs, etc. +func (ba *BalancedAllocation) Name() string { + return BalancedAllocationName +} + +// Score invoked at the score extension point. +func (ba *BalancedAllocation) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { + nodeInfo, exist := ba.handle.NodeInfoSnapshot().NodeInfoMap[nodeName] + if !exist { + return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) + } + meta := migration.PriorityMetadata(state) + + s, err := priorities.BalancedResourceAllocationMap(pod, meta, nodeInfo) + return s.Score, migration.ErrorToFrameworkStatus(err) +} + +// ScoreExtensions of the Score plugin. +func (ba *BalancedAllocation) ScoreExtensions() framework.ScoreExtensions { + return nil +} + +// NewBalancedAllocation initializes a new plugin and returns it. +func NewBalancedAllocation(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) { + return &BalancedAllocation{handle: h}, nil +} diff --git a/pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go b/pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go new file mode 100644 index 00000000000..f8ea9197ff5 --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go @@ -0,0 +1,407 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/features" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" + schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" +) + +// getExistingVolumeCountForNode gets the current number of volumes on node. +func getExistingVolumeCountForNode(pods []*v1.Pod, maxVolumes int) int { + volumeCount := 0 + for _, pod := range pods { + volumeCount += len(pod.Spec.Volumes) + } + if maxVolumes-volumeCount > 0 { + return maxVolumes - volumeCount + } + return 0 +} + +func TestNodeResourcesBalancedAllocation(t *testing.T) { + // Enable volumesOnNodeForBalancing to do balanced node resource allocation + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BalanceAttachedNodeVolumes, true)() + podwithVol1 := v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("2000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("3000"), + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + VolumeSource: v1.VolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{VolumeID: "ovp"}, + }, + }, + }, + NodeName: "machine4", + } + podwithVol2 := v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("0m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("0m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + VolumeSource: v1.VolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{VolumeID: "ovp1"}, + }, + }, + }, + NodeName: "machine4", + } + podwithVol3 := v1.PodSpec{ + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("0m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("0m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + VolumeSource: v1.VolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{VolumeID: "ovp1"}, + }, + }, + }, + NodeName: "machine4", + } + labels1 := map[string]string{ + "foo": "bar", + "baz": "blah", + } + labels2 := map[string]string{ + "bar": "foo", + "baz": "blah", + } + machine1Spec := v1.PodSpec{ + NodeName: "machine1", + } + machine2Spec := v1.PodSpec{ + NodeName: "machine2", + } + noResources := v1.PodSpec{ + Containers: []v1.Container{}, + } + cpuOnly := v1.PodSpec{ + NodeName: "machine1", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + }, + } + cpuOnly2 := cpuOnly + cpuOnly2.NodeName = "machine2" + cpuAndMemory := v1.PodSpec{ + NodeName: "machine2", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("2000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("3000"), + }, + }, + }, + }, + } + cpuAndMemory3 := v1.PodSpec{ + NodeName: "machine3", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("2000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("3000"), + }, + }, + }, + }, + } + tests := []struct { + pod *v1.Pod + pods []*v1.Pod + nodes []*v1.Node + expectedList framework.NodeScoreList + name string + }{ + { + // Node1 scores (remaining resources) on 0-10 scale + // CPU Fraction: 0 / 4000 = 0% + // Memory Fraction: 0 / 10000 = 0% + // Node1 Score: 10 - (0-0)*100 = 100 + // Node2 scores (remaining resources) on 0-10 scale + // CPU Fraction: 0 / 4000 = 0 % + // Memory Fraction: 0 / 10000 = 0% + // Node2 Score: 10 - (0-0)*100 = 100 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: framework.MaxNodeScore}}, + name: "nothing scheduled, nothing requested", + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 3000 / 4000= 75% + // Memory Fraction: 5000 / 10000 = 50% + // Node1 Score: 10 - (0.75-0.5)*100 = 75 + // Node2 scores on 0-10 scale + // CPU Fraction: 3000 / 6000= 50% + // Memory Fraction: 5000/10000 = 50% + // Node2 Score: 10 - (0.5-0.5)*100 = 100 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 6000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 75}, {Name: "machine2", Score: framework.MaxNodeScore}}, + name: "nothing scheduled, resources requested, differently sized machines", + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 0 / 4000= 0% + // Memory Fraction: 0 / 10000 = 0% + // Node1 Score: 10 - (0-0)*100 = 100 + // Node2 scores on 0-10 scale + // CPU Fraction: 0 / 4000= 0% + // Memory Fraction: 0 / 10000 = 0% + // Node2 Score: 10 - (0-0)*100 = 100 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: framework.MaxNodeScore}}, + name: "no resources requested, pods scheduled", + pods: []*v1.Pod{ + {Spec: machine1Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels2}}, + {Spec: machine1Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: machine2Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: machine2Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 0 / 20000 = 0% + // Node1 Score: 10 - (0.6-0)*100 = 40 + // Node2 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 5000 / 20000 = 25% + // Node2 Score: 10 - (0.6-0.25)*100 = 65 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 40}, {Name: "machine2", Score: 65}}, + name: "no resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels2}}, + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuOnly2, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuAndMemory, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 5000 / 20000 = 25% + // Node1 Score: 10 - (0.6-0.25)*100 = 65 + // Node2 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 10000 / 20000 = 50% + // Node2 Score: 10 - (0.6-0.5)*100 = 9 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 65}, {Name: "machine2", Score: 90}}, + name: "resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 5000 / 20000 = 25% + // Node1 Score: 10 - (0.6-0.25)*100 = 65 + // Node2 scores on 0-10 scale + // CPU Fraction: 6000 / 10000 = 60% + // Memory Fraction: 10000 / 50000 = 20% + // Node2 Score: 10 - (0.6-0.2)*100 = 60 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 50000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 65}, {Name: "machine2", Score: 60}}, + name: "resources requested, pods scheduled with resources, differently sized machines", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Fraction: 6000 / 4000 > 100% ==> Score := 0 + // Memory Fraction: 0 / 10000 = 0 + // Node1 Score: 0 + // Node2 scores on 0-10 scale + // CPU Fraction: 6000 / 4000 > 100% ==> Score := 0 + // Memory Fraction 5000 / 10000 = 50% + // Node2 Score: 0 + pod: &v1.Pod{Spec: cpuOnly}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}}, + name: "requested resources exceed node capacity", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 0, 0), makeNode("machine2", 0, 0)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}}, + name: "zero node resources, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Machine4 will be chosen here because it already has a existing volume making the variance + // of volume count, CPU usage, memory usage closer. + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Volumes: []v1.Volume{ + { + VolumeSource: v1.VolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{VolumeID: "ovp2"}, + }, + }, + }, + }, + }, + nodes: []*v1.Node{makeNode("machine3", 3500, 40000), makeNode("machine4", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine3", Score: 89}, {Name: "machine4", Score: 98}}, + name: "Include volume count on a node for balanced resource allocation", + pods: []*v1.Pod{ + {Spec: cpuAndMemory3}, + {Spec: podwithVol1}, + {Spec: podwithVol2}, + {Spec: podwithVol3}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, test.nodes) + if len(test.pod.Spec.Volumes) > 0 { + maxVolumes := 5 + for _, info := range nodeNameToInfo { + info.TransientInfo.TransNodeInfo.AllocatableVolumesCount = getExistingVolumeCountForNode(info.Pods(), maxVolumes) + info.TransientInfo.TransNodeInfo.RequestedVolumes = len(test.pod.Spec.Volumes) + } + } + fh, _ := framework.NewFramework(nil, nil, nil) + snapshot := fh.NodeInfoSnapshot() + snapshot.NodeInfoMap = nodeNameToInfo + + p, _ := NewBalancedAllocation(nil, fh) + + for i := range test.nodes { + hostResult, err := p.(framework.ScorePlugin).Score(context.Background(), nil, test.pod, test.nodes[i].Name) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(test.expectedList[i].Score, hostResult) { + t.Errorf("expected %#v, got %#v", test.expectedList[i].Score, hostResult) + } + } + }) + } +} diff --git a/pkg/scheduler/framework/plugins/noderesources/least_allocated.go b/pkg/scheduler/framework/plugins/noderesources/least_allocated.go new file mode 100644 index 00000000000..d5d60fb763b --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/least_allocated.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities" + "k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" +) + +// LeastAllocated is a score plugin that favors nodes with fewer allocation requested resources based on requested resources. +type LeastAllocated struct { + handle framework.FrameworkHandle +} + +var _ = framework.ScorePlugin(&LeastAllocated{}) + +// LeastAllocatedName is the name of the plugin used in the plugin registry and configurations. +const LeastAllocatedName = "NodeResourcesLeastAllocated" + +// Name returns name of the plugin. It is used in logs, etc. +func (la *LeastAllocated) Name() string { + return LeastAllocatedName +} + +// Score invoked at the score extension point. +func (la *LeastAllocated) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { + nodeInfo, exist := la.handle.NodeInfoSnapshot().NodeInfoMap[nodeName] + if !exist { + return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) + } + meta := migration.PriorityMetadata(state) + + s, err := priorities.LeastRequestedPriorityMap(pod, meta, nodeInfo) + return s.Score, migration.ErrorToFrameworkStatus(err) +} + +// ScoreExtensions of the Score plugin. +func (la *LeastAllocated) ScoreExtensions() framework.ScoreExtensions { + return nil +} + +// NewLeastAllocated initializes a new plugin and returns it. +func NewLeastAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) { + return &LeastAllocated{handle: h}, nil +} diff --git a/pkg/scheduler/framework/plugins/noderesources/least_allocated_test.go b/pkg/scheduler/framework/plugins/noderesources/least_allocated_test.go new file mode 100644 index 00000000000..e6728081cd6 --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/least_allocated_test.go @@ -0,0 +1,253 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" + schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" +) + +func TestNodeResourcesLeastAllocated(t *testing.T) { + labels1 := map[string]string{ + "foo": "bar", + "baz": "blah", + } + labels2 := map[string]string{ + "bar": "foo", + "baz": "blah", + } + machine1Spec := v1.PodSpec{ + NodeName: "machine1", + } + machine2Spec := v1.PodSpec{ + NodeName: "machine2", + } + noResources := v1.PodSpec{ + Containers: []v1.Container{}, + } + cpuOnly := v1.PodSpec{ + NodeName: "machine1", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + }, + } + cpuOnly2 := cpuOnly + cpuOnly2.NodeName = "machine2" + cpuAndMemory := v1.PodSpec{ + NodeName: "machine2", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("2000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("3000"), + }, + }, + }, + }, + } + tests := []struct { + pod *v1.Pod + pods []*v1.Pod + nodes []*v1.Node + expectedList framework.NodeScoreList + name string + }{ + { + // Node1 scores (remaining resources) on 0-10 scale + // CPU Score: ((4000 - 0) *100) / 4000 = 100 + // Memory Score: ((10000 - 0) *100) / 10000 = 100 + // Node1 Score: (100 + 100) / 2 = 100 + // Node2 scores (remaining resources) on 0-10 scale + // CPU Score: ((4000 - 0) *100) / 4000 = 100 + // Memory Score: ((10000 - 0) *10) / 10000 = 100 + // Node2 Score: (100 + 100) / 2 = 100 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: framework.MaxNodeScore}}, + name: "nothing scheduled, nothing requested", + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((4000 - 3000) *100) / 4000 = 25 + // Memory Score: ((10000 - 5000) *100) / 10000 = 50 + // Node1 Score: (25 + 50) / 2 = 37 + // Node2 scores on 0-10 scale + // CPU Score: ((6000 - 3000) *100) / 6000 = 50 + // Memory Score: ((10000 - 5000) *100) / 10000 = 50 + // Node2 Score: (50 + 50) / 2 = 50 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 6000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 37}, {Name: "machine2", Score: 50}}, + name: "nothing scheduled, resources requested, differently sized machines", + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((4000 - 0) *100) / 4000 = 100 + // Memory Score: ((10000 - 0) *100) / 10000 = 100 + // Node1 Score: (100 + 100) / 2 = 100 + // Node2 scores on 0-10 scale + // CPU Score: ((4000 - 0) *100) / 4000 = 100 + // Memory Score: ((10000 - 0) *100) / 10000 = 100 + // Node2 Score: (100 + 100) / 2 = 100 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: framework.MaxNodeScore}, {Name: "machine2", Score: framework.MaxNodeScore}}, + name: "no resources requested, pods scheduled", + pods: []*v1.Pod{ + {Spec: machine1Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels2}}, + {Spec: machine1Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: machine2Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: machine2Spec, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *100) / 10000 = 40 + // Memory Score: ((20000 - 0) *100) / 20000 = 100 + // Node1 Score: (40 + 100) / 2 = 70 + // Node2 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *100) / 10000 = 40 + // Memory Score: ((20000 - 5000) *100) / 20000 = 75 + // Node2 Score: (40 + 75) / 2 = 57 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 70}, {Name: "machine2", Score: 57}}, + name: "no resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels2}}, + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuOnly2, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuAndMemory, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *10) / 10000 = 40 + // Memory Score: ((20000 - 5000) *10) / 20000 = 75 + // Node1 Score: (40 + 75) / 2 = 57 + // Node2 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *100) / 10000 = 40 + // Memory Score: ((20000 - 10000) *100) / 20000 = 50 + // Node2 Score: (40 + 50) / 2 = 45 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 57}, {Name: "machine2", Score: 45}}, + name: "resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *100) / 10000 = 40 + // Memory Score: ((20000 - 5000) *100) / 20000 = 75 + // Node1 Score: (40 + 75) / 2 = 57 + // Node2 scores on 0-10 scale + // CPU Score: ((10000 - 6000) *100) / 10000 = 40 + // Memory Score: ((50000 - 10000) *100) / 50000 = 80 + // Node2 Score: (40 + 80) / 2 = 60 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 50000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 57}, {Name: "machine2", Score: 60}}, + name: "resources requested, pods scheduled with resources, differently sized machines", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: ((4000 - 6000) *100) / 4000 = 0 + // Memory Score: ((10000 - 0) *100) / 10000 = 100 + // Node1 Score: (0 + 100) / 2 = 50 + // Node2 scores on 0-10 scale + // CPU Score: ((4000 - 6000) *100) / 4000 = 0 + // Memory Score: ((10000 - 5000) *100) / 10000 = 50 + // Node2 Score: (0 + 50) / 2 = 25 + pod: &v1.Pod{Spec: cpuOnly}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 50}, {Name: "machine2", Score: 25}}, + name: "requested resources exceed node capacity", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 0, 0), makeNode("machine2", 0, 0)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}}, + name: "zero node resources, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, test.nodes) + fh, _ := framework.NewFramework(nil, nil, nil) + snapshot := fh.NodeInfoSnapshot() + snapshot.NodeInfoMap = nodeNameToInfo + + p, _ := NewLeastAllocated(nil, fh) + for i := range test.nodes { + hostResult, err := p.(framework.ScorePlugin).Score(context.Background(), nil, test.pod, test.nodes[i].Name) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(test.expectedList[i].Score, hostResult) { + t.Errorf("expected %#v, got %#v", test.expectedList[i].Score, hostResult) + } + } + }) + } +} diff --git a/pkg/scheduler/framework/plugins/noderesources/most_allocated.go b/pkg/scheduler/framework/plugins/noderesources/most_allocated.go new file mode 100644 index 00000000000..4ad4dfe3565 --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/most_allocated.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities" + "k8s.io/kubernetes/pkg/scheduler/framework/plugins/migration" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" +) + +// MostAllocated is a score plugin that favors nodes with high allocation based on requested resources. +type MostAllocated struct { + handle framework.FrameworkHandle +} + +var _ = framework.ScorePlugin(&MostAllocated{}) + +// MostAllocatedName is the name of the plugin used in the plugin registry and configurations. +const MostAllocatedName = "NodeResourcesMostAllocated" + +// Name returns name of the plugin. It is used in logs, etc. +func (ma *MostAllocated) Name() string { + return MostAllocatedName +} + +// Score invoked at the Score extension point. +func (ma *MostAllocated) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { + nodeInfo, exist := ma.handle.NodeInfoSnapshot().NodeInfoMap[nodeName] + if !exist { + return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) + } + meta := migration.PriorityMetadata(state) + + s, err := priorities.MostRequestedPriorityMap(pod, meta, nodeInfo) + return s.Score, migration.ErrorToFrameworkStatus(err) +} + +// ScoreExtensions of the Score plugin. +func (ma *MostAllocated) ScoreExtensions() framework.ScoreExtensions { + return nil +} + +// NewMostAllocated initializes a new plugin and returns it. +func NewMostAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) { + return &MostAllocated{handle: h}, nil +} diff --git a/pkg/scheduler/framework/plugins/noderesources/most_allocated_test.go b/pkg/scheduler/framework/plugins/noderesources/most_allocated_test.go new file mode 100644 index 00000000000..d803ffda71b --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/most_allocated_test.go @@ -0,0 +1,216 @@ +/* +Copyright 2019 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 noderesources + +import ( + "context" + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" + schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" +) + +func TestNodeResourcesMostAllocated(t *testing.T) { + labels1 := map[string]string{ + "foo": "bar", + "baz": "blah", + } + labels2 := map[string]string{ + "bar": "foo", + "baz": "blah", + } + noResources := v1.PodSpec{ + Containers: []v1.Container{}, + } + cpuOnly := v1.PodSpec{ + NodeName: "machine1", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("0"), + }, + }, + }, + }, + } + cpuOnly2 := cpuOnly + cpuOnly2.NodeName = "machine2" + cpuAndMemory := v1.PodSpec{ + NodeName: "machine2", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("1000m"), + v1.ResourceMemory: resource.MustParse("2000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("3000"), + }, + }, + }, + }, + } + bigCPUAndMemory := v1.PodSpec{ + NodeName: "machine1", + Containers: []v1.Container{ + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("2000m"), + v1.ResourceMemory: resource.MustParse("4000"), + }, + }, + }, + { + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("3000m"), + v1.ResourceMemory: resource.MustParse("5000"), + }, + }, + }, + }, + } + tests := []struct { + pod *v1.Pod + pods []*v1.Pod + nodes []*v1.Node + expectedList framework.NodeScoreList + name string + }{ + { + // Node1 scores (used resources) on 0-10 scale + // CPU Score: (0 * 100) / 4000 = 0 + // Memory Score: (0 * 100) / 10000 = 0 + // Node1 Score: (0 + 0) / 2 = 0 + // Node2 scores (used resources) on 0-10 scale + // CPU Score: (0 * 100) / 4000 = 0 + // Memory Score: (0 * 100) / 10000 = 0 + // Node2 Score: (0 + 0) / 2 = 0 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 4000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}}, + name: "nothing scheduled, nothing requested", + }, + { + // Node1 scores on 0-10 scale + // CPU Score: (3000 * 100) / 4000 = 75 + // Memory Score: (5000 * 100) / 10000 = 50 + // Node1 Score: (75 + 50) / 2 = 6 + // Node2 scores on 0-10 scale + // CPU Score: (3000 * 100) / 6000 = 50 + // Memory Score: (5000 * 100) / 10000 = 50 + // Node2 Score: (50 + 50) / 2 = 50 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 6000, 10000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 62}, {Name: "machine2", Score: 50}}, + name: "nothing scheduled, resources requested, differently sized machines", + }, + { + // Node1 scores on 0-10 scale + // CPU Score: (6000 * 100) / 10000 = 60 + // Memory Score: (0 * 100) / 20000 = 100 + // Node1 Score: (60 + 0) / 2 = 30 + // Node2 scores on 0-10 scale + // CPU Score: (6000 * 100) / 10000 = 60 + // Memory Score: (5000 * 100) / 20000 = 25 + // Node2 Score: (60 + 25) / 2 = 42 + pod: &v1.Pod{Spec: noResources}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 30}, {Name: "machine2", Score: 42}}, + name: "no resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels2}}, + {Spec: cpuOnly, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuOnly2, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + {Spec: cpuAndMemory, ObjectMeta: metav1.ObjectMeta{Labels: labels1}}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: (6000 * 100) / 10000 = 60 + // Memory Score: (5000 * 100) / 20000 = 25 + // Node1 Score: (60 + 25) / 2 = 42 + // Node2 scores on 0-10 scale + // CPU Score: (6000 * 100) / 10000 = 60 + // Memory Score: (10000 * 100) / 20000 = 50 + // Node2 Score: (60 + 50) / 2 = 55 + pod: &v1.Pod{Spec: cpuAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 10000, 20000), makeNode("machine2", 10000, 20000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 42}, {Name: "machine2", Score: 55}}, + name: "resources requested, pods scheduled with resources", + pods: []*v1.Pod{ + {Spec: cpuOnly}, + {Spec: cpuAndMemory}, + }, + }, + { + // Node1 scores on 0-10 scale + // CPU Score: 5000 > 4000 return 0 + // Memory Score: (9000 * 100) / 10000 = 90 + // Node1 Score: (0 + 90) / 2 = 45 + // Node2 scores on 0-10 scale + // CPU Score: (5000 * 100) / 10000 = 50 + // Memory Score: 9000 > 8000 return 0 + // Node2 Score: (50 + 0) / 2 = 25 + pod: &v1.Pod{Spec: bigCPUAndMemory}, + nodes: []*v1.Node{makeNode("machine1", 4000, 10000), makeNode("machine2", 10000, 8000)}, + expectedList: []framework.NodeScore{{Name: "machine1", Score: 45}, {Name: "machine2", Score: 25}}, + name: "resources requested with more than the node, pods scheduled with resources", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, test.nodes) + fh, _ := framework.NewFramework(nil, nil, nil) + snapshot := fh.NodeInfoSnapshot() + snapshot.NodeInfoMap = nodeNameToInfo + + p, _ := NewMostAllocated(nil, fh) + for i := range test.nodes { + hostResult, err := p.(framework.ScorePlugin).Score(context.Background(), nil, test.pod, test.nodes[i].Name) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(test.expectedList[i].Score, hostResult) { + t.Errorf("expected %#v, got %#v", test.expectedList[i].Score, hostResult) + } + } + }) + } +} diff --git a/pkg/scheduler/framework/plugins/noderesources/test_util.go b/pkg/scheduler/framework/plugins/noderesources/test_util.go new file mode 100644 index 00000000000..bd77f6a0bc6 --- /dev/null +++ b/pkg/scheduler/framework/plugins/noderesources/test_util.go @@ -0,0 +1,39 @@ +/* +Copyright 2019 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 noderesources + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func makeNode(node string, milliCPU, memory int64) *v1.Node { + return &v1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: node}, + Status: v1.NodeStatus{ + Capacity: v1.ResourceList{ + v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), + v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), + }, + Allocatable: v1.ResourceList{ + v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), + v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), + }, + }, + } +} diff --git a/test/integration/scheduler/scheduler_test.go b/test/integration/scheduler/scheduler_test.go index a8f2016dc06..9c0b9bdc865 100644 --- a/test/integration/scheduler/scheduler_test.go +++ b/test/integration/scheduler/scheduler_test.go @@ -135,9 +135,7 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) { "MaxGCEPDVolumeCount", ), expectedPrioritizers: sets.NewString( - "BalancedResourceAllocation", "InterPodAffinityPriority", - "LeastRequestedPriority", "SelectorSpreadPriority", ), expectedPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -155,7 +153,9 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) { {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 1}, {Name: "ImageLocality", Weight: 1}, + {Name: "NodeResourcesLeastAllocated", Weight: 1}, {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "TaintToleration", Weight: 1}, @@ -215,9 +215,7 @@ kind: Policy "MaxGCEPDVolumeCount", ), expectedPrioritizers: sets.NewString( - "BalancedResourceAllocation", "InterPodAffinityPriority", - "LeastRequestedPriority", "SelectorSpreadPriority", ), expectedPlugins: map[string][]kubeschedulerconfig.Plugin{ @@ -235,7 +233,9 @@ kind: Policy {Name: "InterPodAffinity"}, }, "ScorePlugin": { + {Name: "NodeResourcesBalancedAllocation", Weight: 1}, {Name: "ImageLocality", Weight: 1}, + {Name: "NodeResourcesLeastAllocated", Weight: 1}, {Name: "NodeAffinity", Weight: 1}, {Name: "NodePreferAvoidPods", Weight: 10000}, {Name: "TaintToleration", Weight: 1}, @@ -486,25 +486,23 @@ func TestUnschedulableNodes(t *testing.T) { } func TestMultiScheduler(t *testing.T) { - /* - This integration tests the multi-scheduler feature in the following way: - 1. create a default scheduler - 2. create a node - 3. create 3 pods: testPodNoAnnotation, testPodWithAnnotationFitsDefault and testPodWithAnnotationFitsFoo - - note: the first two should be picked and scheduled by default scheduler while the last one should be - picked by scheduler of name "foo-scheduler" which does not exist yet. - 4. **check point-1**: - - testPodNoAnnotation, testPodWithAnnotationFitsDefault should be scheduled - - testPodWithAnnotationFitsFoo should NOT be scheduled - 5. create a scheduler with name "foo-scheduler" - 6. **check point-2**: - - testPodWithAnnotationFitsFoo should be scheduled - 7. stop default scheduler - 8. create 2 pods: testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 - - note: these two pods belong to default scheduler which no longer exists - 9. **check point-3**: - - testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 should NOT be scheduled - */ + // This integration tests the multi-scheduler feature in the following way: + // 1. create a default scheduler + // 2. create a node + // 3. create 3 pods: testPodNoAnnotation, testPodWithAnnotationFitsDefault and testPodWithAnnotationFitsFoo + // - note: the first two should be picked and scheduled by default scheduler while the last one should be + // picked by scheduler of name "foo-scheduler" which does not exist yet. + // 4. **check point-1**: + // - testPodNoAnnotation, testPodWithAnnotationFitsDefault should be scheduled + // - testPodWithAnnotationFitsFoo should NOT be scheduled + // 5. create a scheduler with name "foo-scheduler" + // 6. **check point-2**: + // - testPodWithAnnotationFitsFoo should be scheduled + // 7. stop default scheduler + // 8. create 2 pods: testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 + // - note: these two pods belong to default scheduler which no longer exists + // 9. **check point-3**: + // - testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 should NOT be scheduled // 1. create and start default-scheduler context := initTest(t, "multi-scheduler") From bd4167d149eb3ab41cc94b9a569be16a8db03f43 Mon Sep 17 00:00:00 2001 From: zouyee Date: Sat, 19 Oct 2019 22:43:15 +0800 Subject: [PATCH 2/2] remove unused meta and rename lablance_allocated Signed-off-by: Zou Nengren --- pkg/scheduler/framework/plugins/noderesources/BUILD | 4 ++-- .../{balance_allocated.go => balanced_allocation.go} | 5 ++--- ...balance_allocated_test.go => balanced_allocation_test.go} | 0 .../framework/plugins/noderesources/least_allocated.go | 5 ++--- .../framework/plugins/noderesources/most_allocated.go | 5 ++--- 5 files changed, 8 insertions(+), 11 deletions(-) rename pkg/scheduler/framework/plugins/noderesources/{balance_allocated.go => balanced_allocation.go} (93%) rename pkg/scheduler/framework/plugins/noderesources/{balance_allocated_test.go => balanced_allocation_test.go} (100%) diff --git a/pkg/scheduler/framework/plugins/noderesources/BUILD b/pkg/scheduler/framework/plugins/noderesources/BUILD index 0f1f47da301..81ab961bf00 100644 --- a/pkg/scheduler/framework/plugins/noderesources/BUILD +++ b/pkg/scheduler/framework/plugins/noderesources/BUILD @@ -3,7 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ - "balance_allocated.go", + "balanced_allocation.go", "least_allocated.go", "most_allocated.go", "node_resources.go", @@ -41,7 +41,7 @@ filegroup( go_test( name = "go_default_test", srcs = [ - "balance_allocated_test.go", + "balanced_allocation_test.go", "least_allocated_test.go", "most_allocated_test.go", "node_resources_test.go", diff --git a/pkg/scheduler/framework/plugins/noderesources/balance_allocated.go b/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go similarity index 93% rename from pkg/scheduler/framework/plugins/noderesources/balance_allocated.go rename to pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go index 5e1996f61c9..2ee23724f36 100644 --- a/pkg/scheduler/framework/plugins/noderesources/balance_allocated.go +++ b/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go @@ -49,9 +49,8 @@ func (ba *BalancedAllocation) Score(ctx context.Context, state *framework.CycleS if !exist { return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) } - meta := migration.PriorityMetadata(state) - - s, err := priorities.BalancedResourceAllocationMap(pod, meta, nodeInfo) + // BalancedResourceAllocationMap does not use priority metadata, hence we pass nil here + s, err := priorities.BalancedResourceAllocationMap(pod, nil, nodeInfo) return s.Score, migration.ErrorToFrameworkStatus(err) } diff --git a/pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go b/pkg/scheduler/framework/plugins/noderesources/balanced_allocation_test.go similarity index 100% rename from pkg/scheduler/framework/plugins/noderesources/balance_allocated_test.go rename to pkg/scheduler/framework/plugins/noderesources/balanced_allocation_test.go diff --git a/pkg/scheduler/framework/plugins/noderesources/least_allocated.go b/pkg/scheduler/framework/plugins/noderesources/least_allocated.go index d5d60fb763b..b1c59923ece 100644 --- a/pkg/scheduler/framework/plugins/noderesources/least_allocated.go +++ b/pkg/scheduler/framework/plugins/noderesources/least_allocated.go @@ -48,9 +48,8 @@ func (la *LeastAllocated) Score(ctx context.Context, state *framework.CycleState if !exist { return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) } - meta := migration.PriorityMetadata(state) - - s, err := priorities.LeastRequestedPriorityMap(pod, meta, nodeInfo) + // LeastRequestedPriorityMap does not use priority metadata, hence we pass nil here + s, err := priorities.LeastRequestedPriorityMap(pod, nil, nodeInfo) return s.Score, migration.ErrorToFrameworkStatus(err) } diff --git a/pkg/scheduler/framework/plugins/noderesources/most_allocated.go b/pkg/scheduler/framework/plugins/noderesources/most_allocated.go index 4ad4dfe3565..00d1b11b60d 100644 --- a/pkg/scheduler/framework/plugins/noderesources/most_allocated.go +++ b/pkg/scheduler/framework/plugins/noderesources/most_allocated.go @@ -48,9 +48,8 @@ func (ma *MostAllocated) Score(ctx context.Context, state *framework.CycleState, if !exist { return 0, framework.NewStatus(framework.Error, fmt.Sprintf("node %q does not exist in NodeInfoSnapshot", nodeName)) } - meta := migration.PriorityMetadata(state) - - s, err := priorities.MostRequestedPriorityMap(pod, meta, nodeInfo) + // MostRequestedPriorityMap does not use priority metadata, hence we pass nil here + s, err := priorities.MostRequestedPriorityMap(pod, nil, nodeInfo) return s.Score, migration.ErrorToFrameworkStatus(err) }