Merge pull request #103169 from Huang-Wei/res-scorer

Optimize scheduler res scorer on non-requested extended res
This commit is contained in:
Kubernetes Prow Robot 2021-06-26 04:21:23 -07:00 committed by GitHub
commit df2e13376d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 111 additions and 38 deletions

View File

@ -95,7 +95,8 @@ func NewLeastAllocated(laArgs runtime.Object, h framework.Handle, fts feature.Fe
func leastResourceScorer(resToWeightMap resourceToWeightMap) func(resourceToValueMap, resourceToValueMap) int64 { func leastResourceScorer(resToWeightMap resourceToWeightMap) func(resourceToValueMap, resourceToValueMap) int64 {
return func(requested, allocable resourceToValueMap) int64 { return func(requested, allocable resourceToValueMap) int64 {
var nodeScore, weightSum int64 var nodeScore, weightSum int64
for resource, weight := range resToWeightMap { for resource := range requested {
weight := resToWeightMap[resource]
resourceScore := leastRequestedScore(requested[resource], allocable[resource]) resourceScore := leastRequestedScore(requested[resource], allocable[resource])
nodeScore += resourceScore * weight nodeScore += resourceScore * weight
weightSum += weight weightSum += weight

View File

@ -100,6 +100,14 @@ func TestNodeResourcesLeastAllocated(t *testing.T) {
{Name: string(v1.ResourceCPU), Weight: 1}, {Name: string(v1.ResourceCPU), Weight: 1},
{Name: string(v1.ResourceMemory), Weight: 1}, {Name: string(v1.ResourceMemory), Weight: 1},
} }
extendedRes := "abc.com/xyz"
extendedResourceLeastAllocatedSet := []config.ResourceSpec{
{Name: string(v1.ResourceCPU), Weight: 1},
{Name: string(v1.ResourceMemory), Weight: 1},
{Name: extendedRes, Weight: 1},
}
cpuMemoryAndExtendedRes := *cpuAndMemory.DeepCopy()
cpuMemoryAndExtendedRes.Containers[0].Resources.Requests[v1.ResourceName(extendedRes)] = resource.MustParse("2")
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
pods []*v1.Pod pods []*v1.Pod
@ -270,7 +278,7 @@ func TestNodeResourcesLeastAllocated(t *testing.T) {
name: "nothing scheduled, resources requested with different weight on CPU and memory, differently sized machines", name: "nothing scheduled, resources requested with different weight on CPU and memory, differently sized machines",
}, },
{ {
// resource with negtive weight is not allowed // resource with negative weight is not allowed
pod: &v1.Pod{Spec: cpuAndMemory}, pod: &v1.Pod{Spec: cpuAndMemory},
nodes: []*v1.Node{makeNode("machine", 4000, 10000)}, nodes: []*v1.Node{makeNode("machine", 4000, 10000)},
args: config.NodeResourcesLeastAllocatedArgs{Resources: []config.ResourceSpec{{Name: "memory", Weight: -1}, {Name: "cpu", Weight: 1}}}, args: config.NodeResourcesLeastAllocatedArgs{Resources: []config.ResourceSpec{{Name: "memory", Weight: -1}, {Name: "cpu", Weight: 1}}},
@ -308,6 +316,40 @@ func TestNodeResourcesLeastAllocated(t *testing.T) {
}.ToAggregate(), }.ToAggregate(),
name: "resource weight larger than MaxNodeScore", name: "resource weight larger than MaxNodeScore",
}, },
{
// Bypass extended resource if the pod does not request.
// For both nodes: cpuScore and memScore are 50
// Given that extended resource score are intentionally bypassed,
// the final scores are:
// - node1: (50 + 50) / 2 = 50
// - node2: (50 + 50) / 2 = 50
pod: &v1.Pod{Spec: cpuAndMemory},
nodes: []*v1.Node{
makeNode("machine1", 6000, 10000),
makeNodeWithExtendedResource("machine2", 6000, 10000, map[string]int64{extendedRes: 4}),
},
args: config.NodeResourcesLeastAllocatedArgs{Resources: extendedResourceLeastAllocatedSet},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 50}, {Name: "machine2", Score: 50}},
name: "bypass extended resource if the pod does not request",
},
{
// Honor extended resource if the pod requests.
// For both nodes: cpuScore and memScore are 50.
// In terms of extended resource score:
// - node1 get: 2 / 4 * 100 = 50
// - node2 get: (10 - 2) / 10 * 100 = 80
// So the final scores are:
// - node1: (50 + 50 + 50) / 3 = 50
// - node2: (50 + 50 + 80) / 3 = 60
pod: &v1.Pod{Spec: cpuMemoryAndExtendedRes},
nodes: []*v1.Node{
makeNodeWithExtendedResource("machine1", 6000, 10000, map[string]int64{extendedRes: 4}),
makeNodeWithExtendedResource("machine2", 6000, 10000, map[string]int64{extendedRes: 10}),
},
args: config.NodeResourcesLeastAllocatedArgs{Resources: extendedResourceLeastAllocatedSet},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 50}, {Name: "machine2", Score: 60}},
name: "honor extended resource if the pod requests",
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -93,7 +93,8 @@ func NewMostAllocated(maArgs runtime.Object, h framework.Handle, fts feature.Fea
func mostResourceScorer(resToWeightMap resourceToWeightMap) func(requested, allocable resourceToValueMap) int64 { func mostResourceScorer(resToWeightMap resourceToWeightMap) func(requested, allocable resourceToValueMap) int64 {
return func(requested, allocable resourceToValueMap) int64 { return func(requested, allocable resourceToValueMap) int64 {
var nodeScore, weightSum int64 var nodeScore, weightSum int64
for resource, weight := range resToWeightMap { for resource := range requested {
weight := resToWeightMap[resource]
resourceScore := mostRequestedScore(requested[resource], allocable[resource]) resourceScore := mostRequestedScore(requested[resource], allocable[resource])
nodeScore += resourceScore * weight nodeScore += resourceScore * weight
weightSum += weight weightSum += weight
@ -101,7 +102,7 @@ func mostResourceScorer(resToWeightMap resourceToWeightMap) func(requested, allo
if weightSum == 0 { if weightSum == 0 {
return 0 return 0
} }
return (nodeScore / weightSum) return nodeScore / weightSum
} }
} }

View File

@ -122,6 +122,14 @@ func TestNodeResourcesMostAllocated(t *testing.T) {
{Name: string(v1.ResourceCPU), Weight: 1}, {Name: string(v1.ResourceCPU), Weight: 1},
{Name: string(v1.ResourceMemory), Weight: 1}, {Name: string(v1.ResourceMemory), Weight: 1},
} }
extendedRes := "abc.com/xyz"
extendedResourceMostAllocatedSet := []config.ResourceSpec{
{Name: string(v1.ResourceCPU), Weight: 1},
{Name: string(v1.ResourceMemory), Weight: 1},
{Name: extendedRes, Weight: 1},
}
cpuMemoryAndExtendedRes := *cpuAndMemory.DeepCopy()
cpuMemoryAndExtendedRes.Containers[0].Resources.Requests[v1.ResourceName(extendedRes)] = resource.MustParse("2")
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
pods []*v1.Pod pods []*v1.Pod
@ -294,6 +302,40 @@ func TestNodeResourcesMostAllocated(t *testing.T) {
}.ToAggregate(), }.ToAggregate(),
name: "resource weight larger than MaxNodeScore", name: "resource weight larger than MaxNodeScore",
}, },
{
// Bypass extended resource if the pod does not request.
// For both nodes: cpuScore and memScore are 50
// Given that extended resource score are intentionally bypassed,
// the final scores are:
// - node1: (50 + 50) / 2 = 50
// - node2: (50 + 50) / 2 = 50
pod: &v1.Pod{Spec: cpuAndMemory},
nodes: []*v1.Node{
makeNode("machine1", 6000, 10000),
makeNodeWithExtendedResource("machine2", 6000, 10000, map[string]int64{extendedRes: 4}),
},
args: config.NodeResourcesMostAllocatedArgs{Resources: extendedResourceMostAllocatedSet},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 50}, {Name: "machine2", Score: 50}},
name: "bypass extended resource if the pod does not request",
},
{
// Honor extended resource if the pod requests.
// For both nodes: cpuScore and memScore are 50.
// In terms of extended resource score:
// - node1 get: 2 / 4 * 100 = 50
// - node2 get: 2 / 10 * 100 = 20
// So the final scores are:
// - node1: (50 + 50 + 50) / 3 = 50
// - node2: (50 + 50 + 20) / 3 = 40
pod: &v1.Pod{Spec: cpuMemoryAndExtendedRes},
nodes: []*v1.Node{
makeNodeWithExtendedResource("machine1", 6000, 10000, map[string]int64{extendedRes: 4}),
makeNodeWithExtendedResource("machine2", 6000, 10000, map[string]int64{extendedRes: 10}),
},
args: config.NodeResourcesMostAllocatedArgs{Resources: extendedResourceMostAllocatedSet},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 50}, {Name: "machine2", Score: 40}},
name: "honor extended resource if the pod requests",
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -125,7 +125,8 @@ func buildRequestedToCapacityRatioScorerFunction(scoringFunctionShape helper.Fun
} }
return func(requested, allocable resourceToValueMap) int64 { return func(requested, allocable resourceToValueMap) int64 {
var nodeScore, weightSum int64 var nodeScore, weightSum int64
for resource, weight := range resourceToWeightMap { for resource := range requested {
weight := resourceToWeightMap[resource]
resourceScore := resourceScoringFunction(requested[resource], allocable[resource]) resourceScore := resourceScoringFunction(requested[resource], allocable[resource])
if resourceScore > 0 { if resourceScore > 0 {
nodeScore += resourceScore * weight nodeScore += resourceScore * weight

View File

@ -191,7 +191,6 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
extendedResource1 := map[string]int64{ extendedResource1 := map[string]int64{
"intel.com/foo": 4, "intel.com/foo": 4,
} }
extendedResource2 := map[string]int64{ extendedResource2 := map[string]int64{
"intel.com/foo": 8, "intel.com/foo": 8,
} }
@ -231,27 +230,13 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
name string name string
}{ }{
{ {
// Node1 Score = Node2 Score = 0 as the incoming Pod doesn't request extended resource.
// Node1 scores (used resources) on 0-MaxNodeScore scale
// Node1 Score:
// rawScoringFunction(used + requested / available)
// resourceScoringFunction((0+0),8)
// = 0/8 * maxUtilization = 0 = rawScoringFunction(0)
// Node1 Score: 0
// Node2 scores (used resources) on 0-MaxNodeScore scale
// rawScoringFunction(used + requested / available)
// resourceScoringFunction((0+0),4)
// = 0/4 * maxUtilization = 0 = rawScoringFunction(0)
// Node2 Score: 0
pod: &v1.Pod{Spec: noResources}, pod: &v1.Pod{Spec: noResources},
nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)}, nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}}, expectedList: []framework.NodeScore{{Name: "machine1", Score: 0}, {Name: "machine2", Score: 0}},
name: "nothing scheduled, nothing requested", name: "nothing scheduled, nothing requested",
}, },
{ {
// Node1 scores (used resources) on 0-MaxNodeScore scale // Node1 scores (used resources) on 0-MaxNodeScore scale
// Node1 Score: // Node1 Score:
// rawScoringFunction(used + requested / available) // rawScoringFunction(used + requested / available)
@ -263,7 +248,6 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
// resourceScoringFunction((0+2),4) // resourceScoringFunction((0+2),4)
// = 2/4 * maxUtilization = 50 = rawScoringFunction(50) // = 2/4 * maxUtilization = 50 = rawScoringFunction(50)
// Node2 Score: 5 // Node2 Score: 5
pod: &v1.Pod{Spec: extendedResourcePod1}, pod: &v1.Pod{Spec: extendedResourcePod1},
nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)}, nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 2}, {Name: "machine2", Score: 5}}, expectedList: []framework.NodeScore{{Name: "machine1", Score: 2}, {Name: "machine2", Score: 5}},
@ -272,9 +256,7 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
{Spec: noResources}, {Spec: noResources},
}, },
}, },
{ {
// Node1 scores (used resources) on 0-MaxNodeScore scale // Node1 scores (used resources) on 0-MaxNodeScore scale
// Node1 Score: // Node1 Score:
// rawScoringFunction(used + requested / available) // rawScoringFunction(used + requested / available)
@ -286,7 +268,6 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
// resourceScoringFunction((2+2),4) // resourceScoringFunction((2+2),4)
// = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization) // = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization)
// Node2 Score: 10 // Node2 Score: 10
pod: &v1.Pod{Spec: extendedResourcePod1}, pod: &v1.Pod{Spec: extendedResourcePod1},
nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)}, nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 2}, {Name: "machine2", Score: 10}}, expectedList: []framework.NodeScore{{Name: "machine1", Score: 2}, {Name: "machine2", Score: 10}},
@ -295,9 +276,7 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
{Spec: machine2Pod}, {Spec: machine2Pod},
}, },
}, },
{ {
// Node1 scores (used resources) on 0-MaxNodeScore scale // Node1 scores (used resources) on 0-MaxNodeScore scale
// Node1 Score: // Node1 Score:
// rawScoringFunction(used + requested / available) // rawScoringFunction(used + requested / available)
@ -309,7 +288,6 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
// resourceScoringFunction((0+4),4) // resourceScoringFunction((0+4),4)
// = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization) // = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization)
// Node2 Score: 10 // Node2 Score: 10
pod: &v1.Pod{Spec: extendedResourcePod2}, pod: &v1.Pod{Spec: extendedResourcePod2},
nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)}, nodes: []*v1.Node{makeNodeWithExtendedResource("machine1", 4000, 10000*1024*1024, extendedResource2), makeNodeWithExtendedResource("machine2", 4000, 10000*1024*1024, extendedResource1)},
expectedList: []framework.NodeScore{{Name: "machine1", Score: 5}, {Name: "machine2", Score: 10}}, expectedList: []framework.NodeScore{{Name: "machine1", Score: 5}, {Name: "machine2", Score: 10}},

View File

@ -52,14 +52,17 @@ func (r *resourceAllocationScorer) score(
if r.resourceToWeightMap == nil { if r.resourceToWeightMap == nil {
return 0, framework.NewStatus(framework.Error, "resources not found") return 0, framework.NewStatus(framework.Error, "resources not found")
} }
requested := make(resourceToValueMap, len(r.resourceToWeightMap)) requested := make(resourceToValueMap)
allocatable := make(resourceToValueMap, len(r.resourceToWeightMap)) allocatable := make(resourceToValueMap)
for resource := range r.resourceToWeightMap { for resource := range r.resourceToWeightMap {
allocatable[resource], requested[resource] = calculateResourceAllocatableRequest(nodeInfo, pod, resource, r.enablePodOverhead) alloc, req := calculateResourceAllocatableRequest(nodeInfo, pod, resource, r.enablePodOverhead)
if alloc != 0 {
// Only fill the extended resource entry when it's non-zero.
allocatable[resource], requested[resource] = alloc, req
}
} }
var score int64
score = r.scorer(requested, allocatable) score := r.scorer(requested, allocatable)
if klog.V(10).Enabled() { if klog.V(10).Enabled() {
klog.Infof( klog.Infof(
@ -72,15 +75,22 @@ func (r *resourceAllocationScorer) score(
return score, nil return score, nil
} }
// calculateResourceAllocatableRequest returns resources Allocatable and Requested values // calculateResourceAllocatableRequest returns 2 parameters:
// - 1st param: quantity of allocatable resource on the node.
// - 2nd param: aggregated quantity of requested resource on the node.
// Note: if it's an extended resource, and the pod doesn't request it, (0, 0) is returned.
func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.Pod, resource v1.ResourceName, enablePodOverhead bool) (int64, int64) { func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.Pod, resource v1.ResourceName, enablePodOverhead bool) (int64, int64) {
podRequest := calculatePodResourceRequest(pod, resource, enablePodOverhead) podRequest := calculatePodResourceRequest(pod, resource, enablePodOverhead)
// If it's an extended resource, and the pod doesn't request it. We return (0, 0)
// as an implication to bypass scoring on this resource.
if podRequest == 0 && schedutil.IsScalarResourceName(resource) {
return 0, 0
}
switch resource { switch resource {
case v1.ResourceCPU: case v1.ResourceCPU:
return nodeInfo.Allocatable.MilliCPU, (nodeInfo.NonZeroRequested.MilliCPU + podRequest) return nodeInfo.Allocatable.MilliCPU, (nodeInfo.NonZeroRequested.MilliCPU + podRequest)
case v1.ResourceMemory: case v1.ResourceMemory:
return nodeInfo.Allocatable.Memory, (nodeInfo.NonZeroRequested.Memory + podRequest) return nodeInfo.Allocatable.Memory, (nodeInfo.NonZeroRequested.Memory + podRequest)
case v1.ResourceEphemeralStorage: case v1.ResourceEphemeralStorage:
return nodeInfo.Allocatable.EphemeralStorage, (nodeInfo.Requested.EphemeralStorage + podRequest) return nodeInfo.Allocatable.EphemeralStorage, (nodeInfo.Requested.EphemeralStorage + podRequest)
default: default:
@ -89,9 +99,7 @@ func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.P
} }
} }
if klog.V(10).Enabled() { if klog.V(10).Enabled() {
klog.Infof("requested resource %v not considered for node score calculation", klog.Infof("requested resource %v not considered for node score calculation", resource)
resource,
)
} }
return 0, 0 return 0, 0
} }