k8s Affinity 亲和性专题源码分析 (三)

本文是续前面亲和性专题的最后一篇《服务亲和性》的算法分析篇,在default调度器代码内并未注册此预选策略,仅有代码实现。连google/baidu上都无法查询到相关使用案例,配置用法不予分析,仅看下面源码详细分析。

四 Service亲和性

代码场景应用注释译文:
一个服务的第一个Pod被调度到带有Label “region=foo”的Nodes(资源集群)上, 那么其服务后面的其它Pod都将调度至Label “region=foo”的Nodes。

4.1 Serice亲和性预选策略checkServiceAffinity

通过NewServiceAffinityPredicate()创建一个ServiceAffinity类对象,并返回两个预选策略所必须的处理Func:

  • affinity.checkServiceAffinity 基于预选元数据Meta,对被调度的pod检测Node是否满足服务亲和性.

  • affinity.serverAffinityMetadataProducer 基于预选Meta的pod信息,获取服务信息和在相同NameSpace下的的Pod列表,供亲和检测时使用。

后面将详述处理func

!FILENAME pkg/scheduler/algorithm/predicates/predicates.go:955

func NewServiceAffinityPredicate(podLister algorithm.PodLister, serviceLister algorithm.ServiceLister, nodeInfo NodeInfo, labels []string) (algorithm.FitPredicate, PredicateMetadataProducer) {
    affinity := &ServiceAffinity{
        podLister:     podLister,
        serviceLister: serviceLister,
        nodeInfo:      nodeInfo,
        labels:        labels,
    }
    return affinity.checkServiceAffinity, affinity.serviceAffinityMetadataProducer
}

affinity.serverAffinityMetadataProducer()
输入:predicateMateData
返回:services 和 pods

  1. 基于预选MetaData的pod信息查询出services
  2. 基于预选MetaData的pod Lables获取所有匹配的pods,且过滤掉仅剩在同一个Namespace的pods。

!FILENAME pkg/scheduler/algorithm/predicates/predicates.go:934

func (s *ServiceAffinity) serviceAffinityMetadataProducer(pm *predicateMetadata) {
    if pm.pod == nil {
        klog.Errorf("Cannot precompute service affinity, a pod is required to calculate service affinity.")
        return
    }
    pm.serviceAffinityInUse = true
    var errSvc, errList error
    // 1.基于预选MetaData的pod信息查询services
    pm.serviceAffinityMatchingPodServices, errSvc = s.serviceLister.GetPodServices(pm.pod)
    // 2.基于预选MetaData的pod Lables获取所有匹配的pods
    selector := CreateSelectorFromLabels(pm.pod.Labels)
    allMatches, errList := s.podLister.List(selector)

    // In the future maybe we will return them as part of the function.
    if errSvc != nil || errList != nil {
        klog.Errorf("Some Error were found while precomputing svc affinity: \nservices:%v , \npods:%v", errSvc, errList)
    }
    // 3.过滤掉仅剩在同一个Namespace的pods
    pm.serviceAffinityMatchingPodList = FilterPodsByNamespace(allMatches, pm.pod.Namespace)
}


affinity.checkServiceAffinity()
基于预处理的MetaData,对被调度的pod检测Node是否满足服务亲和性。

最终的亲和检测Labels:

​ Final affinityLabels =(A ∩ B)+ (B ∩ C) 与 node.Labels 进行Match计算 //∩交集符号

A: 需被调度podNodeSelector配置
B: 需被调度pod定义的服务亲和affinityLabels配置
C: 被选定的亲和目标NodeLables

!FILENAME pkg/scheduler/algorithm/predicates/predicates.go:992

func (s *ServiceAffinity) checkServiceAffinity(pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
    var services []*v1.Service
    var pods []*v1.Pod
    if pm, ok := meta.(*predicateMetadata); ok && (pm.serviceAffinityMatchingPodList != nil || pm.serviceAffinityMatchingPodServices != nil) {
        services = pm.serviceAffinityMatchingPodServices
        pods = pm.serviceAffinityMatchingPodList
    } else {
        // Make the predicate resilient in case metadata is missing.
        pm = &predicateMetadata{pod: pod}
        s.serviceAffinityMetadataProducer(pm)
        pods, services = pm.serviceAffinityMatchingPodList, pm.serviceAffinityMatchingPodServices
    }
    // 筛选掉存在于Node(nodeinfo)上pods,且与之进行podKey比对不相等的pods。          ①
    filteredPods := nodeInfo.FilterOutPods(pods)
    node := nodeInfo.Node()
    if node == nil {
        return false, nil, fmt.Errorf("node not found")
    }
    // affinityLabes交集 ==(A ∩ B) 
  // A:被调度pod的NodeSelector定义  B:定义的亲和性Labels                           ②
    affinityLabels := FindLabelsInSet(s.labels, labels.Set(pod.Spec.NodeSelector))
    // Step 1: If we don't have all constraints, introspect nodes to find the missing constraints.
    if len(s.labels) > len(affinityLabels) {
        if len(services) > 0 {
            if len(filteredPods) > 0 {
                //"被选定的亲和Node"
        //基于第一个filteredPods获取Node信息
                nodeWithAffinityLabels, err := s.nodeInfo.GetNodeInfo(filteredPods[0].Spec.NodeName)
                if err != nil {
                    return false, nil, err
                }
                // 输入:交集Labels、服务亲和Labels、被选出的亲和Node Lables
                // affinityLabels = affinityLabels + 交集(B ∩ C)
                // B: 服务亲和Labels  C:被选出的亲和Node的Lables                           ③
                AddUnsetLabelsToMap(affinityLabels, s.labels, labels.Set(nodeWithAffinityLabels.Labels))
            }
        }
    }

    // 进行一次最终的匹配(affinityLabels 与 被检测亲和的node.Labels )               ④
    if CreateSelectorFromLabels(affinityLabels).Matches(labels.Set(node.Labels)) {
        return true, nil, nil
    }
    return false, []algorithm.PredicateFailureReason{ErrServiceAffinityViolated}, nil
}

FilterOutPods()
筛选掉存在于Node(nodeinfo)上pods,且与之进行podKey比对不相等的pods
filteredPods = 未在Node上的pods + 在node上但podKey相同的pods

!FILENAME pkg/scheduler/cache/node_info.go:656

func (n *NodeInfo) FilterOutPods(pods []*v1.Pod) []*v1.Pod {
    //获取Node的详细信息
    node := n.Node()
    if node == nil {
        return pods
    }
    filtered := make([]*v1.Pod, 0, len(pods))
    for _, p := range pods {
        //如果pod(亲和matched)的NodeName 不等于Spec配置的nodeNmae (即pod不在此Node上),将pod放入filtered.
        if p.Spec.NodeName != node.Name {
            filtered = append(filtered, p)
            continue
        }
        //如果在此Node上,则获取podKey(pod.UID)
        //遍历此Node上所有的目标Pods,获取每个podKey进行与匹配pod的podkey是否相同,
    //相同则将pod放入filtered并返回
        podKey, _ := GetPodKey(p)
        for _, np := range n.Pods() {
            npodkey, _ := GetPodKey(np)
            if npodkey == podKey {
                filtered = append(filtered, p)
                break
            }
        }
    }
    return filtered
}

② *FindLabelsInSet() *
参数一: (B)定义的亲和性Labels配置
参数二: (A)被调度pod的定义NodeSelector配置Selector
检测存在的于NodeSelector的亲和性Labels配置,则取两者的交集部分. (A ∩ B)

!FILENAME pkg/scheduler/algorithm/predicates/utils.go:26

func FindLabelsInSet(labelsToKeep []string, selector labels.Set) map[string]string {
    aL := make(map[string]string)
    for _, l := range labelsToKeep {
        if selector.Has(l) {
            aL[l] = selector.Get(l)
        }
    }
    return aL
}

AddUnsetLabelsToMap()
参数一: (N)在FindLabelsInSet()计算出来的交集Labels
参数二: (B)定义的亲和性Labels配置
参数三: (C)"被选出的亲和Node"上的Lables
检测存在的于"被选出的亲和Node"上的亲和性配置Labels,则取两者的交集部分存放至N. (B ∩ C)=>N

!FILENAME pkg/scheduler/algorithm/predicates/utils.go:37

// 输入:交集Labels、服务亲和Labels、被选出的亲和Node Lables
// 填充:Labels交集 ==(B ∩ C) B: 服务亲和Labels  C:被选出的亲和Node Lables
func AddUnsetLabelsToMap(aL map[string]string, labelsToAdd []string, labelSet labels.Set) {
    for _, l := range labelsToAdd {
        // 如果存在则不作任何操作
        if _, exists := aL[l]; exists {
            continue
        }
        // 反之,计算包含的交集部分 C ∩ B
        if labelSet.Has(l) {
            aL[l] = labelSet.Get(l)
        }
    }
}

CreateSelectorFromLabels().Match() 返回labels.Selector对象

!FILENAME pkg/scheduler/algorithm/predicates/utils.go:62

func CreateSelectorFromLabels(aL map[string]string) labels.Selector {
    if aL == nil || len(aL) == 0 {
        return labels.Everything()
    }
    return labels.Set(aL).AsSelector()
}

END

文章及内容转发请署名XiaoYang

你可能感兴趣的:(k8s Affinity 亲和性专题源码分析 (三))