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
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:污点,用在节点上的。