diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go index a87828020e7..a5f0f4f516b 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go @@ -200,6 +200,7 @@ func (pl *PodTopologySpread) Score(ctx context.Context, cycleState *framework.Cy pair := topologyPair{key: c.TopologyKey, value: tpVal} cnt = *s.TopologyPairToPodCounts[pair] } + cnt = adjustForMaxSkew(cnt, int64(c.MaxSkew)) score += float64(cnt) * s.TopologyNormalizingWeight[i] } } @@ -285,3 +286,14 @@ func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error) func topologyNormalizingWeight(size int) float64 { return math.Log(float64(size + 2)) } + +// adjustForMaxSkew adjusts the number of matching pods in a topology domain +// using the constraint's maxSkew. +// Topology domains with less than maxSkew number of pods are considered to have +// the same priority. +func adjustForMaxSkew(cnt, maxSkew int64) int64 { + if cnt < maxSkew { + return maxSkew - 1 + } + return cnt +} diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go index e5a44642819..75b03f70ebd 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go @@ -314,6 +314,68 @@ func TestPodTopologySpreadScore(t *testing.T) { {Name: "node-d", Score: 0}, }, }, + { + // matching pods spread as 2/1/0/3. + // With maxSkew=2, counts change internally to 2/1/1/3. + name: "one constraint on node, all 4 nodes are candidates, maxSkew=2", + pod: st.MakePod().Name("p").Label("foo", ""). + SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), + st.MakePod().Name("p-d2").Node("node-d").Label("foo", "").Obj(), + st.MakePod().Name("p-d3").Node("node-d").Label("foo", "").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), + }, + failedNodes: []*v1.Node{}, + want: []framework.NodeScore{ + {Name: "node-a", Score: 60}, // +20, compared to maxSkew=1 + {Name: "node-b", Score: 100}, // +20, compared to maxSkew=1 + {Name: "node-c", Score: 100}, + {Name: "node-d", Score: 20}, // +20, compared to maxSkew=1 + }, + }, + { + // matching pods spread as 4/3/2/1. + // With maxSkew=3, counts change internally to 4/3/2/2. + name: "one constraint on node, all 4 nodes are candidates, maxSkew=3", + pod: st.MakePod().Name("p").Label("foo", ""). + SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-b3").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), + st.MakePod().Name("p-c2").Node("node-c").Label("foo", "").Obj(), + st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), + }, + failedNodes: []*v1.Node{}, + want: []framework.NodeScore{ + {Name: "node-a", Score: 42}, // +28 compared to maxSkew=1 + {Name: "node-b", Score: 71}, // +29 compared to maxSkew=1 + {Name: "node-c", Score: 100}, // +29 compared to maxSkew=1 + {Name: "node-d", Score: 100}, + }, + }, { // matching pods spread as 4/2/1/~3~ (node4 is not a candidate) name: "one constraint on node, 3 out of 4 nodes are candidates",