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  }