13.调度器、预选策略及优选函数、高级调度方式

参见:调度器、预选策略与优选函数 https://www.cnblogs.com/weiyiming007/p/10560518.html

一、前言

master上运行着三个最核心的组件,apiserver、scheduler、controller manager。此外,master还依赖于ectd存储节点,最好ectd是有冗余能力的集群。

1、调度器(scheduler)

master上的scheduler控制着pod运行在哪个node上,默认用的default-scheduler(用describe时详情里可以看到用的哪个调度器);

调度器的调度信息存储在master上的etcd里面,apiserver负责和etcd通信;

kubelet运行在node节点上,监控着Node节点上的pod状态,并参与pod的创建等工作;

kube-proxy也运行在node节点上,它监控着service资源的变动, 然后将其在当前节点上创建为iptables或ipvs规则。Kube-proxy是管理service的组件;

kubelet和kube-proxy都要连接master上的apiserver去获取定义信息;

2、调度过程

预选策略(predicate):先排除那些完全不符合此pod运行法则的节点,有两个维度来排除,一个是最低资源需求(request),即节点必须满足此Pod的最低资源;第二个是资源限额(limit),即当前节点最多能给pod提供的资源;

优选(priority):在符合节点找到最佳匹配的节点;

选定(select):把pod绑定到优选的节点上,如果有多个最佳节点,就会随机选一个;

3、K8S的高级调度方式

nodeAffinity:表示node亲和性调度,表示这个pod对这个节点有一定的倾向性。我们通过nodeselector来完成这类调度;

podAffinity或podAntiAffinity:pod亲和性或者pod反亲和性调度,有时我们期望某些Pod运行在同一个节点上或者是相邻的节点上,或者我们期望某些Pod不要运行在某些节点上;

taints和tolerations:污点和污点容忍调度:可以在某些节点上打上污点,表示这些节点不让pod在其上面运行。容忍某节点上污点的子集,则可以该Pod可被调度到该节点上。taints是定义在节点之上的,tolerations是定义在pod之上的。

一般是基于最大资源的空闲率来调度。

二、预选策略

1.源码

https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go   # 预选策略很多,参见源码。

定义的策略很多,但是很多策略默认是没有启用的,可以手动配置启用。

2.常用的预选策略

1、CheckNodeCondition:表示检查是否可以在节点磁盘、网络不可用,或未准备好的前提下,能够把pod调度到上去。

2、GeneralPredicates:通用预选策略,不是一个策略,包含好几个预选策略:

       HostName:表示如果pod定义了hostname属性(pod.spec.hostname),则检查节点的名字跟pod的hostname是否相匹配,但这里并不是定义这个pod必须运行在这些节点上;

       PodFitsHostPorts:如果节点定义了hostport属性(pod.spec.containers.ports.hostPort),表示指定在节点的哪个端口上。如果这个节点的端口被占用了,那么这个节点就不适合运行这个pod;

       MatchNodeSelector:检查pods.spec.nodeSelector这个字段你是否定义了,运行在携有这有这些标签选择器的节点上;没有对应的node,则pod无法运行。

       PodFitsResources:检查节点是否有足够的资源来支持这个pod运行;

3、NoDiskConfict:检查Pod所请求的存储卷类型在此节点上是否可用。比如需要glusterfs类型的资源,但该节点不能挂在该类型资源。NoDiskConfict调度策略默认没有启用。

4、PodToleratesNodeTaints:如果Pod定义了Tolerates(容忍度),即pods.spec.tolerations,那么就检查Pod上的spec.tolerations可容忍的污点是否完全包含节点上的污点,如果是,表示这个节点可以被选定;

5、PodToleratesNodeNoExecuteTaints:检查pod是否容忍节点上有NoExecute污点。NoExecute污点:如果一个pod上运行在一个没有污点的节点上,后来这个节点又给加上污点了,那么NoExecute表示这个新加污点的节点会去除其上正在运行的pod;此调度策略默认没有启用;

6、CheckNodeLabelPresence:检查节点上指定标签的存在性,如果节点有pod指定的标签,那么这个节点就被选中。 此调度策略默认没有启用;

7、CheckServiceAffinity:根据当前Pod对象所属的Service已有的其他Pod对象。一个service下可以有多个pod,比如这些pod都运行在1、2、3机器上,而没有运行在4、5、6机器上,那么CheckServceAffinity就表示新加入的pod都集中运行在1、2、3机器上,这样集中好处是一个service下pod之间内部通信的效率变高了。 这个策略默认是没有启用的。

8、CheckVolumeBinding:检查节点上的pvc是否被别的pod绑定了;

9、NoVolumeZoneConflict:区域限制的前提下,检查节点上的pod是否与pod的需求冲突;

10、CheckNodeMemoryPressure:检查节点内存是否存在压力;

11、CheckNodePIDPressure:检查节点pid数量是否压力过大,但一般pid是可以重复使用的;

12、CheckNodeDiskPressure:检查内存/磁盘 IO是否压力过大;

13、MatchInterPodAffinity:检查Pod是否满足亲和性或者反亲和性;

14、MaxCSIVolumeCountPred:检查节点上挂载的附加存储的数量是否超出了最大的设定值。

pod在调度的时候,启用的所有预选策略要逐一评估。(都要满足,一票否决)

三、优选函数

1.源码

https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm/priorities

https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/priorities/priorities.go

2.常用的优选函数

每一个优选函数的最大得分是10分。(得分范围是0-10)

1、LeastRequest:最少请求,空闲比例。对cpu来说,用cpu((capacity-sum(requested))*10/capacity)方式进行评估,得分最高的胜出;内存同样。(cpu得分+mem得分)/2=优选函数得分

2、BalanceResourceAllocation:均衡资源的使用方式,表示以cpu和内存占用率的相近程度(均衡)作为评估标准,二者占用越接近,得分就越高,得分高的胜出。

3、NodePreferAvoidPods:看节点是否有注解信息"scheduler.alpha.kubernetes.io/preferAvoidPods"。没有这个注解信息,说明这个节点是适合运行这个pod的,得分为10。优先级很高。

4、TaintToleration:将pod对象的spec.toleration与节点的taint列表项进行匹配度检查,匹配的条目越多,得分越低;

5、SelectorSpreading:查找当前pod对象对应的service,statefulset,replicatset等所匹配的标签选择器,在节点上运行的带有这样标签的pod越少得分越高。 这就是说我们要把同一个标签选择器下运行的pod散开(spreading)到多个节点上;

6、InterPodAffinity:遍历Pod对象亲和性的条目,并将那些能够匹配到节点权重相加,值越大的得分越高,得分高的胜出;

7、NodeAffinity:根据pod对象中的nodeselector,对节点进行匹配度检查,能够成功匹配的数量越多,得分就越高;

8、MostRequest:表示尽可能的把一个节点的资源先用完,这个和least_requested相反,二者不能同时使用;默认未启用;

9、NodeLabel:根据节点是否拥有标签,不关注标签值,来评估分数;默认未启用;

10、ImageLocality:表示根据满足当前pod运行需要的已有镜像体积大小之和来选择节点的;默认未启用。

启用的优选函数会逐一评估,最后得分相加,根据最终得分来优!!

四、高级调度设置方式

两类调度方式

节点选择器:nodeSelector,nodeName

节点亲和调度:nodeAffinity

(一)、节点选择器nodeSelector(强约束)

[root@master ~]# kubectl explain pod.spec.nodeSelector

 

Pod处于Pending状态,因为调度失败,没有节点拥有disk:ssh标签。

[root@master ~]# kubectl label node node2 disk=ssd  # 给node2打上需要的标签。

可以看到刚才Pending的pod运行到了节点2上。

(二)、节点亲和性调度nodeAffinity

[root@master ~]# kubectl explain pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution:硬亲和性,必须满足
preferredDuringSchedulingIgnoredDuringExecution:软亲和性,不是必须,尽量满足,优先运行在满足条件的节点上。

软亲和性和硬亲和性资源清单定义不相同,不能单独改个单词就是。

1、硬亲和性

[root@master ~]# kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms

 

目前没有节点拥有zone=foo或者zone=bar。

[root@master ~]# kubectl label node node1 zone=foo  #给node1打个标签

Pod状态变为Running。

2、软亲和性

[root@master ~]# kubectl label node node1 zone-  # 先删除node1上的zone标签

目前是没有节点有拥有pod所需要的标签的,但是Pod一经创建马上运行,因为软亲和性,满足最好,不能满足也能将就。

三、Pod亲和性调度podAffinity,podAntiAffinity

比如在机房中,我们可以将一个机柜中的机器都打上标签,让pod调度的时候,对此机柜有亲和性;或者将机柜中某几台机器打上标签,让pod调度的时候,对这几个机器有亲和性;

通过节点亲和性,也能让一组pod运行在一起,但需要对节点进行精心编排,节点拥有的标签,想在一起pod使用相同的标签选择器,不想在一起的pod使用不同的标签选择器,比较麻烦。

Pod亲和性,第一个Pod随机调度,后面的Pod都以此为标准,运行在相近节点上,不需要考虑节点标签配置。但仍需要有一个逻辑条件预先设置好哪些节点是属于相近节点,同一位置,让pod去判断。

1、Pod亲和性podAffinity

[root@master ~]# kubectl explain pod.spec.affinity.podAffinity
preferredDuringSchedulingIgnoredDuringExecution:软亲和性
requiredDuringSchedulingIgnoredDuringExecution:硬亲和性

硬亲和性

[root@master ~]# kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution

第二个Pod定义的podAffinity意思为:当前这个pod要跟一个有着标签app=myapp(要和上面pod-first的metadata里面的标签一致)的pod在一起。

topologyKey: kubernetes.io/hostname意思为:节点标签kubernetes.io/hostname相同的为相近节点。

由于定义的是硬亲和性,如果调度器找不到运行着app=myapp标签资源的节点,则Pod状态应该是Pending。

注意:要考虑到所有存在此标签的Pod,预期结果不同可能是因为当前名称空间已经存在app=myapp标签的Pod。

软亲和性

类似,不多介绍。

2、Pod反亲和性podAntiAffinity

[root@master ~]# kubectl explain pod.spec.affinity.podAntiAffinity
preferredDuringSchedulingIgnoredDuringExecution:软亲和性
requiredDuringSchedulingIgnoredDuringExecution:硬亲和性

资源清单定义类似于podAffinity。

硬亲和性

 

这样展现出来的效果不明显,如果只有一个节点可用的话,应该是第二个Pod会Pending。

再次实验:

[root@master ~]# kubectl label nodes node1 zone=foo
[root@master ~]# kubectl label nodes node2 zone=foo 
# 给node1,node2打标签,这样node1,node2以此为标签属于一组相近节点,创建Pod时以zone为topologyKey。

与预期结果相符,调度失败提示也很明显。

软亲和性

类似,不过多实验。

四、污点调度

给节点选择了让哪些Pod运行的权利。

污点(taint)定义在节点上。

Tolerations:定义在pod上,定义容忍度。

[root@master ~]# kubectl get node node1 -o yaml
[root@master ~]# kubectl explain node.spec.taints

在Pod上定义容忍度有两种方式

[root@master schedule]# kubectl explain deployment.spec.template.spec.tolerations.operator
等值比较Equal: key,value,effect完全匹配
存在性判断Exists:key,effect完全匹配,value可以为空,不关注。

逐一匹配节点上的污点是否被容忍,只要有一个污点没有被容忍,并且effect为NoSchedule,则一定不能被调度过来。

[root@master ~]# kubectl describe node master  #一般pod不被调度到master的原因是Master上存在污点。

1、node.spec.taints.effect

taints上的effect定义对Pod的排斥效果:

NoSchedule:仅影响调度过程,对现存pod不产生影响,不能容忍就不能调度过来。

PreferNoSchedule:仅影响调度过程,不能容忍就不能调度过来,但是实在没办法也是能调度过来的。对节点新加了污点,那么对节点上现存的pod没有影响。

NoExecute:既影响调度过程,也影响现存的Pod对象。不容忍污点的Pod对象将被驱逐。

2、定义污点及容忍度

定义污点:

[root@master ~]# kubectl taint --help
[root@master schedule]# kubectl taint node node1 node-type=production:NoSchedule #给node1打上污点

所有的pod都被调度到了node2,因为pod不能容忍污点。在node2上也打上污点,并且effect为NoExecute,则已调度的Pod会被驱逐,然后因为所有节点上都有pod容忍不了的污点,pod就pending了。效果如下:

[root@master schedule]# kubectl taint node node2 node-type=dev:NoExecute

 

定义容忍度:

[root@master schedule]# kubectl explain pod.spec.tolerations
[root@master schedule]# kubectl explain deployment.spec.template.spec.tolerations

在Pod上定义容忍度(容忍node-type=production:NoSchedule)后,之前Pending的pod被调度到了node1上,符合预期。

修改资源清单,定义容忍node-type的所有effect,则pod能容忍node1,node2上的node-type污点,不管effect是什么。

注意:

       容忍度定义,等值比较(Equal)必须key,value,effect三者完全相匹配;如果effect,key定义为空,代表匹配所有的effect和key

       修改了资源清单的容忍度,Pod会被重新调度。

[root@master ~]# kubectl describe nodes node1 |grep -E '(Roles|Taints)'  #查看节点上的污点
[root@master ~]# kubectl taint node node1 node-type-    #删除节点上的污点
[root@master ~]# kubectl taint node node2 node-type-

五、Tips:

Protocol Buffer:组件之间内部对象序列化格式。

Json:资源对象序列化格式。

键值数据对:

       Labels:标签

       Annotations:注解

       Taints:污点,用在节点上的。

原文地址:https://www.cnblogs.com/cmxu/p/12255993.html