From 98041d0925ccb3677a3ad9f24f6d9c9b53b4bb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Osipiuk?= Date: Mon, 21 May 2018 17:58:11 +0200 Subject: [PATCH] Allow parametrization of RequestedToCapacityRatio priority function via policy config --- .../defaults/compatibility_test.go | 24 +++++++++- .../algorithmprovider/defaults/defaults.go | 4 -- pkg/scheduler/api/types.go | 16 +++++++ pkg/scheduler/api/v1/types.go | 16 +++++++ pkg/scheduler/api/v1/zz_generated.deepcopy.go | 46 +++++++++++++++++++ pkg/scheduler/api/zz_generated.deepcopy.go | 46 +++++++++++++++++++ pkg/scheduler/factory/BUILD | 2 + pkg/scheduler/factory/plugins.go | 25 ++++++++++ pkg/scheduler/factory/plugins_test.go | 18 ++++++++ 9 files changed, 191 insertions(+), 6 deletions(-) diff --git a/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go b/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go index f6c351bc52..06e793a518 100644 --- a/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go +++ b/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go @@ -640,7 +640,17 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {"name": "TaintTolerationPriority", "weight": 2}, {"name": "InterPodAffinityPriority", "weight": 2}, {"name": "MostRequestedPriority", "weight": 2}, - {"name": "RequestedToCapacityRatioPriority", "weight": 2} + { + "name": "RequestedToCapacityRatioPriority", + "weight": 2, + "argument": { + "requestedToCapacityRatioArguments": { + "shape": [ + {"utilization": 0, "score": 0}, + {"utilization": 50, "score": 7} + ] + } + }} ] }`, ExpectedPolicy: schedulerapi.Policy{ @@ -676,7 +686,17 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { {Name: "TaintTolerationPriority", Weight: 2}, {Name: "InterPodAffinityPriority", Weight: 2}, {Name: "MostRequestedPriority", Weight: 2}, - {Name: "RequestedToCapacityRatioPriority", Weight: 2}, + { + Name: "RequestedToCapacityRatioPriority", + Weight: 2, + Argument: &schedulerapi.PriorityArgument{ + RequestedToCapacityRatioArguments: &schedulerapi.RequestedToCapacityRatioArguments{ + UtilizationShape: []schedulerapi.UtilizationShapePoint{ + {Utilization: 0, Score: 0}, + {Utilization: 50, Score: 7}, + }}, + }, + }, }, }, }, diff --git a/pkg/scheduler/algorithmprovider/defaults/defaults.go b/pkg/scheduler/algorithmprovider/defaults/defaults.go index 932bd9c4df..94d73b7e7f 100644 --- a/pkg/scheduler/algorithmprovider/defaults/defaults.go +++ b/pkg/scheduler/algorithmprovider/defaults/defaults.go @@ -100,10 +100,6 @@ func init() { factory.RegisterPriorityFunction2("ImageLocalityPriority", priorities.ImageLocalityPriorityMap, nil, 1) // Optional, cluster-autoscaler friendly priority function - give used nodes higher priority. factory.RegisterPriorityFunction2("MostRequestedPriority", priorities.MostRequestedPriorityMap, nil, 1) - - // TODO use RequestedToCapacityRatioResourceAllocationPriority(scoringFunctionShape functionShape) - // and build scoringFunctionShape based on configuration passed to scheduler on startup. - // What is proper way of passing configuration here? config file? startup flag? factory.RegisterPriorityFunction2( "RequestedToCapacityRatioPriority", priorities.RequestedToCapacityRatioResourceAllocationPriorityDefault().PriorityMap, diff --git a/pkg/scheduler/api/types.go b/pkg/scheduler/api/types.go index 8ce5d205bb..4964349ade 100644 --- a/pkg/scheduler/api/types.go +++ b/pkg/scheduler/api/types.go @@ -109,6 +109,8 @@ type PriorityArgument struct { // The priority function that checks whether a particular node has a certain label // defined or not, regardless of value LabelPreference *LabelPreference + // The RequestedToCapacityRatio priority function is parametrized with function shape. + RequestedToCapacityRatioArguments *RequestedToCapacityRatioArguments } // ServiceAffinity holds the parameters that are used to configure the corresponding predicate in scheduler policy configuration. @@ -143,6 +145,20 @@ type LabelPreference struct { Presence bool } +// RequestedToCapacityRatioArguments holds arguments specific to RequestedToCapacityRatio priority function +type RequestedToCapacityRatioArguments struct { + // Array of point defining priority function shape + UtilizationShape []UtilizationShapePoint +} + +// UtilizationShapePoint represents single point of priority function shape +type UtilizationShapePoint struct { + // Utilization (x axis). Valid values are 0 to 100. Fully utilized node maps to 100. + Utilization int + // Score assigned to given utilization (y axis). Valid values are 0 to 10. + Score int +} + // ExtenderManagedResource describes the arguments of extended resources // managed by an extender. type ExtenderManagedResource struct { diff --git a/pkg/scheduler/api/v1/types.go b/pkg/scheduler/api/v1/types.go index 21412c3ea1..4058d06889 100644 --- a/pkg/scheduler/api/v1/types.go +++ b/pkg/scheduler/api/v1/types.go @@ -91,6 +91,8 @@ type PriorityArgument struct { // The priority function that checks whether a particular node has a certain label // defined or not, regardless of value LabelPreference *LabelPreference `json:"labelPreference"` + // The RequestedToCapacityRatio priority function is parametrized with function shape. + RequestedToCapacityRatioArguments *RequestedToCapacityRatioArguments `json:"requestedToCapacityRatioArguments"` } // ServiceAffinity holds the parameters that are used to configure the corresponding predicate in scheduler policy configuration. @@ -125,6 +127,20 @@ type LabelPreference struct { Presence bool `json:"presence"` } +// RequestedToCapacityRatioArguments holds arguments specific to RequestedToCapacityRatio priority function +type RequestedToCapacityRatioArguments struct { + // Array of point defining priority function shape + UtilizationShape []UtilizationShapePoint `json:"shape"` +} + +// UtilizationShapePoint represents single point of priority function shape +type UtilizationShapePoint struct { + // Utilization (x axis). Valid values are 0 to 100. Fully utilized node maps to 100. + Utilization int `json:"utilization"` + // Score assigned to given utilization (y axis). Valid values are 0 to 10. + Score int `json:"score"` +} + // ExtenderManagedResource describes the arguments of extended resources // managed by an extender. type ExtenderManagedResource struct { diff --git a/pkg/scheduler/api/v1/zz_generated.deepcopy.go b/pkg/scheduler/api/v1/zz_generated.deepcopy.go index 130637cc03..cbe8dc90c4 100644 --- a/pkg/scheduler/api/v1/zz_generated.deepcopy.go +++ b/pkg/scheduler/api/v1/zz_generated.deepcopy.go @@ -538,6 +538,15 @@ func (in *PriorityArgument) DeepCopyInto(out *PriorityArgument) { **out = **in } } + if in.RequestedToCapacityRatioArguments != nil { + in, out := &in.RequestedToCapacityRatioArguments, &out.RequestedToCapacityRatioArguments + if *in == nil { + *out = nil + } else { + *out = new(RequestedToCapacityRatioArguments) + (*in).DeepCopyInto(*out) + } + } return } @@ -576,6 +585,27 @@ func (in *PriorityPolicy) DeepCopy() *PriorityPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestedToCapacityRatioArguments) DeepCopyInto(out *RequestedToCapacityRatioArguments) { + *out = *in + if in.UtilizationShape != nil { + in, out := &in.UtilizationShape, &out.UtilizationShape + *out = make([]UtilizationShapePoint, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestedToCapacityRatioArguments. +func (in *RequestedToCapacityRatioArguments) DeepCopy() *RequestedToCapacityRatioArguments { + if in == nil { + return nil + } + out := new(RequestedToCapacityRatioArguments) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAffinity) DeepCopyInto(out *ServiceAffinity) { *out = *in @@ -613,6 +643,22 @@ func (in *ServiceAntiAffinity) DeepCopy() *ServiceAntiAffinity { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UtilizationShapePoint) DeepCopyInto(out *UtilizationShapePoint) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UtilizationShapePoint. +func (in *UtilizationShapePoint) DeepCopy() *UtilizationShapePoint { + if in == nil { + return nil + } + out := new(UtilizationShapePoint) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Victims) DeepCopyInto(out *Victims) { *out = *in diff --git a/pkg/scheduler/api/zz_generated.deepcopy.go b/pkg/scheduler/api/zz_generated.deepcopy.go index 78396741f0..d8654dd27a 100644 --- a/pkg/scheduler/api/zz_generated.deepcopy.go +++ b/pkg/scheduler/api/zz_generated.deepcopy.go @@ -538,6 +538,15 @@ func (in *PriorityArgument) DeepCopyInto(out *PriorityArgument) { **out = **in } } + if in.RequestedToCapacityRatioArguments != nil { + in, out := &in.RequestedToCapacityRatioArguments, &out.RequestedToCapacityRatioArguments + if *in == nil { + *out = nil + } else { + *out = new(RequestedToCapacityRatioArguments) + (*in).DeepCopyInto(*out) + } + } return } @@ -576,6 +585,27 @@ func (in *PriorityPolicy) DeepCopy() *PriorityPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestedToCapacityRatioArguments) DeepCopyInto(out *RequestedToCapacityRatioArguments) { + *out = *in + if in.UtilizationShape != nil { + in, out := &in.UtilizationShape, &out.UtilizationShape + *out = make([]UtilizationShapePoint, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestedToCapacityRatioArguments. +func (in *RequestedToCapacityRatioArguments) DeepCopy() *RequestedToCapacityRatioArguments { + if in == nil { + return nil + } + out := new(RequestedToCapacityRatioArguments) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceAffinity) DeepCopyInto(out *ServiceAffinity) { *out = *in @@ -613,6 +643,22 @@ func (in *ServiceAntiAffinity) DeepCopy() *ServiceAntiAffinity { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UtilizationShapePoint) DeepCopyInto(out *UtilizationShapePoint) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UtilizationShapePoint. +func (in *UtilizationShapePoint) DeepCopy() *UtilizationShapePoint { + if in == nil { + return nil + } + out := new(UtilizationShapePoint) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Victims) DeepCopyInto(out *Victims) { *out = *in diff --git a/pkg/scheduler/factory/BUILD b/pkg/scheduler/factory/BUILD index 6f5688acdc..faf552687c 100644 --- a/pkg/scheduler/factory/BUILD +++ b/pkg/scheduler/factory/BUILD @@ -103,12 +103,14 @@ go_test( "//pkg/api/testing:go_default_library", "//pkg/scheduler:go_default_library", "//pkg/scheduler/algorithm:go_default_library", + "//pkg/scheduler/algorithm/priorities:go_default_library", "//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api/latest:go_default_library", "//pkg/scheduler/cache:go_default_library", "//pkg/scheduler/core:go_default_library", "//pkg/scheduler/testing:go_default_library", "//pkg/scheduler/util:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/policy/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/scheduler/factory/plugins.go b/pkg/scheduler/factory/plugins.go index ba481dde93..f9a668b4a9 100644 --- a/pkg/scheduler/factory/plugins.go +++ b/pkg/scheduler/factory/plugins.go @@ -322,6 +322,15 @@ func RegisterCustomPriorityFunction(policy schedulerapi.PriorityPolicy) string { }, Weight: policy.Weight, } + } else if policy.Argument.RequestedToCapacityRatioArguments != nil { + pcf = &PriorityConfigFactory{ + MapReduceFunction: func(args PluginFactoryArgs) (algorithm.PriorityMapFunction, algorithm.PriorityReduceFunction) { + scoringFunctionShape := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(policy.Argument.RequestedToCapacityRatioArguments) + p := priorities.RequestedToCapacityRatioResourceAllocationPriority(scoringFunctionShape) + return p.PriorityMap, nil + }, + Weight: policy.Weight, + } } } else if existingPcf, ok := priorityFunctionMap[policy.Name]; ok { glog.V(2).Infof("Priority type %s already registered, reusing.", policy.Name) @@ -340,6 +349,19 @@ func RegisterCustomPriorityFunction(policy schedulerapi.PriorityPolicy) string { return RegisterPriorityConfigFactory(policy.Name, *pcf) } +func buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(arguments *schedulerapi.RequestedToCapacityRatioArguments) priorities.FunctionShape { + n := len(arguments.UtilizationShape) + points := make([]priorities.FunctionShapePoint, 0, n) + for _, point := range arguments.UtilizationShape { + points = append(points, priorities.FunctionShapePoint{Utilization: int64(point.Utilization), Score: int64(point.Score)}) + } + shape, err := priorities.NewFunctionShape(points) + if err != nil { + glog.Fatalf("invalid RequestedToCapacityRatioPriority arguments: %s", err.Error()) + } + return shape +} + // IsPriorityFunctionRegistered is useful for testing providers. func IsPriorityFunctionRegistered(name string) bool { schedulerFactoryMutex.Lock() @@ -494,6 +516,9 @@ func validatePriorityOrDie(priority schedulerapi.PriorityPolicy) { if priority.Argument.LabelPreference != nil { numArgs++ } + if priority.Argument.RequestedToCapacityRatioArguments != nil { + numArgs++ + } if numArgs != 1 { glog.Fatalf("Exactly 1 priority argument is required, numArgs: %v, Priority: %s", numArgs, priority.Name) } diff --git a/pkg/scheduler/factory/plugins_test.go b/pkg/scheduler/factory/plugins_test.go index a3508c139d..ee813de97a 100644 --- a/pkg/scheduler/factory/plugins_test.go +++ b/pkg/scheduler/factory/plugins_test.go @@ -19,7 +19,9 @@ package factory import ( "testing" + "github.com/stretchr/testify/assert" "k8s.io/kubernetes/pkg/scheduler/algorithm" + "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities" "k8s.io/kubernetes/pkg/scheduler/api" ) @@ -80,3 +82,19 @@ func TestValidatePriorityConfigOverFlow(t *testing.T) { } } } + +func TestBuildScoringFunctionShapeFromRequestedToCapacityRatioArguments(t *testing.T) { + arguments := api.RequestedToCapacityRatioArguments{ + UtilizationShape: []api.UtilizationShapePoint{ + {Utilization: 10, Score: 1}, + {Utilization: 30, Score: 5}, + {Utilization: 70, Score: 2}, + }} + builtShape := buildScoringFunctionShapeFromRequestedToCapacityRatioArguments(&arguments) + expectedShape, _ := priorities.NewFunctionShape([]priorities.FunctionShapePoint{ + {Utilization: 10, Score: 1}, + {Utilization: 30, Score: 5}, + {Utilization: 70, Score: 2}, + }) + assert.Equal(t, expectedShape, builtShape) +}