Kube-Scheduler Policy
本文分析了Kubernetes内置的各种调度策略。
Predicate
整体梳理
| 策略名称 | 策略算法 | 备注 |
|---|---|---|
| CheckNodeUnschedulable | 在 Node 节点上有一个 NodeUnschedulable 的标记,那这个节点就不会被调度了 | |
| CheckVolumeBinding | 在 pvc 和 pv 的 binding 过程中对其进行逻辑校验 | |
| GeneralPredicates | 是 PodFitsHostPorts,PodFitsResources,HostName,MatchNodeSelector这四个的组合 | |
| MatchInterPodAffinity | 亲和性检查,当Node上所有正在运行的Pod与待调度的Pod不互相排斥时,则可调度 | |
| MaxAzureDiskVolumeCount | 当Node上被挂载的Azure Disk Volume超过默认限制,该Node不可调度 | |
| MaxCSIVolumeCountPred | 当Node上被挂载的CSI Volume超过默认限制,该Node不可调度 | |
| MaxEBSVolumeCount | 当Node上被挂载的AWS EBS Volume超过默认限制39,该Node不可调度 | |
| MaxGCEPDVolumeCount | 当Node上被挂载的GCD Persistent Disk超过默认限制16,该Node不可调度 | |
| MaxQcloudCbsVolumeCount | 当Node上被挂载的Qcloud CBS Volume超过默认限制,该Node不可调度 | |
| NoDiskConflict | 当Node上所有Pod使用的卷和待调度Pod使用的卷存在冲突,该Node不可调度 | |
| NoVolumeZoneConflict | 当Node上的zone-lable包含Pod中PV卷下的zone-label时,可以调度。当Node上没有zone-label,表示没有zone限制,也可调度 | |
| PodToleratesNodeTaints | 当Pod可以容忍Node上所有的taint时,该Node才可以调度 | |
| PodFitsHostPorts | 当待调度Pod中所有容器所用到的HostPort与Node上已使用的Port存在冲突,则无法调度 | |
| PodFitsResources | 当总资源-Node中所有Pod对资源的request总量 < 待调度的Pod request总量,则无法调度 | |
| HostName | 如果待调度的Pod制定了pod.Spec.Host,则调度到该主机上 | |
| MatchNodeSelector | 校验 Pod.Spec.Affinity.NodeAffinity 和 Pod.Spec.NodeSelector 是否与 Node 的 Labels 匹配 | |
| CheckNodeMemoryPressure | 当Node剩余内存紧张时,BestEffort类型的Pod无法调度到该主机 | |
| CheckNodeDiskPressure | 当Node剩余磁盘空间紧张时,无法调度到该主机 | |
| PodFitsHostPorts | 当待调度Pod中所有容器所用到的HostPort与Node上已使用的Port存在冲突,则无法调度 | |
| PodFitsResources | 当总资源-Node中所有Pod对资源的request总量 < 待调度的Pod request总量,则无法调度 | |
| HostName | 如果待调度的Pod制定了pod.Spec.Host,则调度到该主机上 | |
| EvenPodsSpread | 在1.18版本默认启动,符合条件的一组 Pod 在指定 TopologyKey 上的打散要求 | |
| CheckNodeLabelPresence | 主要用于检查指定的Label是否在Node上存在 | |
| CheckServiceAffinityPred | 根据当前POD对象所属的service已有的其他POD对象所运行的节点进行调度,其目的在于将相同service的POD 对象放置与同一个或同一类节点上以提高效率,此预选此类试图将那些在其节点选择器中带有特定标签的POD资源调度至拥有同样标签的节点上,具体的标签则取决于用户的定义。 |
存储相关
NoVolumeZoneConflictPred
当在 k8s 集群中使用 zone 时,所有的Node都会被标记上 zone label,下面四种是常见的lable的key:
|
|
举个例子:
|
|
当一个Pod有存储卷要求时,需要检查该存储卷的zone调度约束是否与Node的zone限制存在冲突。
|
|
通过检查的条件是:属于该Pod的所有volumes都必须与Node上的zone label完全匹配。
算法注册逻辑:
|
|
CheckVolumeBindingPred
在 pvc 和 pv 的 binding 过程中对其进行逻辑校验,里头的逻辑写的比较复杂,主要都是如何复用 pv;
算法注册逻辑:
|
|
NoDiskConflictPred
SCSI 存储不会被重复的 volume, 检查在此主机上是否存在卷冲突。如果这个主机已经挂载了卷,其它同样使用这个卷的Pod不能调度到这个主机上,不同的存储后端具体规则不同
算法注册逻辑:
|
|
MaxCSIVolumeCountPred
一个Pod请求Volumes的时候,节点上可能已经有Volumes,需要检查加上这个Pod之后的Volumes是否超过Node最大允许的Volumes限制。MaxCSIVolumeCountPred 用来校验 pvc 上指定的 Provision 在 CSI plugin 上的单机最大 pv 数限制。
|
|
算法注册逻辑:
|
|
MaxNonCSIVolumeCountPred
对于不是CSI标准的存储插件,也需要满足最大PV数限制,整体逻辑类似。
|
|
MaxEBSVolumeCountPred
算法注册逻辑:
|
|
MaxGCEPDVolumeCountPred
算法注册逻辑:
|
|
MaxAzureDiskVolumeCountPred
算法注册逻辑:
|
|
MaxCinderVolumeCountPred
算法注册逻辑:
|
|
Pod 与 Node 匹配相关
- CheckNodeCondition:校验节点是否准备好被调度,校验node.condition的condition type :Ready为true和NetworkUnavailable为false以及Node.Spec.Unschedulable为false;
- PodFitsHostPorts:校验 Pod 上的 Container 声明的 Ports 是否正在被 Node 上已经分配的 Pod 使用;
- MatchNodeSelector: 校验 Pod.Spec.Affinity.NodeAffinity 和 Pod.Spec.NodeSelector 是否与 Node 的 Labels 匹配。
PodFitsHostPortsPred
PodFitsHostPorts策略主要用于校验 Pod 上的 Container 声明的 Ports 是否正在被 Node 上已经分配的 Pod 使用。
在 PreFilter 阶段,获取当前 Pod 对应的所有容器的Port,并且写入cycleState。
|
|
在 Filter 阶段,从Cycle拿到当前Pod请求的Port,对比当前系统中已使用的 Port,看是否会发生冲突。
|
|
算法注册逻辑:
|
|
PodFitsResourcesPred
算法注册逻辑:
|
|
PodToleratesNodeTaintsPred
PodToleratesNodeTaints策略校验 Node 的 Taints 是否被 Pod Tolerates 包含。这里主要检查 NoSchedule 和 NoExecute 这两个 taint,如果不容忍,那么返回错误。
|
|
算法注册逻辑:
|
|
HostNamePred
NodeNamePred策略主要用于检查Pod Spec声明的Node Name是否与Node实际的Name匹配。
|
|
算法注册逻辑:
|
|
MatchNodeSelectorPred
MatchNodeSelectorPred策略用于校验 Pod.Spec.Affinity.NodeAffinity 和 Pod.Spec.NodeSelector 是否与 Node 的 Labels 匹配 。
|
|
这是一个典型的Node亲和性示例:
|
|
算法注册逻辑:
|
|
GeneralPred
算法注册逻辑:
|
|
CheckNodeUnschedulablePred
CheckNodeUnschedulable 在 node 节点上有一个 NodeUnschedulable 的标记,那这个节点就不会被调度了,形如这种。
|
|
在 1.16 的版本里,这个 Unschedulable 已经变成了一个 Taints。也就是说需要校验一下 Pod 上打上的 Tolerates 是不是可以容忍这个 Taints。如果容忍了这个不可调度的taint,那么它也可以容忍 NodeSpec的不可调度。
|
|
算法注册逻辑:
|
|
CheckNodeLabelPresencePred
CheckNodeLablePresencePred策略主要用于检查指定的Label是否在Node上存在。这里检查的是两种情况:
- 一种检查Node上面是否有指定Label。比如有时候通过
region/zone/racks这种label来划分空间,想要把Pod调度到有特定region/zone/racks的Node。 - 一种是检查Node上面是否没有指定的Label。比如有的Node被打上
retiring的 label,想要制定Pod不调度到这些Node上。
|
|
对这个策略,需要在注册的时候设定策略插件的参数。
算法注册逻辑:
|
|
Pod 与 Pod 匹配相关
MatchInterPodAffinityPred
算法注册逻辑:
|
|
Pod 服务打散相关
EvenPodsSpread
算法注册逻辑:
|
|
CheckServiceAffinity
算法注册逻辑:
|
|
Priority
整体梳理
| 策略名称 | 策略算法 | 权重 |
|---|---|---|
| BalancedResourceAllocation* | CPU和内存利用率越接近,得分越高 | 1 |
| ImageLocalityPriority* | 待调度的Pod会使用一些镜像,拥有这些镜像越多的节点,得分越高 | 1 |
| InterPodAffinityPriority* | Pod与Node上正运行的其他Pod亲和性匹配度越高,得分越高 | 1 |
| LeastRequestedPriority* | 剩余资源越多,得分越高 | 1 |
| NodeAffinityPriority* | Pod与Node亲和性匹配度越高,得分越高 | 1 |
| NodePreferAvoidPodsPriority* | 该Node的annotation scheduler.alpha.kubernetes.io/preferAvoidPods被设置时,说明该Node不希望被调度,得分低。 | 10000 |
| SelectorSpreadPriority* | 相同service/rc的Pods越分散,得分越高 | 1 |
| TaintTolerationPriority* | Pod对Node的taint容忍度越高,得分越高 | 1 |
| ServiceSpreadingPriority | 相同Service的Pods越分散,得分越高,被 SelectorSpreadPriority取代,保留在系统中并不使用 | 1 |
| EqualPriority | 所有机器得分一样 | 1 |
| MostRequestPriority | Request资源越多,得分越高,与LeastRequestPriority相反 | 1 |
| EvenPodsSpreadPriority | 在1.18版本默认启动,用来指定一组符合条件的 Pod 在某个拓扑结构上的打散需求,这样是比较灵活、比较定制化的一种方式,使用起来也是比较复杂的一种方式 | 2 |
| RequestedToCapacityRatioName | 允许用户对于CPU、内存和扩展加速卡等资源实现bin packing | |
| NodeLabel | 主要是为了实现对某些特定 label 的 Node 优先分配,算法很简单,启动时候依据调度策略 (SchedulerPolicy)配置的 label 值,判断 Node 上是否满足这个label条件,如果满足条件的节点优先分配。 | |
| ServiceAffinity | 是为了支持 Service 下的 Pod 的分布要按照 Node 的某个 label 的值进行均衡。 | |
打分算法主要解决的问题就是集群的碎片、容灾、水位、亲和、反亲和等,可以分为以下四个大类。
资源水位
- 资源水位公式的概念:Request:Node 已经分配的资源;Allocatable:Node 的可调度的资源。
- 优先打散:把 Pod 分到资源空闲率最高的节点上,而非空闲资源最大的节点,公式:资源空闲率 = (Allocatable - Request) / Allocatable,当这个值越大,表示分数越高,优先分配到高分数的节点。其中 (Allocatable - Request) 表示为 Pod 分配到这个节点之后空闲的资源数。
- 优先堆叠:把 Pod 分配到资源使用率最高的节点上,公式:资源使用率 = Request / Allocatable ,资源使用率越高,表示得分越高,会优先分配到高分数的节点。
- 碎片率:是指 Node 上的多种资源之间的资源使用率的差值,目前支持 CPU/Mem/Disk 三类资源, 假如仅考虑 CPU/Mem,那么碎片率的公式 = Abs[CPU(Request / Allocatable) - Mem(Request / Allocatable)] 。举一个例子,当 CPU 的分配率是 99%,内存的分配率是 50%,那么碎片率 = 99% - 50% = 50%,那么这个例子中剩余 1% CPU, 50% Mem,很难有这类规格的容器能用完 Mem。得分 = 1 - 碎片率,碎片率越高得分低。
- 指定比率:可以在 Scheduler 启动的时候,为每一个资源使用率设置得分,从而实现控制集群上 node 资源分配分布曲线。
LeastRequestedPriority
LeastRequestedPriority 策略对于那些使用率越低的Node的优先级越高。通过这种算法,可以使得各个节点的资源得到均衡利用。
计算公式如下: $$ (cpu((capacity-sum(requested))*MaxNodeScore/capacity) + memory((capacity-sum(requested))*MaxNodeScore/capacity))/weightSum $$
算法注册逻辑:
|
|
MostRequestedPriority
MostRequestedPriority 策略对于那些使用率更高的Node的优先级更高。这种算法在动态伸缩集群环境比较适用,会优先调度pod到使用率最高的主机节点,这样在伸缩集群时,就会腾出空闲机器,从而进行停机处理。
其计算公式如下: $$ (cpu(MaxNodeScore * sum(requested) / capacity) + memory(MaxNodeScore * sum(requested) / capacity)) / weightSum $$ 算法注册逻辑:
|
|
BalancedResourceAllocation
BalancedResourceAllocation:尽量选择在部署Pod后各项资源更均衡的机器。BalancedResourceAllocation不能单独使用,而且必须和LeastRequestedPriority同时使用,它分别计算主机上的cpu和memory的比重,主机的分值由cpu比重和memory比重的“距离”决定。
计算公式如下: $$ score = (1 - variance(cpuFraction,memoryFraction,volumeFraction)) * MaxNodeScore $$ 算法注册逻辑:
|
|
RequestedToCapacityRatioPriority
RequestedToCapacityRatioPriority允许用户对于CPU、内存和扩展加速卡等资源实现bin packing。
所谓 Bin Packing ,又称装箱问题,是运筹学中的一个经典问题。问题的背景是,现有若干个小盒子,想要把它们装进有限个给定大小的箱子中,如何既能够装的多油装的快,使得尽可能每个箱子都装满,从而减少箱子的使用数目。BinPack问题有很多变种,当限制箱子的数目为1,每个盒子给定value和weight,binpack问题就变成了背包问题。
Kubernetes默认开启的资源调度策略是Spread的策略,资源尽量打散,但是会导致较多的资源碎片,使得整体资源利用率下降。通过RequestedToCapacityRatioPriority配置支持CPU、内存和GPU等扩展卡的权重,在打分阶段计算对应资源的利用率,通过利用率进行排序,优先打满一个节点后再向后调度,从而实现bin packing。
RequestedToCapacityRatioResourceAllocation 优先级函数的行为可以通过名为 requestedToCapacityRatioArguments 的配置选项进行控制。 该标志由两个参数 shape 和 resources 组成。 shape 允许用户根据 utilization 和 score 值将函数调整为最少请求(least requested)或 最多请求(most requested)计算。 resources 由 name 和 weight 组成,name 指定评分时要考虑的资源,weight 指定每种资源的权重。
以下是一个配置示例,该配置将 requestedToCapacityRatioArguments 设置为对扩展资源 intel.com/foo 和 intel.com/bar 的装箱行为
|
|
实际上,这里的shape参数定义的是不同utilization下对应的得分,是对 LeastRequestedPriority 和 MostRequestedPriority 的进一步抽象。
这种配置对应的是LeastRequestedPriority
|
|
这种配置对应的是MostRequestedPriority
|
|
算法注册逻辑:
|
|
Pod 打散
Pod打散目的是支持符合条件的一组 Pod 在不同 topology 上部署的 spread 需求。
ServiceSpreadingPriority
ServiceSpreadingPriority:官方注释上说大概率会用来替换 SelectorSpreadPriority,为什么呢?我个人理解:Service 代表一组服务,我们只要能做到服务的打散分配就足够了。
算法注册逻辑:
|
|
EvenPodsSpread
EvenPodsSpreadPriority:用来指定一组符合条件的 Pod 在某个拓扑结构上的打散需求,这样是比较灵活、比较定制化的一种方式,使用起来也是比较复杂的一种方式。因为这个使用方式可能会一直变化,我们假设这个拓扑结构是这样的:Spec 是要求在 node 上进行分布的,我们就可以按照上图中的计算公式,计算一下在这个 node 上满足 Spec 指定 labelSelector 条件的 pod 数量,然后计算一下最大的差值,接着计算一下 Node 分配的权重,如果说这个值越大,表示这个值越优先。
算法注册逻辑:
|
|
CheckServiceAffinity
算法注册逻辑:
|
|
SelectorSpreadPriority
SelectorSpreadPriority:用于实现 Pod 所属的 Controller 下所有的 Pod 在 Node 上打散的要求。实现方式是这样的:它会依据待分配的 Pod 所属的 controller,计算该 controller 下的所有 Pod,假设总数为 T,对这些 Pod 按照所在的 Node 分组统计;假设为 N (表示为某个 Node 上的统计值),那么对 Node上的分数统计为 (T-N)/T 的分数,值越大表示这个节点的 controller 部署的越少,分数越高,从而达到 workload 的 pod 打散需求。
算法注册逻辑:
|
|
Pod 亲和/反亲和
InterPodAffinityPriority
InterPodAffinityPriority:先介绍一下使用场景:第一个例子,比如说应用 A 提供数据,应用 B 提供服务,A 和 B 部署在一起可以走本地网络,优化网络传输;第二个例子,如果应用 A 和应用 B 之间都是 CPU 密集型应用,而且证明它们之间是会互相干扰的,那么可以通过这个规则设置尽量让它们不在一个节点上。pod亲和性选择策略,类似NodeAffinityPriority,提供两种选择器支持:requiredDuringSchedulingIgnoredDuringExecution(保证所选的主机必须满足所有Pod对主机的规则要求)、preferresDuringSchedulingIgnoredDuringExecution(调度器会尽量但不保证满足NodeSelector的所有要求),两个子策略:podAffinity和podAntiAffinity
算法注册逻辑:
|
|
Node 亲和/反亲和
- NodeAffinityPriority,这个是为了满足 Pod 和 Node 的亲和 & 反亲和;
- ServiceAntiAffinity,是为了支持 Service 下的 Pod 的分布要按照 Node 的某个 label 的值进行均衡。比如:集群的节点有云上也有云下两组节点,我们要求服务在云上云下均衡去分布,假设 Node 上有某个 label,那我们就可以用这个 ServiceAntiAffinity 进行打散分布;
- NodeLabelPrioritizer,主要是为了实现对某些特定 label 的 Node 优先分配,算法很简单,启动时候依据调度策略 (SchedulerPolicy)配置的 label 值,判断 Node 上是否满足这个label条件,如果满足条件的节点优先分配;
- ImageLocalityPriority,节点亲和主要考虑的是镜像下载的速度。如果节点里面存在镜像的话,优先把 Pod 调度到这个节点上,这里还会去考虑镜像的大小,比如这个 Pod 有好几个镜像,镜像越大下载速度越慢,它会按照节点上已经存在的镜像大小优先级亲和。
NodePreferAvoidPodsPriority
NodePreferAvoidPodsPriority策略用于实现某些 controller 尽量不分配到某些节点上的能力;通过在 node 上加 annotation 声明哪些 controller 不要分配到 Node 上,如果不满足就优先。
具体实现就是会在Node上加上Annotation,形如这种
|
|
在检查的时候,对于那些不被 ReplicaSet 和 ReplicationController 拥有的 Pod,直接跳过,给予最高分。如果和 annotation 中标记的相同,那么给予最低分。
|
|
算法注册逻辑:
|
|
NodeAffinityPriority
NodeAffinityPriority策略用于满足Pod与Node之间的亲和与反亲和。
|
|
算法注册逻辑:
|
|
TaintTolerationPriority
TaintTolerationPriority 策略,Pod 对 Node 的 taint 容忍程度越高,优先级越大。
在 PreScore 阶段,拿到所有 all Tolerations with Effect PreferNoSchedule or with no effect,并将其写到cycleState。
|
|
在 Score 阶段,具体算法就是Pod不能容忍的taint越多,那么得分就越高(之后会在Normalize处正则化,将得分逆序),也就是其优先级越低。
|
|
算法注册逻辑:
|
|
ImageLocalityPriority
ImageLocalityPriority策略主要考虑的是镜像下载的速度。如果节点里面存在镜像的话,优先把 Pod 调度到这个节点上,这里还会去考虑镜像的大小,比如这个 Pod 有好几个镜像,镜像越大下载速度越慢,它会按照节点上已经存在的镜像大小优先级亲和。
|
|
算法注册逻辑:
|
|
NodeLabel
NodeLabel策略主要是为了实现对某些特定 label 的 Node 优先分配,算法很简单,启动时候依据调度策略 (SchedulerPolicy)配置的 label 值,判断 Node 上是否满足这个label条件,如果满足条件的节点优先分配。
|
|
算法注册逻辑:
|
|
-
No backlinks found.