本文是续前面亲和性专题的最后一篇《服务亲和性》的算法分析篇,在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
- 基于预选MetaData的pod信息查询出services
- 基于预选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:
需被调度pod
的NodeSelector配置
B:需被调度pod
定义的服务亲和affinityLabels配置
C: 被选定的亲和目标Node
的Lables
!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