github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/evictionmanager/rule/rule.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 rule
    18  
    19  import (
    20  	"k8s.io/klog/v2"
    21  	kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
    22  
    23  	pkgconfig "github.com/kubewharf/katalyst-core/pkg/config"
    24  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    25  	"github.com/kubewharf/katalyst-core/pkg/util/general"
    26  )
    27  
    28  var evictionScopePriority = map[string]int{
    29  	EvictionScopeForce:  1,
    30  	EvictionScopeMemory: 2,
    31  	EvictionScopeCPU:    3,
    32  }
    33  
    34  type EvictionStrategy interface {
    35  	// CandidateSort defines the eviction priority among different EvictPods
    36  	CandidateSort(rpList RuledEvictPodList)
    37  
    38  	// CandidateValidate defines whether the given pod is permitted for
    39  	// eviction since some specific EvictPods should always keep running.
    40  	CandidateValidate(rp *RuledEvictPod) bool
    41  }
    42  
    43  type EvictionStrategyImpl struct {
    44  	conf     *generic.GenericConfiguration
    45  	compares []general.CmpFunc
    46  }
    47  
    48  func NewEvictionStrategyImpl(conf *pkgconfig.Configuration) EvictionStrategy {
    49  	e := &EvictionStrategyImpl{
    50  		conf: conf.GenericConfiguration,
    51  	}
    52  
    53  	// if any compare function reach out with a result, returns immediately
    54  	e.compares = []general.CmpFunc{
    55  		e.CompareKatalystQoS,
    56  		e.ComparePriority,
    57  		e.CompareEvictionResource,
    58  		e.ComparePodName,
    59  	}
    60  	return e
    61  }
    62  
    63  // CandidateSort defines the sorting rules will be as below
    64  // - katalyst QoS: none-reclaimed > reclaimed
    65  // - pod priority
    66  // - predefined resource priority: e.g. memory > cpu > ...
    67  // - pod names
    68  func (e *EvictionStrategyImpl) CandidateSort(rpList RuledEvictPodList) {
    69  	general.NewMultiSorter(e.compares...).Sort(rpList)
    70  }
    71  
    72  // CandidateValidate will try to filter out EvictPods from eviction
    73  // - EvictPods with katalyst SystemQoS
    74  // - EvictPods marked as critical
    75  func (e *EvictionStrategyImpl) CandidateValidate(rp *RuledEvictPod) bool {
    76  	pod := rp.EvictPod.Pod
    77  	if pod == nil {
    78  		return false
    79  	}
    80  
    81  	if ok, err := e.conf.CheckSystemQoSForPod(pod); err != nil {
    82  		klog.Errorf("failed to get qos for pod %v, err: %v", pod.Name, err)
    83  		return true
    84  	} else if ok {
    85  		return false
    86  	}
    87  
    88  	if kubelettypes.IsCriticalPod(pod) {
    89  		return false
    90  	}
    91  	return true
    92  }
    93  
    94  // CompareKatalystQoS compares KatalystQoS for EvictPods, if we failed to
    95  // parse qos level for a pod, we will consider it as none-reclaimed pod.
    96  func (e *EvictionStrategyImpl) CompareKatalystQoS(s1, s2 interface{}) int {
    97  	return covertBoolSortCompareToIntSort(func(s1, s2 interface{}) bool {
    98  		c1, c2 := s1.(*RuledEvictPod), s2.(*RuledEvictPod)
    99  
   100  		p1Reclaimed, err := e.conf.CheckReclaimedQoSForPod(c1.Pod)
   101  		if err != nil {
   102  			klog.Errorf("failed to get qos for pod %v, err: %v", c1.Pod.Name, err)
   103  			p1Reclaimed = false
   104  		}
   105  
   106  		p2Reclaimed, err := e.conf.CheckReclaimedQoSForPod(c2.Pod)
   107  		if err != nil {
   108  			klog.Errorf("failed to get qos for pod %v, err: %v", c2.Pod.Name, err)
   109  			p2Reclaimed = false
   110  		}
   111  
   112  		return !p1Reclaimed && p2Reclaimed
   113  	}, s1, s2)
   114  }
   115  
   116  // ComparePriority compares pod priority for EvictPods, if any pod doesn't have
   117  // nominated priority, it will always be inferior to those with priority nominated.
   118  func (e *EvictionStrategyImpl) ComparePriority(s1, s2 interface{}) int {
   119  	return covertBoolSortCompareToIntSort(func(s1, s2 interface{}) bool {
   120  		c1, c2 := s1.(*RuledEvictPod), s2.(*RuledEvictPod)
   121  
   122  		if c1.Pod.Spec.Priority == nil {
   123  			return false
   124  		}
   125  		return c2.Pod.Spec.Priority == nil || *c1.Pod.Spec.Priority > *c2.Pod.Spec.Priority
   126  	}, s1, s2)
   127  }
   128  
   129  // CompareEvictionResource compares the eviction scope defined in each plugin
   130  func (e *EvictionStrategyImpl) CompareEvictionResource(s1, s2 interface{}) int {
   131  	return covertBoolSortCompareToIntSort(func(s1, s2 interface{}) bool {
   132  		c1, c2 := s1.(*RuledEvictPod), s2.(*RuledEvictPod)
   133  
   134  		if _, ok := evictionScopePriority[c1.Scope]; !ok {
   135  			return false
   136  		}
   137  		if _, ok := evictionScopePriority[c2.Scope]; !ok {
   138  			return true
   139  		}
   140  		return evictionScopePriority[c2.Scope] > evictionScopePriority[c1.Scope]
   141  	}, s1, s2)
   142  }
   143  
   144  func (e *EvictionStrategyImpl) ComparePodName(s1, s2 interface{}) int {
   145  	return covertBoolSortCompareToIntSort(func(s1, s2 interface{}) bool {
   146  		c1, c2 := s1.(*RuledEvictPod), s2.(*RuledEvictPod)
   147  
   148  		return c1.Pod.Name > c2.Pod.Name
   149  	}, s1, s2)
   150  }
   151  
   152  // covertBoolSortCompareToIntSort converts the bool-based sorting
   153  // function to int-based sorting function
   154  func covertBoolSortCompareToIntSort(f func(s1, s2 interface{}) bool, s1, s2 interface{}) int {
   155  	if f(s1, s2) {
   156  		return -1
   157  	} else if f(s2, s1) {
   158  		return 1
   159  	}
   160  	return 0
   161  }