mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 02:11:09 +00:00
scheduler(NodeResourcesFit & NodeResourcesBalancedAllocation): calculatePodResourceRequest in PreScore phase (#115655)
* scheduler(NodeResourcesFit): calculatePodResourceRequest in PreScore phase * scheduler(NodeResourcesFit and NodeResourcesBalancedAllocation): calculatePodResourceRequest in PreScore phase * modify the comments and tests. * revert the tests. * don't need consider nodes. * use list instead of map. * add comment for podRequests. * avoid using negative wording in variable names.
This commit is contained in:
parent
2e3c5003b9
commit
be080584c6
@ -402,6 +402,7 @@ leaderElection:
|
||||
{Name: "NodePorts"},
|
||||
}
|
||||
plugins.PreScore.Enabled = []config.Plugin{
|
||||
{Name: "NodeResourcesFit"},
|
||||
{Name: "InterPodAffinity"},
|
||||
{Name: "TaintToleration"},
|
||||
}
|
||||
@ -431,6 +432,7 @@ leaderElection:
|
||||
{Name: "NodePorts"},
|
||||
}
|
||||
plugins.PreScore.Enabled = []config.Plugin{
|
||||
{Name: "NodeResourcesFit"},
|
||||
{Name: "InterPodAffinity"},
|
||||
{Name: "TaintToleration"},
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ var PluginsV1beta2 = &config.Plugins{
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: config.PluginSet{
|
||||
@ -238,8 +240,10 @@ var ExpandedPluginsV1beta3 = &config.Plugins{
|
||||
Enabled: []config.Plugin{
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.InterPodAffinity},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: config.PluginSet{
|
||||
@ -415,8 +419,10 @@ var ExpandedPluginsV1 = &config.Plugins{
|
||||
Enabled: []config.Plugin{
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.InterPodAffinity},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: config.PluginSet{
|
||||
|
@ -76,6 +76,8 @@ func getDefaultPlugins() *v1beta2.Plugins {
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: v1beta2.PluginSet{
|
||||
|
@ -88,6 +88,8 @@ func TestApplyFeatureGates(t *testing.T) {
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: v1beta2.PluginSet{
|
||||
@ -176,6 +178,8 @@ func TestApplyFeatureGates(t *testing.T) {
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: v1beta2.PluginSet{
|
||||
|
@ -378,6 +378,8 @@ func TestSchedulerDefaults(t *testing.T) {
|
||||
{Name: names.PodTopologySpread},
|
||||
{Name: names.TaintToleration},
|
||||
{Name: names.NodeAffinity},
|
||||
{Name: names.NodeResourcesFit},
|
||||
{Name: names.NodeResourcesBalancedAllocation},
|
||||
},
|
||||
},
|
||||
Score: v1beta2.PluginSet{
|
||||
|
@ -37,10 +37,51 @@ type BalancedAllocation struct {
|
||||
resourceAllocationScorer
|
||||
}
|
||||
|
||||
var _ framework.PreScorePlugin = &BalancedAllocation{}
|
||||
var _ framework.ScorePlugin = &BalancedAllocation{}
|
||||
|
||||
// BalancedAllocationName is the name of the plugin used in the plugin registry and configurations.
|
||||
const BalancedAllocationName = names.NodeResourcesBalancedAllocation
|
||||
const (
|
||||
BalancedAllocationName = names.NodeResourcesBalancedAllocation
|
||||
|
||||
// balancedAllocationPreScoreStateKey is the key in CycleState to NodeResourcesBalancedAllocation pre-computed data for Scoring.
|
||||
balancedAllocationPreScoreStateKey = "PreScore" + BalancedAllocationName
|
||||
)
|
||||
|
||||
// balancedAllocationPreScoreState computed at PreScore and used at Score.
|
||||
type balancedAllocationPreScoreState struct {
|
||||
// podRequests have the same order of the resources defined in NodeResourcesFitArgs.Resources,
|
||||
// same for other place we store a list like that.
|
||||
podRequests []int64
|
||||
}
|
||||
|
||||
// Clone implements the mandatory Clone interface. We don't really copy the data since
|
||||
// there is no need for that.
|
||||
func (s *balancedAllocationPreScoreState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// PreScore calculates incoming pod's resource requests and writes them to the cycle state used.
|
||||
func (ba *BalancedAllocation) PreScore(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status {
|
||||
state := &balancedAllocationPreScoreState{
|
||||
podRequests: ba.calculatePodResourceRequestList(pod, ba.resources),
|
||||
}
|
||||
cycleState.Write(balancedAllocationPreScoreStateKey, state)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBalancedAllocationPreScoreState(cycleState *framework.CycleState) (*balancedAllocationPreScoreState, error) {
|
||||
c, err := cycleState.Read(balancedAllocationPreScoreStateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %q from cycleState: %w", balancedAllocationPreScoreStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(*balancedAllocationPreScoreState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid PreScore state, got type %T", c)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (ba *BalancedAllocation) Name() string {
|
||||
@ -54,12 +95,17 @@ func (ba *BalancedAllocation) Score(ctx context.Context, state *framework.CycleS
|
||||
return 0, framework.AsStatus(fmt.Errorf("getting node %q from Snapshot: %w", nodeName, err))
|
||||
}
|
||||
|
||||
s, err := getBalancedAllocationPreScoreState(state)
|
||||
if err != nil {
|
||||
s = &balancedAllocationPreScoreState{podRequests: ba.calculatePodResourceRequestList(pod, ba.resources)}
|
||||
}
|
||||
|
||||
// ba.score favors nodes with balanced resource usage rate.
|
||||
// It calculates the standard deviation for those resources and prioritizes the node based on how close the usage of those resources is to each other.
|
||||
// Detail: score = (1 - std) * MaxNodeScore, where std is calculated by the root square of Σ((fraction(i)-mean)^2)/len(resources)
|
||||
// The algorithm is partly inspired by:
|
||||
// "Wei Huang et al. An Energy Efficient Virtual Machine Placement Algorithm with Balanced Resource Utilization"
|
||||
return ba.score(pod, nodeInfo)
|
||||
return ba.score(pod, nodeInfo, s.podRequests)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
|
@ -123,6 +123,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
expectedList framework.NodeScoreList
|
||||
name string
|
||||
args config.NodeResourcesBalancedAllocationArgs
|
||||
runPreScore bool
|
||||
}{
|
||||
{
|
||||
// Node1 scores (remaining resources) on 0-MaxNodeScore scale
|
||||
@ -138,6 +139,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}},
|
||||
name: "nothing scheduled, nothing requested",
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -155,6 +157,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
expectedList: []framework.NodeScore{{Name: "node1", Score: 87}, {Name: "node2", Score: framework.MaxNodeScore}},
|
||||
name: "nothing scheduled, resources requested, differently sized nodes",
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -178,6 +181,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
st.MakePod().Node("node2").Labels(labels1).Obj(),
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -199,6 +203,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
st.MakePod().Node("node1").Obj(),
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -222,6 +227,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Spec: cpuAndMemory, ObjectMeta: metav1.ObjectMeta{Labels: labels1}},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -243,6 +249,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Spec: cpuAndMemory},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -264,6 +271,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Spec: cpuAndMemory},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
@ -286,6 +294,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Spec: cpuAndMemory},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
pod: st.MakePod().Obj(),
|
||||
@ -297,6 +306,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Spec: cpuAndMemory},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: true,
|
||||
},
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
// CPU Fraction: 3000 / 3500 = 85.71%
|
||||
@ -327,6 +337,7 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Name: string(v1.ResourceMemory), Weight: 1},
|
||||
{Name: "nvidia.com/gpu", Weight: 1},
|
||||
}},
|
||||
runPreScore: true,
|
||||
},
|
||||
// Only one node (node1) has the scalar resource, pod doesn't request the scalar resource and the scalar resource should be skipped for consideration.
|
||||
// Node1: std = 0, score = 100
|
||||
@ -344,6 +355,29 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
{Name: string(v1.ResourceCPU), Weight: 1},
|
||||
{Name: "nvidia.com/gpu", Weight: 1},
|
||||
}},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
// Node1 scores on 0-MaxNodeScore scale
|
||||
// CPU Fraction: 6000 / 10000 = 60%
|
||||
// Memory Fraction: 5000 / 20000 = 25%
|
||||
// Node1 std: (0.6 - 0.25) / 2 = 0.175
|
||||
// Node1 Score: (1 - 0.175)*MaxNodeScore = 82
|
||||
// Node2 scores on 0-MaxNodeScore scale
|
||||
// CPU Fraction: 6000 / 10000 = 60%
|
||||
// Memory Fraction: 10000 / 20000 = 50%
|
||||
// Node2 std: (0.6 - 0.5) / 2 = 0.05
|
||||
// Node2 Score: (1 - 0.05)*MaxNodeScore = 95
|
||||
pod: &v1.Pod{Spec: cpuAndMemory},
|
||||
nodes: []*v1.Node{makeNode("node1", 10000, 20000, nil), makeNode("node2", 10000, 20000, nil)},
|
||||
expectedList: []framework.NodeScore{{Name: "node1", Score: 82}, {Name: "node2", Score: 95}},
|
||||
name: "resources requested, pods scheduled with resources if PreScore not called",
|
||||
pods: []*v1.Pod{
|
||||
{Spec: cpuOnly},
|
||||
{Spec: cpuAndMemory},
|
||||
},
|
||||
args: config.NodeResourcesBalancedAllocationArgs{Resources: defaultResourceBalancedAllocationSet},
|
||||
runPreScore: false,
|
||||
},
|
||||
}
|
||||
|
||||
@ -354,10 +388,17 @@ func TestNodeResourcesBalancedAllocation(t *testing.T) {
|
||||
defer cancel()
|
||||
fh, _ := runtime.NewFramework(nil, nil, ctx.Done(), runtime.WithSnapshotSharedLister(snapshot))
|
||||
p, _ := NewBalancedAllocation(&test.args, fh, feature.Features{})
|
||||
state := framework.NewCycleState()
|
||||
for i := range test.nodes {
|
||||
hostResult, err := p.(framework.ScorePlugin).Score(ctx, nil, test.pod, test.nodes[i].Name)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
if !test.runPreScore {
|
||||
status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.pod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
}
|
||||
hostResult, status := p.(framework.ScorePlugin).Score(ctx, state, test.pod, test.nodes[i].Name)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectedList[i].Score, hostResult) {
|
||||
t.Errorf("got score %v for host %v, expected %v", hostResult, test.nodes[i].Name, test.expectedList[i].Score)
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
var _ framework.PreFilterPlugin = &Fit{}
|
||||
var _ framework.FilterPlugin = &Fit{}
|
||||
var _ framework.EnqueueExtensions = &Fit{}
|
||||
var _ framework.PreScorePlugin = &Fit{}
|
||||
var _ framework.ScorePlugin = &Fit{}
|
||||
|
||||
const (
|
||||
@ -46,6 +47,9 @@ const (
|
||||
// preFilterStateKey is the key in CycleState to NodeResourcesFit pre-computed data.
|
||||
// Using the name of the plugin will likely help us avoid collisions with other plugins.
|
||||
preFilterStateKey = "PreFilter" + Name
|
||||
|
||||
// preScoreStateKey is the key in CycleState to NodeResourcesFit pre-computed data for Scoring.
|
||||
preScoreStateKey = "PreScore" + Name
|
||||
)
|
||||
|
||||
// nodeResourceStrategyTypeMap maps strategy to scorer implementation
|
||||
@ -100,6 +104,41 @@ func (s *preFilterState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// preScoreState computed at PreScore and used at Score.
|
||||
type preScoreState struct {
|
||||
// podRequests have the same order as the resources defined in NodeResourcesBalancedAllocationArgs.Resources,
|
||||
// same for other place we store a list like that.
|
||||
podRequests []int64
|
||||
}
|
||||
|
||||
// Clone implements the mandatory Clone interface. We don't really copy the data since
|
||||
// there is no need for that.
|
||||
func (s *preScoreState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// PreScore calculates incoming pod's resource requests and writes them to the cycle state used.
|
||||
func (f *Fit) PreScore(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status {
|
||||
state := &preScoreState{
|
||||
podRequests: f.calculatePodResourceRequestList(pod, f.resources),
|
||||
}
|
||||
cycleState.Write(preScoreStateKey, state)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error) {
|
||||
c, err := cycleState.Read(preScoreStateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %q from cycleState: %w", preScoreStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(*preScoreState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid PreScore state, got type %T", c)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (f *Fit) Name() string {
|
||||
return Name
|
||||
@ -335,5 +374,12 @@ func (f *Fit) Score(ctx context.Context, state *framework.CycleState, pod *v1.Po
|
||||
return 0, framework.AsStatus(fmt.Errorf("getting node %q from Snapshot: %w", nodeName, err))
|
||||
}
|
||||
|
||||
return f.score(pod, nodeInfo)
|
||||
s, err := getPreScoreState(state)
|
||||
if err != nil {
|
||||
s = &preScoreState{
|
||||
podRequests: f.calculatePodResourceRequestList(pod, f.resources),
|
||||
}
|
||||
}
|
||||
|
||||
return f.score(pod, nodeInfo, s.podRequests)
|
||||
}
|
||||
|
@ -647,6 +647,7 @@ func TestFitScore(t *testing.T) {
|
||||
existingPods []*v1.Pod
|
||||
expectedPriorities framework.NodeScoreList
|
||||
nodeResourcesFitArgs config.NodeResourcesFitArgs
|
||||
runPreScore bool
|
||||
}{
|
||||
{
|
||||
name: "test case for ScoringStrategy RequestedToCapacityRatio case1",
|
||||
@ -674,6 +675,7 @@ func TestFitScore(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy RequestedToCapacityRatio case2",
|
||||
@ -701,6 +703,7 @@ func TestFitScore(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy MostAllocated",
|
||||
@ -722,6 +725,7 @@ func TestFitScore(t *testing.T) {
|
||||
Resources: defaultResources,
|
||||
},
|
||||
},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy LeastAllocated",
|
||||
@ -743,6 +747,79 @@ func TestFitScore(t *testing.T) {
|
||||
Resources: defaultResources,
|
||||
},
|
||||
},
|
||||
runPreScore: true,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy RequestedToCapacityRatio case1 if PreScore is not called",
|
||||
requestedPod: st.MakePod().
|
||||
Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "4000"}).Obj(),
|
||||
st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).Obj(),
|
||||
},
|
||||
expectedPriorities: []framework.NodeScore{{Name: "node1", Score: 10}, {Name: "node2", Score: 32}},
|
||||
nodeResourcesFitArgs: config.NodeResourcesFitArgs{
|
||||
ScoringStrategy: &config.ScoringStrategy{
|
||||
Type: config.RequestedToCapacityRatio,
|
||||
Resources: defaultResources,
|
||||
RequestedToCapacityRatio: &config.RequestedToCapacityRatioParam{
|
||||
Shape: []config.UtilizationShapePoint{
|
||||
{Utilization: 0, Score: 10},
|
||||
{Utilization: 100, Score: 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
runPreScore: false,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy MostAllocated if PreScore is not called",
|
||||
requestedPod: st.MakePod().
|
||||
Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "4000"}).Obj(),
|
||||
st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).Obj(),
|
||||
},
|
||||
expectedPriorities: []framework.NodeScore{{Name: "node1", Score: 67}, {Name: "node2", Score: 36}},
|
||||
nodeResourcesFitArgs: config.NodeResourcesFitArgs{
|
||||
ScoringStrategy: &config.ScoringStrategy{
|
||||
Type: config.MostAllocated,
|
||||
Resources: defaultResources,
|
||||
},
|
||||
},
|
||||
runPreScore: false,
|
||||
},
|
||||
{
|
||||
name: "test case for ScoringStrategy LeastAllocated if PreScore is not called",
|
||||
requestedPod: st.MakePod().
|
||||
Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
|
||||
Obj(),
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
|
||||
},
|
||||
existingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "4000"}).Obj(),
|
||||
st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).Obj(),
|
||||
},
|
||||
expectedPriorities: []framework.NodeScore{{Name: "node1", Score: 32}, {Name: "node2", Score: 63}},
|
||||
nodeResourcesFitArgs: config.NodeResourcesFitArgs{
|
||||
ScoringStrategy: &config.ScoringStrategy{
|
||||
Type: config.LeastAllocated,
|
||||
Resources: defaultResources,
|
||||
},
|
||||
},
|
||||
runPreScore: false,
|
||||
},
|
||||
}
|
||||
|
||||
@ -762,9 +839,15 @@ func TestFitScore(t *testing.T) {
|
||||
|
||||
var gotPriorities framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
if !test.runPreScore {
|
||||
status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
}
|
||||
score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("unexpected error: %v", status)
|
||||
t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
gotPriorities = append(gotPriorities, framework.NodeScore{Name: n.Name, Score: score})
|
||||
}
|
||||
@ -879,6 +962,7 @@ func BenchmarkTestFitScore(b *testing.B) {
|
||||
|
||||
requestedPod := st.MakePod().Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).Obj()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
_, status := p.Score(context.Background(), state, requestedPod, nodes[0].Name)
|
||||
if !status.IsSuccess() {
|
||||
b.Errorf("unexpected status: %v", status)
|
||||
|
@ -407,11 +407,16 @@ func TestLeastAllocatedScoringStrategy(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
|
||||
var gotScores framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
|
||||
if status.Code() != test.wantStatusCode {
|
||||
t.Errorf("unexpected status code, want: %v, got: %v", test.wantStatusCode, status)
|
||||
t.Errorf("unexpected status code, want: %v, got: %v", test.wantStatusCode, status.Code())
|
||||
}
|
||||
gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
|
||||
}
|
||||
|
@ -364,6 +364,11 @@ func TestMostAllocatedScoringStrategy(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
|
||||
var gotScores framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
|
||||
|
@ -129,9 +129,13 @@ func TestRequestedToCapacityRatioScoringStrategy(t *testing.T) {
|
||||
|
||||
var gotScores framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("unexpected error: %v", status)
|
||||
t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
|
||||
}
|
||||
@ -321,9 +325,13 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
|
||||
|
||||
var gotList framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
status := p.(framework.PreScorePlugin).PreScore(context.Background(), state, test.pod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
score, status := p.(framework.ScorePlugin).Score(context.Background(), state, test.pod, n.Name)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("unexpected error: %v", status)
|
||||
t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
gotList = append(gotList, framework.NodeScore{Name: n.Name, Score: score})
|
||||
}
|
||||
@ -542,11 +550,16 @@ func TestResourceBinPackingMultipleExtended(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
status := p.(framework.PreScorePlugin).PreScore(context.Background(), state, test.pod, test.nodes)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
|
||||
var gotScores framework.NodeScoreList
|
||||
for _, n := range test.nodes {
|
||||
score, status := p.(framework.ScorePlugin).Score(context.Background(), state, test.pod, n.Name)
|
||||
if !status.IsSuccess() {
|
||||
t.Errorf("unexpected error: %v", status)
|
||||
t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
|
||||
}
|
||||
gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
|
||||
}
|
||||
|
@ -45,7 +45,8 @@ type resourceAllocationScorer struct {
|
||||
// score will use `scorer` function to calculate the score.
|
||||
func (r *resourceAllocationScorer) score(
|
||||
pod *v1.Pod,
|
||||
nodeInfo *framework.NodeInfo) (int64, *framework.Status) {
|
||||
nodeInfo *framework.NodeInfo,
|
||||
podRequests []int64) (int64, *framework.Status) {
|
||||
node := nodeInfo.Node()
|
||||
if node == nil {
|
||||
return 0, framework.NewStatus(framework.Error, "node not found")
|
||||
@ -58,7 +59,7 @@ func (r *resourceAllocationScorer) score(
|
||||
requested := make([]int64, len(r.resources))
|
||||
allocatable := make([]int64, len(r.resources))
|
||||
for i := range r.resources {
|
||||
alloc, req := r.calculateResourceAllocatableRequest(nodeInfo, pod, v1.ResourceName(r.resources[i].Name))
|
||||
alloc, req := r.calculateResourceAllocatableRequest(nodeInfo, v1.ResourceName(r.resources[i].Name), podRequests[i])
|
||||
// Only fill the extended resource entry when it's non-zero.
|
||||
if alloc == 0 {
|
||||
continue
|
||||
@ -83,13 +84,12 @@ func (r *resourceAllocationScorer) score(
|
||||
// - 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 (r *resourceAllocationScorer) calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.Pod, resource v1.ResourceName) (int64, int64) {
|
||||
func (r *resourceAllocationScorer) calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, resource v1.ResourceName, podRequest int64) (int64, int64) {
|
||||
requested := nodeInfo.NonZeroRequested
|
||||
if r.useRequested {
|
||||
requested = nodeInfo.Requested
|
||||
}
|
||||
|
||||
podRequest := r.calculatePodResourceRequest(pod, resource)
|
||||
// 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) {
|
||||
@ -133,3 +133,11 @@ func (r *resourceAllocationScorer) calculatePodResourceRequest(pod *v1.Pod, reso
|
||||
}
|
||||
return quantity.Value()
|
||||
}
|
||||
|
||||
func (r *resourceAllocationScorer) calculatePodResourceRequestList(pod *v1.Pod, resources []config.ResourceSpec) []int64 {
|
||||
podRequests := make([]int64, len(resources))
|
||||
for i := range resources {
|
||||
podRequests[i] = r.calculatePodResourceRequest(pod, v1.ResourceName(resources[i].Name))
|
||||
}
|
||||
return podRequests
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user