mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-19 07:25:19 +00:00
Merge pull request #136652 from brejman/kep-5732-tas-placement-plugin-config
Extend NodeResourcesFit plugin to score placements in the PodGroup scheduling cycle
This commit is contained in:
@@ -159,6 +159,11 @@ var ExpandedPluginsV1 = &config.Plugins{
|
||||
{Name: names.DefaultBinder},
|
||||
},
|
||||
},
|
||||
PlacementScore: config.PluginSet{
|
||||
Enabled: []config.Plugin{
|
||||
{Name: names.NodeResourcesFit, Weight: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// PluginConfigsV1 default plugin configurations.
|
||||
|
||||
@@ -47,6 +47,7 @@ var _ fwk.EnqueueExtensions = &Fit{}
|
||||
var _ fwk.PreScorePlugin = &Fit{}
|
||||
var _ fwk.ScorePlugin = &Fit{}
|
||||
var _ fwk.SignPlugin = &Fit{}
|
||||
var _ fwk.PlacementScorePlugin = &Fit{}
|
||||
|
||||
const (
|
||||
// Name is the name of the plugin used in the plugin registry and configurations.
|
||||
@@ -62,27 +63,27 @@ const (
|
||||
|
||||
// nodeResourceStrategyTypeMap maps strategy to scorer implementation
|
||||
var nodeResourceStrategyTypeMap = map[config.ScoringStrategyType]scorer{
|
||||
config.LeastAllocated: func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer {
|
||||
resources := args.ScoringStrategy.Resources
|
||||
config.LeastAllocated: func(strategy *config.ScoringStrategy) *resourceAllocationScorer {
|
||||
resources := strategy.Resources
|
||||
return &resourceAllocationScorer{
|
||||
Name: string(config.LeastAllocated),
|
||||
scorer: leastResourceScorer(resources),
|
||||
resources: resources,
|
||||
}
|
||||
},
|
||||
config.MostAllocated: func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer {
|
||||
resources := args.ScoringStrategy.Resources
|
||||
config.MostAllocated: func(strategy *config.ScoringStrategy) *resourceAllocationScorer {
|
||||
resources := strategy.Resources
|
||||
return &resourceAllocationScorer{
|
||||
Name: string(config.MostAllocated),
|
||||
scorer: mostResourceScorer(resources),
|
||||
resources: resources,
|
||||
}
|
||||
},
|
||||
config.RequestedToCapacityRatio: func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer {
|
||||
resources := args.ScoringStrategy.Resources
|
||||
config.RequestedToCapacityRatio: func(strategy *config.ScoringStrategy) *resourceAllocationScorer {
|
||||
resources := strategy.Resources
|
||||
return &resourceAllocationScorer{
|
||||
Name: string(config.RequestedToCapacityRatio),
|
||||
scorer: requestedToCapacityRatioScorer(resources, args.ScoringStrategy.RequestedToCapacityRatio.Shape),
|
||||
scorer: requestedToCapacityRatioScorer(resources, strategy.RequestedToCapacityRatio.Shape),
|
||||
resources: resources,
|
||||
}
|
||||
},
|
||||
@@ -100,6 +101,7 @@ type Fit struct {
|
||||
enableInPlacePodLevelResourcesVerticalScaling bool
|
||||
handle fwk.Handle
|
||||
*resourceAllocationScorer
|
||||
placementScorer *resourceAllocationScorer
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
@@ -194,6 +196,17 @@ func (pl *Fit) SignPod(ctx context.Context, pod *v1.Pod) ([]fwk.SignFragment, *f
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getScorer(strategy *config.ScoringStrategy) (*resourceAllocationScorer, error) {
|
||||
if strategy == nil {
|
||||
return nil, fmt.Errorf("scoring strategy not specified")
|
||||
}
|
||||
scorerFactory, exists := nodeResourceStrategyTypeMap[strategy.Type]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("scoring strategy %s is not supported", strategy.Type)
|
||||
}
|
||||
return scorerFactory(strategy), nil
|
||||
}
|
||||
|
||||
// NewFit initializes a new plugin and returns it.
|
||||
func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature.Features) (fwk.Plugin, error) {
|
||||
args, ok := plArgs.(*config.NodeResourcesFitArgs)
|
||||
@@ -204,17 +217,10 @@ func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if args.ScoringStrategy == nil {
|
||||
return nil, fmt.Errorf("scoring strategy not specified")
|
||||
scorer, err := getScorer(args.ScoringStrategy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
strategy := args.ScoringStrategy.Type
|
||||
scorePlugin, exists := nodeResourceStrategyTypeMap[strategy]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("scoring strategy %s is not supported", strategy)
|
||||
}
|
||||
|
||||
scorer := scorePlugin(args)
|
||||
if fts.EnableDRAExtendedResource {
|
||||
scorer.enableDRAExtendedResource = true
|
||||
scorer.draManager = h.SharedDRAManager()
|
||||
@@ -224,7 +230,7 @@ func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature.
|
||||
scorer.DRACaches.celCache = cel.NewCache(10, cel.Features{EnableConsumableCapacity: fts.EnableDRAConsumableCapacity})
|
||||
}
|
||||
|
||||
return &Fit{
|
||||
pl := &Fit{
|
||||
ignoredResources: sets.New(args.IgnoredResources...),
|
||||
ignoredResourceGroups: sets.New(args.IgnoredResourceGroups...),
|
||||
enableInPlacePodVerticalScaling: fts.EnableInPlacePodVerticalScaling,
|
||||
@@ -235,7 +241,20 @@ func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature.
|
||||
enableDRAExtendedResource: fts.EnableDRAExtendedResource,
|
||||
enableInPlacePodLevelResourcesVerticalScaling: fts.EnableInPlacePodLevelResourcesVerticalScaling,
|
||||
resourceAllocationScorer: scorer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if fts.EnableTopologyAwareWorkloadScheduling {
|
||||
placementScorer, err := getScorer(&config.ScoringStrategy{
|
||||
Type: config.MostAllocated,
|
||||
Resources: args.ScoringStrategy.Resources,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pl.placementScorer = placementScorer
|
||||
}
|
||||
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
// ResourceRequestsOptions contains feature gate flags for resource request computation.
|
||||
@@ -760,3 +779,13 @@ func (f *Fit) Score(ctx context.Context, state fwk.CycleState, pod *v1.Pod, node
|
||||
|
||||
return f.score(ctx, pod, nodeInfo, s.podRequests, s.draPreScoreState)
|
||||
}
|
||||
|
||||
// PlacementScoreExtensions is not used by this plugin.
|
||||
func (f *Fit) PlacementScoreExtensions() fwk.PlacementScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScorePlacement scores capacity ratio on all nodes in the placement including all assigned pod group pods' resource requests.
|
||||
func (f *Fit) ScorePlacement(ctx context.Context, state fwk.PodGroupCycleState, podGroup fwk.PodGroupInfo, placement *fwk.PodGroupAssignments) (int64, *fwk.Status) {
|
||||
return f.placementScorer.scorePlacement(ctx, podGroup, placement)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
fwk "k8s.io/kube-scheduler/framework"
|
||||
@@ -2292,3 +2293,266 @@ func testFitSignPod(tCtx ktesting.TContext) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type podAssignment struct {
|
||||
pod *v1.Pod
|
||||
nodeName string
|
||||
}
|
||||
|
||||
func (pa *podAssignment) GetPod() *v1.Pod {
|
||||
return pa.pod
|
||||
}
|
||||
|
||||
func (pa *podAssignment) GetNodeName() string {
|
||||
return pa.nodeName
|
||||
}
|
||||
|
||||
func TestScorePlacement_Resources(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
|
||||
features.GenericWorkload: true,
|
||||
features.TopologyAwareWorkloadScheduling: true,
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
nodes []*v1.Node
|
||||
placementNodeNames []string
|
||||
podGroupPods []*v1.Pod
|
||||
podGroupAssignments map[types.UID]string
|
||||
preExistingPods []*v1.Pod
|
||||
resourceSpec []config.ResourceSpec
|
||||
expectedScore int64
|
||||
}{
|
||||
{
|
||||
name: "Computes score based on total requests",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemory("2000m", "2000")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 2000m) / (5000m + 5000m) = 0.3
|
||||
// Memory: (0 + 2000) / (5000 + 5000) = 0.2
|
||||
// Score: MaxScore * (0.3 + 0.2) / 2 = 25
|
||||
expectedScore: 25,
|
||||
},
|
||||
{
|
||||
name: "Computes score using weights",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemory("2000m", "2000")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 4},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 2000m) / (5000m + 5000m) = 0.3
|
||||
// Memory: (0 + 2000) / (5000 + 5000) = 0.2
|
||||
// Score: MaxScore * (4 * 0.3 + 0.2) / 5 = 28
|
||||
expectedScore: 28,
|
||||
},
|
||||
{
|
||||
name: "Handles multiple pods per node",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemory("2000m", "2000")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node1",
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 4},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 2000m) / (5000m + 5000m) = 0.3
|
||||
// Memory: (0 + 2000) / (5000 + 5000) = 0.2
|
||||
// Score: MaxScore * (4 * 0.3 + 0.2) / 5 = 28
|
||||
expectedScore: 28,
|
||||
},
|
||||
{
|
||||
name: "Does not include nodes from outside of placement",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node3").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemory("2000m", "2000")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
expectedScore: 25,
|
||||
},
|
||||
{
|
||||
name: "Includes pre-existing pods from outside of pod group",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemory("5000m", "5000")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemory("2000m", "2000")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
preExistingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").UID("baz").Req(cpuAndMemory("1000m", "0")).Obj(),
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 2000m + 1000m) / (5000m + 5000m) = 0.4
|
||||
// Memory: (0 + 2000 + 0) / (5000 + 5000) = 0.2
|
||||
// Score: MaxScore * (0.3 + 0.2) / 2 = 30
|
||||
expectedScore: 30,
|
||||
},
|
||||
{
|
||||
name: "Includes scalar resources if any pod in pod group has nonzero requests",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemoryAndGpu("5000m", "5000", "5")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemoryAndGpu("5000m", "5000", "5")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemoryAndGpu("1000m", "0", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemoryAndGpu("1000m", "2000", "1")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
preExistingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").UID("baz").Req(cpuAndMemoryAndGpu("1000m", "0", "0")).Obj(),
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
{Name: "nvidia.com/gpu", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 1000m + 1000m) / (5000m + 5000m) = 0.3
|
||||
// Memory: (0 + 2000 + 0) / (5000 + 5000) = 0.2
|
||||
// GPU: (0 + 1 + 0) / (5 + 5) = 0.1
|
||||
// Score: MaxScore * (0.3 + 0.2 + 0.1) / 3 = 20
|
||||
expectedScore: 20,
|
||||
},
|
||||
{
|
||||
name: "Does not include scalar resources if all pods in pod group have zero requests",
|
||||
nodes: []*v1.Node{
|
||||
st.MakeNode().Name("node1").Capacity(cpuAndMemoryAndGpu("5000m", "5000", "5")).Obj(),
|
||||
st.MakeNode().Name("node2").Capacity(cpuAndMemoryAndGpu("5000m", "5000", "5")).Obj(),
|
||||
},
|
||||
placementNodeNames: []string{"node1", "node2"},
|
||||
podGroupPods: []*v1.Pod{
|
||||
st.MakePod().UID("foo").Req(cpuAndMemoryAndGpu("1000m", "0", "0")).Obj(),
|
||||
st.MakePod().UID("bar").Req(cpuAndMemoryAndGpu("1000m", "2000", "0")).Obj(),
|
||||
},
|
||||
podGroupAssignments: map[types.UID]string{
|
||||
"foo": "node1",
|
||||
"bar": "node2",
|
||||
},
|
||||
preExistingPods: []*v1.Pod{
|
||||
st.MakePod().Node("node1").UID("baz").Req(cpuAndMemoryAndGpu("1000m", "0", "1")).Obj(),
|
||||
},
|
||||
resourceSpec: []config.ResourceSpec{
|
||||
{Name: "cpu", Weight: 1},
|
||||
{Name: "memory", Weight: 1},
|
||||
{Name: "nvidia.com/gpu", Weight: 1},
|
||||
},
|
||||
// CPU: (1000m + 1000m + 1000m) / (5000m + 5000m) = 0.3
|
||||
// Memory: (0 + 2000 + 0) / (5000 + 5000) = 0.2
|
||||
// GPU: not included as it isn't requested by the pods
|
||||
// Score: MaxScore * (0.3 + 0.2) / 2 = 25
|
||||
expectedScore: 25,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tCtx := ktesting.Init(t)
|
||||
snapshot := cache.NewSnapshot(tc.preExistingPods, tc.nodes)
|
||||
fh, _ := runtime.NewFramework(tCtx, nil, nil, runtime.WithSnapshotSharedLister(snapshot))
|
||||
scoringStrategy := defaultScoringStrategy.DeepCopy()
|
||||
scoringStrategy.Resources = tc.resourceSpec
|
||||
plugin, err := NewFit(tCtx, &config.NodeResourcesFitArgs{
|
||||
ScoringStrategy: scoringStrategy,
|
||||
}, fh, plfeature.Features{EnableTopologyAwareWorkloadScheduling: true})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
placementNodes := make([]fwk.NodeInfo, 0, len(tc.placementNodeNames))
|
||||
for _, name := range tc.placementNodeNames {
|
||||
nodeInfo, err := snapshot.NodeInfos().Get(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
placementNodes = append(placementNodes, nodeInfo)
|
||||
}
|
||||
proposedAssignments := make([]fwk.ProposedAssignment, 0, len(tc.podGroupPods))
|
||||
for _, pod := range tc.podGroupPods {
|
||||
if nodeName, ok := tc.podGroupAssignments[pod.UID]; ok {
|
||||
proposedAssignments = append(proposedAssignments, &podAssignment{
|
||||
pod: pod,
|
||||
nodeName: nodeName,
|
||||
})
|
||||
}
|
||||
}
|
||||
podGroupInfo := &framework.PodGroupInfo{
|
||||
UnscheduledPods: tc.podGroupPods,
|
||||
}
|
||||
podGroupAssignments := &fwk.PodGroupAssignments{
|
||||
Placement: &fwk.Placement{
|
||||
Nodes: placementNodes,
|
||||
},
|
||||
ProposedAssignments: proposedAssignments,
|
||||
}
|
||||
|
||||
score, status := plugin.(*Fit).ScorePlacement(tCtx, framework.NewCycleState(), podGroupInfo, podGroupAssignments)
|
||||
|
||||
if !status.IsSuccess() {
|
||||
t.Fatalf("ScorePlacement failed: %v", status.AsError())
|
||||
}
|
||||
if score != tc.expectedScore {
|
||||
t.Fatalf("Unexpected score, want %v got %v", tc.expectedScore, score)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
)
|
||||
|
||||
// scorer is decorator for resourceAllocationScorer
|
||||
type scorer func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer
|
||||
type scorer func(strategy *config.ScoringStrategy) *resourceAllocationScorer
|
||||
|
||||
// DRACaches holds various caches used for DRA-related computations
|
||||
type DRACaches struct {
|
||||
@@ -150,9 +150,29 @@ func (r *resourceAllocationScorer) score(
|
||||
return 0, fwk.NewStatus(fwk.Error, "resources not found")
|
||||
}
|
||||
|
||||
allocated := make([]int64, len(r.resources))
|
||||
requested := make([]int64, len(r.resources))
|
||||
allocatable := make([]int64, len(r.resources))
|
||||
requested, allocated, allocatable := r.calculateNodeAllocatableRequest(ctx, nodeInfo, podRequests, draPreScoreState)
|
||||
|
||||
score := r.scorer(requested, allocated, allocatable)
|
||||
|
||||
if loggerV := logger.V(10); loggerV.Enabled() { // Serializing these maps is costly.
|
||||
loggerV.Info("Listed internal info for allocatable resources, requested resources and score", "pod",
|
||||
klog.KObj(pod), "node", klog.KObj(node), "resourceAllocationScorer", r.Name,
|
||||
"allocatableResource", allocatable, "requestedResource", requested, "resourceScore", score,
|
||||
)
|
||||
}
|
||||
|
||||
return score, nil
|
||||
}
|
||||
|
||||
func (r *resourceAllocationScorer) calculateNodeAllocatableRequest(
|
||||
ctx context.Context,
|
||||
nodeInfo fwk.NodeInfo,
|
||||
podRequests []int64,
|
||||
draPreScoreState *draPreScoreState,
|
||||
) (requested []int64, allocated []int64, allocatable []int64) {
|
||||
requested = make([]int64, len(r.resources))
|
||||
allocated = make([]int64, len(r.resources))
|
||||
allocatable = make([]int64, len(r.resources))
|
||||
for i := range r.resources {
|
||||
resource := v1.ResourceName(r.resources[i].Name)
|
||||
// If it's an extended resource, and the pod doesn't request it.
|
||||
@@ -169,17 +189,7 @@ func (r *resourceAllocationScorer) score(
|
||||
allocated[i] = nodeAllocated
|
||||
requested[i] = allocated[i] + podRequests[i]
|
||||
}
|
||||
|
||||
score := r.scorer(requested, allocated, allocatable)
|
||||
|
||||
if loggerV := logger.V(10); loggerV.Enabled() { // Serializing these maps is costly.
|
||||
loggerV.Info("Listed internal info for allocatable resources, requested resources and score", "pod",
|
||||
klog.KObj(pod), "node", klog.KObj(node), "resourceAllocationScorer", r.Name,
|
||||
"allocatableResource", allocatable, "requestedResource", requested, "resourceScore", score,
|
||||
)
|
||||
}
|
||||
|
||||
return score, nil
|
||||
return requested, allocated, allocatable
|
||||
}
|
||||
|
||||
// calculateResourceAllocatableRequest returns 2 parameters:
|
||||
@@ -190,7 +200,7 @@ func (r *resourceAllocationScorer) calculateResourceAllocatableRequest(
|
||||
nodeInfo fwk.NodeInfo,
|
||||
resource v1.ResourceName,
|
||||
draPreScoreState *draPreScoreState,
|
||||
) (int64, int64) {
|
||||
) (allocatable int64, allocated int64) {
|
||||
requested := nodeInfo.GetNonZeroRequested()
|
||||
if r.useRequested {
|
||||
requested = nodeInfo.GetRequested()
|
||||
@@ -491,3 +501,44 @@ func (r *resourceAllocationScorer) deviceMatchesClass(ctx context.Context, devic
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *resourceAllocationScorer) scorePlacement(
|
||||
ctx context.Context,
|
||||
podGroupInfo fwk.PodGroupInfo,
|
||||
podGroupAssignments *fwk.PodGroupAssignments,
|
||||
) (int64, *fwk.Status) {
|
||||
requested := make([]int64, len(r.resources))
|
||||
// Calculate requests for the pod group pods scheduled for this placement
|
||||
for _, assignment := range podGroupAssignments.ProposedAssignments {
|
||||
pod := assignment.GetPod()
|
||||
podRequests := r.calculatePodResourceRequestList(pod, r.resources)
|
||||
for i := range len(r.resources) {
|
||||
requested[i] += podRequests[i]
|
||||
}
|
||||
}
|
||||
allocatable := make([]int64, len(r.resources))
|
||||
allocated := make([]int64, len(r.resources))
|
||||
// Calculate resources on all nodes in this placement
|
||||
for _, node := range podGroupAssignments.Nodes {
|
||||
_, nodeAllocated, nodeAllocatable := r.calculateNodeAllocatableRequest(ctx, node, requested, nil)
|
||||
for i := range r.resources {
|
||||
allocatable[i] += nodeAllocatable[i]
|
||||
allocated[i] += nodeAllocated[i]
|
||||
// requested includes both the already existing pods
|
||||
// and the pod group pods that are being scheduled for this placement
|
||||
requested[i] += nodeAllocated[i]
|
||||
}
|
||||
}
|
||||
|
||||
score := r.scorer(requested, allocated, allocatable)
|
||||
|
||||
logger := klog.FromContext(ctx)
|
||||
if loggerV := logger.V(10); loggerV.Enabled() { // Serializing these maps is costly.
|
||||
loggerV.Info("Listed internal info for placement's allocatable resources, requested resources and placement score", "podGroup",
|
||||
klog.KRef(podGroupInfo.GetNamespace(), podGroupInfo.GetName()), "placement", podGroupAssignments.Name, "resourceAllocationScorer", r.Name,
|
||||
"allocatableResource", allocatable, "requestedResource", requested, "resourceScore", score,
|
||||
)
|
||||
}
|
||||
|
||||
return score, nil
|
||||
}
|
||||
|
||||
@@ -680,6 +680,10 @@ func (pgi *PodGroupInfo) GetNamespace() string {
|
||||
return pgi.Namespace
|
||||
}
|
||||
|
||||
func (pgi *PodGroupInfo) GetUnscheduledPods() []*v1.Pod {
|
||||
return pgi.UnscheduledPods
|
||||
}
|
||||
|
||||
// PodInfo is a wrapper to a Pod with additional pre-computed information to
|
||||
// accelerate processing. This information is typically immutable (e.g., pre-processed
|
||||
// inter-pod affinity selectors).
|
||||
|
||||
Reference in New Issue
Block a user