Kubernetes Scheduler模块代码学习

来源:互联网 发布:c语言怎么四舍五入 编辑:程序博客网 时间:2024/06/15 23:57

Scheduler模块在Kubernetes中算是相对比较简单易懂的模块,但是其工作却是比较重要的,它主要负责将那些还没有找到node运行的Pod挑选最合适的Node。它的工作是负责为Pod找到合适的Node然后向APIServer提交binder表示该Pod已经属于该Node了,后续的工作则有Kubelet模块来负责。Scheduler模块会不断向APIServer拿那些还没有找到Node的Pod列表,以及当前集群中那些可用的Node列表。有了这两份列表Scheduler模块为Pod找对象(Node)的过程可以分为如下2个步骤:


3.1 Predicate策略

   根据Pod的当前情况从Node列表中选出能用来运行该Pod的Node列表,该过程称为Predicates过程,Predicates的策略主要包括:

3.1.1 NoDiskConflict

   检查在此主机上是否存在卷冲突。如果这个主机已经挂载了卷,其它同样使用这个卷的Pod不能调度到这个主机上, 遍历待检测的node的上已经运行的pod的Volume根据如下3个策略检测。

 a) GCE允许同时挂载多个卷,只要这些卷都是只读的;

 b) Amazon EBS不允许不同的Pod挂载同一个卷;

 c) Ceph RBD不允许任何两个非只读的pods分享相同的monitor,match pool和 image;


3.1.2 NoVolumeZoneConflict

   检查给定的zone限制前提下,检查如果在此主机上部署Pod是否存在卷冲突。假定一些volumes可能有zone调度约束, VolumeZonePredicate根据volumes自身需求来评估pod是否满足条件。必要条件就是任何volumes的zone-labels 必须与节点上的zone-labels完全匹配。节点上可以有多个zone-labels的约束(比如一个假设的复制卷可能会允许进行区域范围内的访 问)。目前,这个只对PersistentVolumeClaims支持,而且只在PersistentVolume的范围内查找标签。处理在Pod的属 性中定义的volumes(即不使用PersistentVolume)有可能会变得更加困难,因为要在调度的过程中确定volume的zone,这很有 可能会需要调用云提供商。具体过程是遍历Pod的上所有声明的PVC所引用的PV定义的zone相关的label是否存在于该node所定义的label上,如果有一个不存在就不适合。


3.1.3 MaxEBSVolumeCount

   确保已挂载的EBS存储卷不超过设置的最大值。默认值是39。它会检查直接使用的存储卷,和间接使用这种类型存储的PVC。计算不同卷的总目,如果新的Pod部署上去后卷的数目会超过设置的最大值,那么Pod不能调度到这个主机上。


3.1.4 MaxGCEPDVolumeCount

   确保已挂载的GCE存储卷不超过设置的最大值。默认值是16。规则同上。


3.1.5 PodFitsResources

   检查主机的资源是否满足Pod的需求。根据实际已经分配的资源量做调度,而 不是使用已实际使用的资源量做调度。

   a) allowedPodNumber: node上最大的可运行pod个数(默认应该是110个)如果该node上已经运行了超过最大允许个数则该node不能再跑新的pod。

   b) memory: 该node上已经运行pod所请求的内存大小加当前待调度的pod所请求的内存大小不能超过该node可以分配的内存总和。pod的请求大小  需在创建的时候配置负责默认请求大小为0, 其次实验时发现node的可分配内存为物理内存实际大小而不是该node剩余内存大小。

   c)CPU:node上的可用cpu资源是根据node上的cpu个数决定的一个cpu为1000(如果该node的cpu个数为8个那么它的可用cpu资源为8000),同上创建pod的时候需指定所需cpu资源。

   d)GPU:在实验的时候发现可用GPU资源为0, 应该是这个还没有启用。

      

3.1.6 PodFitsHost

   如果创建pod的时候有指定NodeName则会在PodFitsHost上去匹配node.Name。


3.1.7 PodFitsHostPorts

   检查Pod内每一个容器所需的HostPort是否已被其它容器占用。如果有所需的HostPort不满足需求,那么Pod不能调度到这个主机上。


3.1.8 PodSelectorMatches

   检查Node的labels是否满足Pod的nodeSelector属性需求。从Pod的Annotations中取出其NodeAffinity,检查Node的labels是否满足Pod的NodeAffinity。(NodeAffinity的匹配是比nodeSelector更加灵活的匹配规则,nodeSelector是精确的==匹配但是NodeAffinity支持 in, notin,==, >, <=, exist, notexist等匹配规则)


3.1.9 PodToleratesNodeTaints

   检测Pod是否能容忍Node上的taints。从Pod的Annotations取出Pod所能容忍的taints列表,然后判断Node的上的所有taints是否多是Pod所能容忍的。


3.1.10 CheckNodeMemoryPressure

   根据Pod创建时Container对资源(CPU资源,Memory资源,GPU资源)的请求的声明(需要多少Requests,不能超过多少Limits),将Pod分为如下如下三类:1)保证资源完全可靠(Guaranteed),Pod中的Container全部定义了Requests和Limits且Requests==Limits != 0;尽力而为不太可靠(BestEffort),Pod中的Container全部未定义Requests和Limits;弹性波动比较可靠(Burstable)所有不属于BestEffort和Guaranteed的Pod。如果Pod输入上面三中类型中的BestEffort,且Node的当前MemoryPressure状态为true则该Pod不能调度到该Node上(在Node的Memory比较紧张的时候将不再接收BestEffort的Pod)。


3.1.11 CheckNodeDiskPressure

   当Node的当前DiskPressure状态为true时不再接受任何Pod调度。


3.1.12 MatchInterPodAffinity

     这个策略感觉对某些特殊调度需求还是很有用对,而MatchInterPodAffinity策略主要实现的是Pod三种亲和性(NodeAffinity, PodAffinity和PodAntiAffinity)中的PodAffinity和PodAntiAffinity。PodAntiAffinity是只要调度的Pod不能和哪些Pod部署到同一个拓扑域中(这里的拓扑域是根据Node的label属性来划分的如Nodename,那么在一个Node上有运行某一个Pod的PodAntiAffinity满足当前待调度的Pod则该Pod不能被调度到这个Node上,也就是说如果有一个Node上有一个Pod不希望你那么你就被能被调度到这个Node上了)。PodAffinity则相反如果待调度的Pod有定义PodAffinity,那这个Pod只能被调度到满足其亲和性的当前运行的Pod的所运行的拓扑域中(如果没有Pod满足其亲和性则Pod自身必须满足其亲和性否则该Pod将会一直Pending)。如下为该策略的详细检测流程:

1)检测Pod是否满足已经运行Pod的PodAntiAffinity,如果不满足在该Node不能用来调度该Pod,并返回,否则转2);

    1.1)遍历当前所有运行的Pod;

    1.2)如果运行的existingPod是否有定义PodAntiAffinity,如果有则遍历PodAntiAffinity里每一个Term;

    1.3)如果该Term定义的Namespace和Selector满足当前待调度Pod的namespace和label定义,且existingPod所运行的Node与当前待检测的Node属于同一个拓扑域中在该Node不能用来调度该Pod,返回;

2)检测Pod的PodAffinity,如果不满足则该Node不能用来调度这个Pod,返回,否则转3);

    2.1)遍历Pod的PodAffinity里所有定义的Term;

    2.2)遍历所有已经运行的Pod;

    2.3)如果existingPod的namespace和label定义满足Term,但existingPod所运行Node的拓扑域与待检测Node的拓扑域不相同则返回该Node不能用来调度该Pod;

3)检测Pod的PodAntiAffinity,如果不满足则返回,否则该Node可以被用来调度该Pod;

   3.1)遍历Pod的PodAntiAffinity里所有定义的Term;

   3.2)遍历所有已经运行的Pod;

   3.3)如果有existingPod的namespace和label定义满足Term在返回在Node不能用来调度该Pod。

   对如上3步流程的总结如下:1)如果已经运行的Pod不喜欢这个Pod则该Pod不能被调度到其拓扑域上运行;2)Pod只能调度到其所亲和的Pod所运行的拓扑域中。3)Pod不能被调度到其自身不喜欢的Pod所运行的拓扑域。


PodAntiAffinity应用场景有:

  1)将服务的Pod分散在不通的拓扑域中以提高稳定性;

  2)把有可能相互影响的Pod分散到不同Node上;

  3)为了保证对独占资源的访问隔离;


PodAffinity应用场景有:

  1)将某一组特定的服务部署到同一拓扑域中;

  2)假设有一个Pod1依赖Pod2所提供的服务,为了减少网络延时需要把Pod1和Pod2部署到同一拓扑域中(如同一个机房,或者同一个交换机上);


3.2 Priority策略

   根据上一步过滤出来的Node列表给每一个Node打分的Priority策略,每一个打分策略打分为0-10,将该项打分乘以其打分权重及为该项打分最后分值,所有打分项的打分累加则为该Node的最后得分。然后选择得分越高的Node作为Pod的调度Node,当最高打分的Node存在多个时采用round-robin策略来从中选择一个Node。默认的打分策略有如下:

2.2.1 LeastRequestedPriority

   如果新的pod要分配给一个节点,这个节点的优先级就由节点空闲的那部分与总容 量的比值(即(总容量-节点上pod的容量总和-新pod的容量)/总容量)来决定。CPU和memory权重相当,比值最大的节点的得分最高。需要注意 的是,这个优先级函数起到了按照资源消耗来跨节点分配pods的作用(注意:当创建pod的时候没有指定请求memory和cpu资源大小,在计算的时候这些pod的CPU和memory资源默认分别为100和200×1024×1024)。计算公式如下:

cpu((capacity – sum(requested)) * 10 / capacity) + memory((capacity – sum(requested)) * 10 / capacity) / 2


3.2.2 BalancedResourceAllocation

   尽量选择在部署Pod后各项资源更均衡的机 器。它 分别计算主机上的cpu和memory的比重,主机的分值由cpu比重和memory比重的“距离”决定(同上如果创建pod的时候没有指定所需cpu和memory资源大小则用默认值cpu为100,memory为200×1024×1024)。计算公式如下:

cpuFraction = (podRequestCpu+nodeRequestedCpu)/totalAllocatableCpu

memoryFraction = (podRequestMemory+nodeRequestedMemory)/totalAllocatableMemory

score = 10 – abs(cpuFraction-memoryFraction)*10


3.2.3 SelectorSpreadPriority

   对于属于同一个service、replication controller的Pod,尽量分散在不同的主机上。如果指定了区域,则会尽量把Pod分散在不同区域的不同主机上。调度一个Pod的时候,先查找 Pod对于的service或者replication controller,然后查找service或replication controller中已存在的Pod,主机上运行的已存在的Pod越少,主机的打分越高,计算方法如下(如下算法可知区域打分占2/3):

  a) 获取pod所属service列表,rc列表;

   b) 合并a)中获得的rc和service的Selector;

   c)遍历所有node分别计算其所有运行的pods计算属于Selector的个数;

  d)  遍历每个node上属于Selector的pod个数求最大个数maxCountByNodeName

   e)   分别计算每个区域运行的属于Selector的pod个数;

  f) 求所有区域中个数最大的个数maxCountByZone;

  g)  fScore = 10 * (float32(maxCountByNodeName-countsByNodeName[node.Name]) / float32(maxCountByNodeName))

  h) 如果有定义区域:

        zoneScore := 10 * (float32(maxCountByZone-countsByZone[zoneId]) / float32(maxCountByZone))

        zoneWeighting = 2.0/3.0

        fScore = (fScore * (1.0 - zoneWeighting)) + (zoneWeighting * zoneScore)


3.2.4 NodePreferAvoidPodsPriority

   Node的Annotations中保存了一份想避免调度到其上运行的RC(ReplicationController)和RS(ReplicaSet)列表,如果待调度的Pod所属的RC或者RS在这个列表里则打分比较低(为了避免其他打分策略影响此项打分策略的权重为1000)。具体打分流程为:

  1. 如果Pod不属于任何RC或者RS则所有Node的打分都是10分;

  2. 从Node的Annotations中取出想避免的PreferAvoidPods列表;

  3. 遍历PreferAvoidPods列表看是否包含Pod所属的RC或者RS,如果有则该Node打10分,否则打10分;


3.2.5 NodeAffinityPriority

   Kubernetes调度中的亲和性机制。Node Selectors(调度时将pod限定在指定节点上),支持多种操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限于对节点labels的精确匹配。另外,Kubernetes支持两种类型的选择器,一种是 “ hard( requiredDuringSchedulingIgnoredDuringExecution )”选择器,它保证所选的主机必须满足所有 Pod对主机的规则要求。这种选择器更像是之前的nodeselector,在nodeselector的基础上增加了更合适的表现语法。另一种是 “soft(preferresDuringSchedulingIgnoredDuringExecution)”选择器,它作为对调度器的提示,调度 器会尽量但不保证满足NodeSelector的所有要求。每一个preferresScheduling Item多有一个权重每个满足该Item的Node会得到相应的权值。具体算法如下:

  a) 从Pod的Annotations中获得其preferresScheduling Items;

  b) 遍历preferresScheduling Items中的每一项Item;

  c) 遍历每一个Node,如果该Node的labels满足Item的Selector则该Node的权值counts[node.Name]加上该Item的权值,同时如果该Node的权值和大于maxCount则更新maxCount;

  d) 上面执行完之后开始给每一个Node计算最后得分 fScore = 10*(counts[node.Name]/maxCount)


3.2.6 TaintTolerationPriority

   Predicate策略里的PodToleratesNodeTaints用的TaintEffectNoSchedule(不满足则无法调度)不同这里用的是TaintEffectPreferNoSchedule(尽量满足,满足条数越多该项打分越高),具体的打分流程如下:

  1. 从Pod的Annotations中取出TaintEffectPreferNoSchedule列表;

  2. 遍历Node List里的每一个Node;

  3. 从Node的Annotations取出taints列表;

  4. 计算Node的taints中Pod所不能容忍的个数保存到counts[node.Name],同时如果counts[node.Name]大于maxCount则更新maxCount。

  5. 上面执行完之后遍历每一个Node分别计算其得分如果maxCount>0则

            fScore=(1.0 - counts[node.Name]/maxCount) * 10; 否则fScore=10;


3.2.7 InterPodAffinityPriority

  



  通过如上的Predicates策略过滤出来一个可以调度的Node List然后在通过Priority给过滤出来的Node List里面的每一个Node进行打分。最后Node根据打分排名打分越高排名越靠前,选择一个打分最高的Node然后将待调度Pod通过apiServer Binder到该Node上。


图 3.1 Pod创建用例图


原创粉丝点击