k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package noderesources 18 19 import ( 20 "context" 21 22 v1 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/api/resource" 24 utilfeature "k8s.io/apiserver/pkg/util/feature" 25 "k8s.io/klog/v2" 26 27 resourcehelper "k8s.io/kubernetes/pkg/api/v1/resource" 28 "k8s.io/kubernetes/pkg/features" 29 "k8s.io/kubernetes/pkg/scheduler/apis/config" 30 "k8s.io/kubernetes/pkg/scheduler/framework" 31 schedutil "k8s.io/kubernetes/pkg/scheduler/util" 32 ) 33 34 // scorer is decorator for resourceAllocationScorer 35 type scorer func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer 36 37 // resourceAllocationScorer contains information to calculate resource allocation score. 38 type resourceAllocationScorer struct { 39 Name string 40 // used to decide whether to use Requested or NonZeroRequested for 41 // cpu and memory. 42 useRequested bool 43 scorer func(requested, allocable []int64) int64 44 resources []config.ResourceSpec 45 } 46 47 // score will use `scorer` function to calculate the score. 48 func (r *resourceAllocationScorer) score( 49 ctx context.Context, 50 pod *v1.Pod, 51 nodeInfo *framework.NodeInfo, 52 podRequests []int64) (int64, *framework.Status) { 53 logger := klog.FromContext(ctx) 54 node := nodeInfo.Node() 55 56 // resources not set, nothing scheduled, 57 if len(r.resources) == 0 { 58 return 0, framework.NewStatus(framework.Error, "resources not found") 59 } 60 61 requested := make([]int64, len(r.resources)) 62 allocatable := make([]int64, len(r.resources)) 63 for i := range r.resources { 64 alloc, req := r.calculateResourceAllocatableRequest(logger, nodeInfo, v1.ResourceName(r.resources[i].Name), podRequests[i]) 65 // Only fill the extended resource entry when it's non-zero. 66 if alloc == 0 { 67 continue 68 } 69 allocatable[i] = alloc 70 requested[i] = req 71 } 72 73 score := r.scorer(requested, allocatable) 74 75 if loggerV := logger.V(10); loggerV.Enabled() { // Serializing these maps is costly. 76 loggerV.Info("Listed internal info for allocatable resources, requested resources and score", "pod", 77 klog.KObj(pod), "node", klog.KObj(node), "resourceAllocationScorer", r.Name, 78 "allocatableResource", allocatable, "requestedResource", requested, "resourceScore", score, 79 ) 80 } 81 82 return score, nil 83 } 84 85 // calculateResourceAllocatableRequest returns 2 parameters: 86 // - 1st param: quantity of allocatable resource on the node. 87 // - 2nd param: aggregated quantity of requested resource on the node. 88 // Note: if it's an extended resource, and the pod doesn't request it, (0, 0) is returned. 89 func (r *resourceAllocationScorer) calculateResourceAllocatableRequest(logger klog.Logger, nodeInfo *framework.NodeInfo, resource v1.ResourceName, podRequest int64) (int64, int64) { 90 requested := nodeInfo.NonZeroRequested 91 if r.useRequested { 92 requested = nodeInfo.Requested 93 } 94 95 // If it's an extended resource, and the pod doesn't request it. We return (0, 0) 96 // as an implication to bypass scoring on this resource. 97 if podRequest == 0 && schedutil.IsScalarResourceName(resource) { 98 return 0, 0 99 } 100 switch resource { 101 case v1.ResourceCPU: 102 return nodeInfo.Allocatable.MilliCPU, (requested.MilliCPU + podRequest) 103 case v1.ResourceMemory: 104 return nodeInfo.Allocatable.Memory, (requested.Memory + podRequest) 105 case v1.ResourceEphemeralStorage: 106 return nodeInfo.Allocatable.EphemeralStorage, (nodeInfo.Requested.EphemeralStorage + podRequest) 107 default: 108 if _, exists := nodeInfo.Allocatable.ScalarResources[resource]; exists { 109 return nodeInfo.Allocatable.ScalarResources[resource], (nodeInfo.Requested.ScalarResources[resource] + podRequest) 110 } 111 } 112 logger.V(10).Info("Requested resource is omitted for node score calculation", "resourceName", resource) 113 return 0, 0 114 } 115 116 // calculatePodResourceRequest returns the total non-zero requests. If Overhead is defined for the pod 117 // the Overhead is added to the result. 118 func (r *resourceAllocationScorer) calculatePodResourceRequest(pod *v1.Pod, resourceName v1.ResourceName) int64 { 119 120 opts := resourcehelper.PodResourcesOptions{ 121 InPlacePodVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), 122 } 123 if !r.useRequested { 124 opts.NonMissingContainerRequests = v1.ResourceList{ 125 v1.ResourceCPU: *resource.NewMilliQuantity(schedutil.DefaultMilliCPURequest, resource.DecimalSI), 126 v1.ResourceMemory: *resource.NewQuantity(schedutil.DefaultMemoryRequest, resource.DecimalSI), 127 } 128 } 129 130 requests := resourcehelper.PodRequests(pod, opts) 131 132 quantity := requests[resourceName] 133 if resourceName == v1.ResourceCPU { 134 return quantity.MilliValue() 135 } 136 return quantity.Value() 137 } 138 139 func (r *resourceAllocationScorer) calculatePodResourceRequestList(pod *v1.Pod, resources []config.ResourceSpec) []int64 { 140 podRequests := make([]int64, len(resources)) 141 for i := range resources { 142 podRequests[i] = r.calculatePodResourceRequest(pod, v1.ResourceName(resources[i].Name)) 143 } 144 return podRequests 145 }