我们说了整体的调度过程中,分为了Predicates和Priorities两个调度策略作用阶段

我们首先看一下Predicates

Predicates在调度过程中的作用,可以理解为Filter,按照调度策略,从当前集群所有节点中,过滤出符合条件的节点,这些都是可以运行待调度Pod的宿主机

至于怎么筛选出来,默认的调度策略有如下的三种

第一种类型,叫做GeneralPredicates

最为基础的调度策略,比如其中的PodFitsResources计算出的就是宿主机的CPU和内存资源是否够用

当然,我们已经提到过,PodFitsResources检查只是Pod的request字段,Kubernetes的调度器并没有去为一些其他硬件资源定义具体的资源类型,而是统一的使用一种名为Extended Resource的Key-value格式的扩展字段来描述的

apiVersion: v1

kind: Pod

metadata:

name: extended-resource-demo

spec:

containers:

– name: extended-resource-demo-ctr

image: nginx

resources:

requests:

alpha.kubernetes.io/nvidia-gpu: 2

limits:

alpha.kubernetes.io/nvidia-gpu: 2

我们通过alpha.kubernetes.io/nvidia-gpu=2的定义方式,声明使用了两个NVDIA类型的GPU

在PodFitsResources中,调度器并不知道这个值对应的硬件资源是什么,只是直接使用后面的Value进行计算,在Node的Capacity字段,也必须要声明这个字段

而PodFitsHost规则检查的,是宿主机的名字和Pod的spec.nodeName是否一致

PodFitsHostPorts检查的是,Pod申请的宿主机端口是否已经跟被使用的端口有冲突

PodMatchNodeSelector检查的是,Pod的nodeSelector或者nodeAffinity指定的节点是否匹配,是否和待考察节点匹配等

像如上的GeneralPredicates,是判断一个Pod能否运行在一个Node上的基本过滤条件

正如之前说的,Kubelet在启动Pod之前,会执行一个Admit操作进行二次确认,就是执行的GeneralPredicates

第二组类型,则是和Volume相关的过滤规则

检查持久化Volume能否成功调度的策略

比如,NoDiskConflict检查的就是多个Pod挂载的持久化Volume是否有冲突,AWS类型的Volume,不被允许同时被两个Pod使用的,如果一个A的EBS已经挂载到了某个节点了,其他声明使用A的就不能在调度到相同节点了

而在MaxPDVolumeCountPredicate检查的条件中,则是某一个节点的特定类型的持久化Volume是不是超过了一定数目,如果是的话,声明使用该类型持久化的Pod就不能再调度到这个节点上了

而VolumeZonePredicate,就是检查持久化Volume的Zone标签是否相匹配

还有一个专门给本地化存储使用的VolumeBindingPredicate的规则,就是这个Pod对应的PV的nodeAffinity,是否和某个节点标签对应

因为Local Persistent Volue,必须要使用nodeAffininty来根据某个具体的节点绑定,在Predicates阶段,Kubernetes要根据Pod的Volume排除某些不能调度的节点

如果对应的Pod的pvc 没有和具体的PV绑定,调度器还要检查所有待绑定的PV,有可用的PV并且该PV的nodeAffinity和待考察的一致,这个规则才会成功

apiVersion: v1

kind: PersistentVolume

metadata:

name: example-local-pv

spec:

capacity:

storage: 500Gi

accessModes:

– ReadWriteOnce

persistentVolumeReclaimPolicy: Retain

storageClassName: local-storage

local:

path: /mnt/disks/vol1

nodeAffinity:

required:

nodeSelectorTerms:

– matchExpressions:

– key: kubernetes.io/hostname

operator: In

values:

– my-node

而上面的PV声明的nodeAffinity,是出现在my-node宿主机上,所以任何通过PVC使用这个PV的Pod,都需要调度到my-node上才可以正常工作,VolumeBindingPredicate,就是筛选这些节点的规则

最后是宿主机相关的规则

比如,PodToleratesNodeTaints,就是检查之前说的Node上的污点机制,只有当Pod的Toleration字段和Node的Taint能够匹配的时候,才能进行调度上去

同样,还有着NodeMemoryPressurePredicate,检查的是当前节点的内存是不是还够,如果是的话,待调度的Pod就不能被调度在这个节点上

最后是Pod相关的过滤规则

这一部分和GeneralPredicates很大一部分重合,其中比较特殊的,是PodAffinityPredicate,这个规则的作用,是检查Pod和Node上的Pod的亲密和反亲密关系

apiVersion: v1

kind: Pod

metadata:

name: with-pod-antiaffinity

spec:

affinity:

podAntiAffinity:

requiredDuringSchedulingIgnoredDuringExecution:

– weight: 100

podAffinityTerm:

labelSelector:

matchExpressions:

– key: security

operator: In

values:

– S2

topologyKey: kubernetes.io/hostname

containers:

– name: with-pod-affinity

image: docker.io/ocpqe/hello-pod

上面给出的PodAntiAffinity,指定了不希望和带有S2的标签的Pod放在同一个Node上,PodAffinityPredicate的作用于是对key为kubrnetes.io/hostname标签的Node有效,也是topologyKey这个关键词的作用

PodAntiAffinity相反的是podAffinity,比如下面的例子

apiVersion: v1

kind: Pod

metadata:

name: with-pod-affinity

spec:

affinity:

podAffinity:

requiredDuringSchedulingIgnoredDuringExecution:

– labelSelector:

matchExpressions:

– key: security

operator: In

values:

– S1

topologyKey: failure-domain.beta.kubernetes.io/zone

containers:

– name: with-pod-affinity

image: docker.io/ocpqe/hello-pod

上面的规则就是携带了Security=S1标签的Pod运行的Node上

上面例子中requiredDuringSchedulingIgnoredDuringExection字段的含义是,就是在Pod调度的时候进行检查,如果已经在运行的Pod发生了变化,比如Label被修改,造成了该Pod不再适合运行在这个Node上的时候,Kubernetes不会进行主动修正

我们上述的四个Predicates,构成了调度器确立了一个Node可以运行待调度Pod的基本策略

在具体执行的时候,当开始调度一个Pod时候,Kubernetes调度器同时启动16个Gorooutine,并发地为集群内所有的Node计算Predicates,最后返回所有可以运行的Pod宿主列表

需要注意的是,每个Node执行Predicates,调度器会按照固定的顺序来进行检查,比如优先去检查宿主机相关的Predicates会优先检查,不然的话,一台资源已经严重不足的宿主机的话,没有必要再去看PodAffinityPredicate

然后是Priorities

在Predicates阶段完成了节点的过滤后,Priorities阶段的工作,就从选出的节点列表中挑选出最佳的节点

常用的一个打分规则,就是LeastRequestedPriority,计算方式可以简单的列为如下的公式

score = (cpu((capacity-sum(requested))10/capacity) + memory((capacity-sum(requested))10/capacity))/2

这个公式是为了计算空闲资源最多的宿主机

然后就是公式 BalancedResourceAllocation,计算公式如下

score = 10 – variance(cpuFraction,memoryFraction,volumeFraction)*10

其中的Faction的定义,是 Pod所需资源除以节点可用资源,variance算法的作用,是计算两种资源之间的距离,最后选择的时候,两个资源差距最小的节点

BalancedResourceAllocation选择的,是调度完成后,所有节点中资源分配最均衡的节点,避免一个节点上CPU大量被使用,Memory大量剩余的情况.

此外,还有着 NodeAffinityPriority TaintTolerationPriority.和 InterPodAffinityPriority三种Priority,这和前面的几种Predicate的含义和计算方法是类似的

还有就是新出的ImageLocalityPriority策略,是在Kubernetes1.12后新的调度规则.待调度Pod需要使用的镜像很大,并且已经存在于某些Node上,会给这些Node多一些分

这些规则执行完成,就能获得一个节点的最终得分

而且,调度器中关于集群和Pod的信息已经缓存起来了,这些算法的执行过程还是比较快的

那么,在本章中,我们讲述了Kubernetes默认调度器中主要的调度算法

这些规则的开启或者关闭,是可以通过kube-scheduler指定一个配置文件或者创建一个ConfigMap,来进行指定的开启或者关闭,我们或者可以设置权重来控制调度行为

那么我们如何控制将Pod分布在不同的机器上,避免堆叠呢?

1.我们可以为pod.yaml设置PreferredDuringSchedulingIgnoredDuringExection,指定不想和相同label的pod放一起,这样在podAntiAffinity在安排Pod的生效节点时候,不想在一起的pod数量越多分越低,打散同一个Service下多个Pod的分布

K8S在官网给出了对应的应用实例,

podAffinity:

requiredDuringSchedulingIgnoredDuringExecution:

– labelSelector:

matchExpressions:

– key: security

operator: In

values:

– S1

2.SelectorSpreadPriority,Kubernetes内置的一个priority,于Services上其他的pod尽可能的不再同一个节点,节点上同一个Service的pod数量越少得分越高

3.自定义策略,实现自己的负载均衡算法

发表评论

邮箱地址不会被公开。 必填项已用*标注