Kubernetes scheduler学习笔记是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
这样就完成的Pod优先级的设置。如果不设置,Pod默认是同一优先级(为0)。
特别注意:
static Pod比较特殊,需要直接设置priority,因为kubelet是根据priority来判断。
scheduler启动流程
通过深入分析server.Run,可看到如下流程:
server.Run还是有一部分的配置处理流程。
schedulerConfig中,根据默认的参数,加载了两大块内容:predicate、priority函数。
Kubernetes提供了这些默认的判断函数:
predicate:
1、CheckNodeConditionPredicate
we really don’t want to check predicates against unschedulable nodes.
检查Node状态:是否处于可调度状态等。
-----> 遍历nodeInfo中Node的所有状况:
如果Node类型为ready,并且状态不是True,则认为结点为notReady
如果Node类型为OutOfDisk,并且状态不是False,则认为结点OutOfDisk
如果Node类型为NetworkUnavailable,并且状态不是False,则认为结点状态为:NetworkUnavailable
检查Node的spec,如果是UnSchedulable,则认为结点为UnSchedulable。
以上检查都通过,则返回匹配成功。
2、PodFitsHost
we check the pod.spec.nodeName.
检查pod.spec.nodeName是否匹配。
----> 如果Pod未指定NodeName,则返回匹配成功。
检查Node的名字,如果与Pod指定的同名,则匹配成功,否则返回:nodeName不匹配。
3、PodFitsHostPorts
we check ports asked on the spec.
检查服务端口是否被占用。
-----> 如果元数据metadata中有定义需要的podPorts,则直接从元数据中取,否则从Pod的所有容器中获取需要的port。
如果需要的port为空,则返回匹配成功。
从nodeInfo中获取当前已经使用的port,如果有冲突,则返回:端口不匹配,否则返回匹配成功。
4、PodMatchNodeSelector
check node label after narrowing search.
检查label是否匹配。
------> 如果Pod中定义了NodeSelector,则根据选择来匹配Node的labels,如果不匹配,则返回NodeSelectorNotMatch。
如果Pod的Affinity中定义了NodeAffinity,则检查结点亲和关系:
特别的:如果nodeSelectorTerms为nil,则全不匹配;如果nodeSelectorTerms不为nil,但是空的切片,则全不匹配;同样,nodeSelectorTerms中的MatchExpressions,如果为nil或者是空切片,也不匹配。
5、PodFitsResources
this one comes here since it’s not restrictive enough as we do not try to match values but ranges.
-----> 检查Node的allowedPodNumber是否超过,如果超过,增加超限错误(此处未直接返回,会把所有错误检查完一次性返回)。
检查元数据中是否有定义podRequest、ignoredExtendedResources,如果定义了,则从元数据中取。否则从Pod中每个容器中取:先检查所有container中所有需要的资源总合,再检查initContainer中,如果有资源比总合还大,则取较大的为所需要的资源。
如果需要的资源都为0,则返回检查结果。
获取Node的可用资源,检查需要新申请的资源+已申请的资源是否超过可用资源,如果超过,则记录资源不足。
检查所有Pod扩展资源,并判断扩展资源是否需要检查(ignoredExtendedResources),如果需要检查,则判断资源是否足够,不足够则记录失败。
返回检查结果(如果无失败,是检查成功)。
6、NoDiskConflict
Following the resource predicate, we check disk.
----> 遍历Pod所有存储、Node下的所有Pod,检查是否有存储冲突:
如果Pod无存储(无GCE、AWS、RBD、ISCSI),则检查通过。
7、PodToleratesNodeTaints
check toleration here, as node might have toleration.
-----> 检查结点是否容忍taint环境:
参数:Pod中定义的容忍规则:tolerations,Node中的环境状态:taints,筛选规则:取effect为NoSchedule、NoExecute的。
如果Node无taints,返回匹配成功。
遍历所有taints,如果taint不满足筛选规则,则跳过检查。
遍历所有的容忍规则,检查是否有规则是允许结点的taint状态。检查步骤:
如果effect为空,则检查通过,否则要相同。
如果key为空,则检查通过,否则要相同。
如果operator为Exists,则检查通过,如果为空或者是Equal,则要相同,否则不通过。
8、PodToleratesNodeNoExecuteTaints
check toleration here, as node might have toleration.
-----> 检查规则同上相似,只是筛选规则变了:取effect为NoExecute的。
9、CheckNodeLabelPresence
labels are easy to check, so this one goes before.
------> 检查label是否存在,不关心值。可设置label存在与不存在。
只有在scheduler.CreateFromConfig(policy)才会初始化该检查,在RegisterCustomFitPredicate中注册,默认无该检查。
10、checkServiceAffinity
-----> 检查服务类同关系。
如果一个Pod的服务调度到有label:"region=foo"的Node,之后有相同服务的Pod都会调度到该Node。
11、MaxPDVolumeCountPredicate
-----> 检查挂载的卷个数是不是超标,只支持:ESB:39,GCE:16,AzureDisk:16。
12、VolumeNodePredicate
-----> 无
13、VolumeZonePredicate
-----> 检查存储区域划分:
检查Node中是否有label:failure-domain.beta.kubernetes.io/zone或者failure-domain.beta.kubernetes.io/region,如果有,则检查Pod存储情况。
遍历Pod需要的存储信息:
根据PVC名字获取PVC信息,取出PVC对应的PV名字,如果没有名字(表示还未绑定PV),获取PVC的StorageClassName,如果处理正在绑定中,则跳过不检查,否则返回匹配失败(因为PVC绑定失败)。
绑定成功的,根据pvName获取对应的PV信息,检查PV的标签,如果PV有上面两个标签(zone、region),检查PV的值中(值可能有多个,用__分隔),是否包含Node对应标签的值,如果没有包含,则返回匹配失败。
14、CheckNodeMemoryPressurePredicate
doesn’t happen often.
-----> 检查Node内存压力。
15、CheckNodeDiskPressurePredicate
doesn’t happen often.
16、InterPodAffinityMatches
Most expensive predicate to compute.
默认有这些打分函数(priority):
SelectorSpreadPriority:根据相同的RC和服务拆分,使每个Node具有相同服务或RC的Pod尽量少,spreads pods by minimizing the number of pods (belonging to the same service or replication controller) on the same node.
InterPodAffinityPriority:根据Pod共性来分配,pods should be placed in the same topological domain (e.g. same node, same rack, same zone, same power domain, etc.).
LeastRequestedPriority:选择比较闲的node,Prioritize nodes by least requested utilization.
BalancedResourceAllocation:从资源分配平衡性来考虑分配,Prioritizes nodes to help achieve balanced resource usage.
NodePreferAvoidPodsPriority:用于用户自定义分配,权重10000起,方便用户来指定。0的时候不起作用。用户通过这个来指定:scheduler.alpha.kubernetes.io/preferAvoidPods Set this weight large enough to override all other priority functions.
NodeAffinityPriority:根据结点关系来分配,Prioritizes nodes that have labels matching NodeAffinity.
TaintTolerationPriority:根据pod设置的容忍项来分配,Prioritizes nodes that marked with taint which pod can tolerate.
最终,死循环进入:scheduleOne,真正开始schedule的调度流程。
Schedule调度流程
先讲主流程:
主流程分为以下8步:
从Pod队列中取出一个需要调度的Pod。
尝试调度该Pod。
调度失败,则尝试抢占Pod。
调度成功后,尝试做volumes绑定。
由于reserve插件暂时未启用,暂未分析。
尝试将Pod分配到Node上。
真正实现绑定。第4步和第6步中,都只是对schedule的cache的操作,先确保对cache的操作能完成,最终在第7步,异常实现将cache中的修改应用到apiserver中。如果应用失败,会将pod的分配信息从cache中清除,重新进行scheduler。
最复杂也最核心的,就是第2步和第3步,下面分别进行分析。
调度Pod流程
调度Pod,就是尝试将Pod分配到Node上,流程如下:
Pod基本检查,检查Pod是否有了对应的PVC,这里只是检查PVC是否存在,不关心绑定过程。
取出所有Node列表。
将nodeInfo应用到缓存中。全局nodeInfo中保存了当前Node的真实数据信息,而cache中会有调度过程的假设分析的信息。
检查Pod是否可调度到Node上,返回可调度的Node列表。
a) 这里的检查,是针对前面初始化时,注册的predicate函数,如果有不符合,则认为不可调度。
b) 这里会尝试两次,之所以两次,是因为有“提名Pod”的存在。暂时先不管“提名Pod”哪来的,后面会讲到。提名Pod,就是说,这个Pod已经分配到Node上,但它还未应用到Kubernetes环境,目前只是占着这个坑位,要调度的Pod,在调度的过程中,也需要考虑它所占的资源。
c) 第一次时,会先把优先级大于当前Pod的提名Pod分配到Node中(添加到一个临时的nodeInfo中),然后检查所有的predicat函数是否通过。
d) 第二次时,不添加提名Pod,再检查所有的predicate函数。之所以有第二次,是因为提名Pod实际还并不存在,有些Pod亲和性可能会判断有误。
e) 当然,如果没有提名Pod,则不需要第二次判断。
如果找不到,则返回失败。如果只找到一个,则返回该Node。
当找到多个Node时,会去给Node打分,打分规则如下:
a) 如果没有定义打分规则,则返回所有分数都为1。schedule默认是有打分函数的,前面初始化中有讲。
b) 运行早期老版本的打分函数。早期就是单纯的一个function,运行后得到打分结果。
c) 新版本,将打分函数拆分成两步,map和reduce,先按16个并发运行map,之后运行reduce统计执行结果。
d) 这里还预留了扩展支持。
e) 最终返回打分结果。
根据打分结果,选择Node。
a) 先取出得分最高的Node列表。
b) 然后按round-robin的方式选择Node。
由于相同最高分的Node可能有多个,genericScheduler采用round-robin的方式:它自己记录一个全局的lastNodeIndex,如何num为当前有相同最高分的节点数,则用lastNodeIndex % num来选取本次节点的下标,之后lastNodeIndex加1,实现轮询调度。
到此,Pod的调度流程分析完成。当中有个特别的东西:提名Pod(NominatedPod),它的出现和下面讲的抢占流程有关。
Pod抢占流程
抢占的流程,比调度复杂一些,主要分两大步:抢占分析和抢占。第一步是检查是不是能完成抢占,第二步是执行抢占(删除Pod)。
抢占检查
检查Pod是否可以发起抢占:如果Pod是提名Pod(已经预分配到Node),并且该Node上有处于terminating的Pod p,并且p的优先级小于当前Pod,则不允许发起抢占。
获取所有Node清单。
获取可能的Node。检查调度失败原因,如果是nodeNotReady这种原因,则Node不参与抢占。predicates.ErrNodeSelectorNotMatch,predicates.ErrPodAffinityRulesNotMatch,predicates.ErrPodNotMatchHostName,predicates.ErrTaintsTolerationsNotMatch,predicates.ErrNodeLabelPresenceViolated,predicates.ErrNodeNotReady,predicates.ErrNodeNetworkUnavailable,predicates.ErrNodeUnderDiskPressure,predicates.ErrNodeUnderPIDPressure,predicates.ErrNodeUnderMemoryPressure,predicates.ErrNodeUnschedulable,predicates.ErrNodeUnknownCondition,predicates.ErrVolumeZoneConflict,predicates.ErrVolumeNodeConflict,predicates.ErrVolumeBindConflict如果可抢占的Node没有,则结束。
获取pdb列表:pdb is PodDisruptionBudget. 这个是预算的定义,比如statefulset定义了3个复本,而我们定义了,允许其中1个Pod可以挂掉。
获取通过抢占(删除一些Pod),能完成调度的Node列表。
a) 将比当前Pod优先级低的Pod全部从nodeInfoCopy中删除,然后尝试去调度。b) 如果调度失败,则表示无法抢占。(因为不能删除比它优先级高的)c) 将要删除的Pod,根据pdb进行拆分:nonViolatingVictim和violatingVictim。说明见图中。d) 然后尝试将violatingVictim中的Pod一个个加进去,尝试能不能调度。numViolatingVictim中记录不通过数。e) 然后尝试将nonViolatingVictim中的Pod一个个加进去,尝试能不能调度。victims记录不通过的Pod信息。f) 返回victims和numViolatingVictim。
从可抢占的Node列表中,选择最合适的一个Node。按如下规则进行选择:a) node pdb violations最小。就是上面返回的numViolatingVictimc) 比较Node中victims中优先级的最高值,取最小的那个。最高:取的是单个Node中,优先级的最高值。最小:取的是所有Node中的最小值e) 取Node中victims优先级总和最小的。g) 取Node中victims的Pod数最小的。
返回Node信息,需要删除的Pod列表,优先级小的提名Pod。
到此,抢占检查结束。得到期望调度的Node、要调度到这个Node上,需要删除的Pod列表、以及比当前Pod优先级小的提名Pod。
抢占执行流程(找到了期望的Node才会进入)
- 将当前Pod,变更为提名Pod,对应的Node为期望的Node。这里就是提名Pod出现的原因。
- 遍历victims(抢占流程返回的需要删除的Pod列表),删除Pod,并记录event。
- 遍历nominatedPodsToClear(抢占返回的比当前Pod优先级小的提名Pod),清空提名Pod配置,并更新apiServer。
到此,调度流程分析完成。
关于Kubernetes scheduler学习笔记是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注天达云行业资讯频道了解更多相关知识。