github.com/kubewharf/katalyst-core@v0.5.3/pkg/scheduler/plugins/qosawarenoderesources/resource_allocation.go (about) 1 /* 2 Copyright 2022 The Katalyst 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 qosawarenoderesources 18 19 import ( 20 v1 "k8s.io/api/core/v1" 21 "k8s.io/klog/v2" 22 kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" 23 "k8s.io/kubernetes/pkg/scheduler/framework" 24 25 "github.com/kubewharf/katalyst-core/pkg/util/native" 26 27 "github.com/kubewharf/katalyst-api/pkg/apis/scheduling/config" 28 "github.com/kubewharf/katalyst-api/pkg/consts" 29 "github.com/kubewharf/katalyst-core/pkg/scheduler/cache" 30 ) 31 32 // resourceToWeightMap contains resource name and weight. 33 type resourceToWeightMap map[v1.ResourceName]int64 34 35 // scorer is decorator for resourceAllocationScorer 36 type scorer func(args *config.QoSAwareNodeResourcesFitArgs) *resourceAllocationScorer 37 38 // resourceAllocationScorer contains information to calculate resource allocation score. 39 type resourceAllocationScorer struct { 40 Name string 41 // used to decide whether to use Requested or NonZeroRequested for 42 // cpu and memory. 43 useRequested bool 44 scorer func(requested, allocable resourceToValueMap) int64 45 resourceToWeightMap resourceToWeightMap 46 } 47 48 // resourceToValueMap is keyed with resource name and valued with quantity. 49 type resourceToValueMap map[v1.ResourceName]int64 50 51 // score will use `scorer` function to calculate the score. 52 func (r *resourceAllocationScorer) score( 53 pod *v1.Pod, extendedNodeInfo *cache.NodeInfo, nodeName string, 54 ) (int64, *framework.Status) { 55 if r.resourceToWeightMap == nil { 56 return 0, framework.NewStatus(framework.Error, "resources not found") 57 } 58 59 requested := make(resourceToValueMap) 60 allocatable := make(resourceToValueMap) 61 for resource := range r.resourceToWeightMap { 62 alloc, req := r.calculateQoSResourceAllocatableRequest(extendedNodeInfo, pod, resource) 63 if alloc != 0 { 64 // Only fill the extended resource entry when it's non-zero. 65 allocatable[resource], requested[resource] = alloc, req 66 } 67 } 68 69 score := r.scorer(requested, allocatable) 70 71 klog.V(10).InfoS("Listing internal info for allocatable resources, requested resources and score", "pod", 72 klog.KObj(pod), "nodeName", nodeName, "resourceAllocationScorer", r.Name, 73 "allocatableResource", allocatable, "requestedResource", requested, "resourceScore", score, 74 ) 75 76 return score, nil 77 } 78 79 // calculateQoSResourceAllocatableRequest returns 2 parameters: 80 // - 1st param: quantity of allocatable resource on the node. 81 // - 2nd param: aggregated quantity of requested resource on the node. 82 func (r *resourceAllocationScorer) calculateQoSResourceAllocatableRequest(extendedNodeInfo *cache.NodeInfo, pod *v1.Pod, resource v1.ResourceName) (int64, int64) { 83 extendedNodeInfo.Mutex.RLock() 84 defer extendedNodeInfo.Mutex.RUnlock() 85 86 requested := extendedNodeInfo.QoSResourcesNonZeroRequested 87 if r.useRequested { 88 requested = extendedNodeInfo.QoSResourcesRequested 89 } 90 91 podQoSRequest := r.calculatePodQoSResourceRequest(pod, resource) 92 switch resource { 93 case consts.ReclaimedResourceMilliCPU: 94 return extendedNodeInfo.QoSResourcesAllocatable.ReclaimedMilliCPU, requested.ReclaimedMilliCPU + podQoSRequest 95 case consts.ReclaimedResourceMemory: 96 return extendedNodeInfo.QoSResourcesAllocatable.ReclaimedMemory, requested.ReclaimedMemory + podQoSRequest 97 } 98 99 klog.V(10).InfoS("Requested resource is omitted for node score calculation", "resourceName", resource) 100 return 0, 0 101 } 102 103 // calculatePodQoSResourceRequest returns the total non-zero requests. If Overhead is defined for the pod and the 104 // PodOverhead feature is enabled, the Overhead is added to the result. 105 // podResourceRequest = max(sum(podSpec.Containers), podSpec.InitContainers) + overHead 106 func (r *resourceAllocationScorer) calculatePodQoSResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 { 107 var podRequest int64 108 for i := range pod.Spec.Containers { 109 container := &pod.Spec.Containers[i] 110 value := native.GetRequestForQoSResource(resource, &container.Resources.Requests, !r.useRequested) 111 podRequest += value 112 } 113 114 for i := range pod.Spec.InitContainers { 115 initContainer := &pod.Spec.InitContainers[i] 116 value := native.GetRequestForQoSResource(resource, &initContainer.Resources.Requests, !r.useRequested) 117 if podRequest < value { 118 podRequest = value 119 } 120 } 121 122 // If Overhead is being utilized, add to the total requests for the pod 123 if pod.Spec.Overhead != nil { 124 if quantity, found := pod.Spec.Overhead[resource]; found { 125 podRequest += quantity.Value() 126 } 127 } 128 129 return podRequest 130 } 131 132 // resourcesToWeightMap make weightmap from resources spec 133 func resourcesToWeightMap(resources []kubeschedulerconfig.ResourceSpec) resourceToWeightMap { 134 resourceToWeightMap := make(resourceToWeightMap) 135 for _, resource := range resources { 136 resourceToWeightMap[v1.ResourceName(resource.Name)] = resource.Weight 137 } 138 return resourceToWeightMap 139 }