一、kubernetes调度器
调度器(scheduler)是当创建Pod对象时,负责为每一个未经调度的Pod资源基于一系列的规则集从集群中挑选一个合适的节点来运行该Pod。其核心目标是基于资源可用性将各Pod资源公平地分布于集群节点之上。
Kubernetes平台提供的默认调度器称为“通用调度器”,它通过节点预选(Prediicate)、节点优先级排序(Priority)及节点择优(Selete)三个步骤完成调度操作。
1、常用的预选策略
预选策略即节点的过滤器,用于排除掉不适用的节点。预选后如果不存在任何一个满足条件的节点则Pod被置于Pending状态,直到至少有一个节点可用位置。常用的预选策略如下:
(1)CheckNodeCondition:检查是否可以在节点报告磁盘、网络不可用或未准备好的情况下将Pod对象调度于其上。
(2)HostName:若Pod对象拥有spec.hostname属性,则检查节点名称与此属性值是否匹配
(3)PodFitsHostPorts:若Pod容器定义了ports.hostPort属性,则检查其值指定的端口是否已被节点上的其他容器或服务占用。
(4)MatchNodeSelector:若Pod对象定义了spec.nodeSelector属性,则检查节点标签是否能匹配此属性值。
(5)NoDiskConfict:检查Pod对象请求的存储卷在此节点是否可用
(6)PodFitsResources:检查节点是否有足够的资源满足Pod对象的运行需求。而那些在注解中标记为关键性(critical)的Pod资源不受此预选策略影响。
(7)PodToleratesNodeTaints:若Pod对象定义了spec.tolerations属性,则检查其值是否能够节点定义的污点(taints)
(8)PodToleratesNodeNoExecuteTaints:若Pod对象定义了spec.tolerations属性,则检查其值是否能够接纳节点定义的NoExecute类型的污点。
(9)CheckNodeLabelPresence:仅检查节点上指定的所有标签的存在性,要检查的标签以及其可否存在取决于用户的定义。
(10)CheckServiceAffinity:根据当前Pod对象所属的Service已有的其他Pod对象所运行的节点进行调度,其目的在于将相同的Service的Pod对象放置在同一个或同一类节点上以提高运行效率。
(11)MaxEBSVolumeCount:检查节点上已挂载的EBS存储卷的数量是否超过了设置的最大值,默认值为39
(12)MaxGCEPDVolumeCount:检查节点上已挂载的GCE PD存储卷的数量是否超过了设置的最大值,默认值为16
(13)MaxAzureDiskVolumeCount:检查节点上已挂载的AzureDisk存储卷的数量是否超过了设置的最大值,默认值为16
(14)CheckVolumeBinding:检查节点上已绑定的和未绑定的PVC是否能够满足Pod对象存储卷需求
(15)NoVolumeZoneConflict:在给定了区域(zone)限制的前提下,检查此节点部署Pod对象是否存在存储卷冲突。
(16)CheckNodeMemoryPressure:若给定的节点已经报告了存在内存资源压力过大的状态,则检查当前Pod对象是否可调度至此节点之上。
(17)CheckNodePIDPressure:若给定的节点已经报告了存在PID资源压力过大的状态,则检查当前Pod对象是否可调度至此节点之上。
(18)CheckNodeDiskPressure:若给定的节点已经报告了存在磁盘资源压力过大的状态,则检查当前Pod对象是否可调度至此节点之上。
(19)MatchInterPodAffinity:检查给定节点是否能够满足Pod对象的亲和性或反亲和性条件,以实现Pod亲和性调度或反亲和性调度。
2、常用的优选函数
在优选的过程中,调度器向每个通过预选的节点传递一系列的优选函数来计算优先级的分值。优先级的分值介于0到10之间。同时,调度器还支持为每个优选函数指定一个简单的由正数值标识的权重;进行节点优先级分值计算时,它首先将每个优选函数的计算得分乘以其权重,然后将所有优选函数的得分相加从而得出节点的最终优先级分值。主要的优选函数有:
(1)LeastRequestdPriority:由节点空闲资源与节点总容量的比值计算而来
(2)BalancedResourceAllocation:以CPU和内存资源占用率的相近程序作为评估标准,越接近的节点权重越高。该函数要与LeastRequestedPriority组合使用来平衡优化节点资源的使用状况。
(3)NodePreferAvoidPodPriority:默认权限为10000,它将根据节点是否设置了注解信息”scheduler.aplha.kubernetes.io/preferAvodPods”来计算其优先级。
(4)NodeAffinityPriority:基于节点亲和性调度偏好进行优先级评估,它将根据Pod资源中的nodeSelector对给定节点进行匹配度检查。
(5)TainTolerationPriority:基于Pod资源对节点的污点容忍度偏好进行其优先级的评估,将Pod对象的tolerations列表与节点的污点进行匹配度检查,成功匹配的条目越多,则节点得分越低。
(6)SelectorSpreadPrity:此优选函数会尽量将同一标签选择器匹配到的Pod资源分散到不同的节点上运行。
(7)InterPodAffinityPriority:遍历Pod对象的亲和性条目,并将那些能够匹配到给定节点的条目的权重相加,结果值越大的节点得分越高
(8)MostRequestedPriority:与优选函数LeastRequestedPriority的评估节点得分的方法相似,资源占用比例越大的节点得分越高。
(9)NodeLabelPriority:根据节点是否拥有特定的标签来评估其得分
(10)ImageLocalityPriority:基于给定节点上拥有的运行当前Pod对象中的容器所依赖到的镜像文件来计算节点得分。不具有Pod依赖到的任何镜像文件的节点其得分为0,而拥有相应镜像文件的各节点中,所拥有的被依赖到的镜像文件其体积之和越大则节点得分越高。
二、节点亲和调度
节点亲和性是调度程序用来确定Pod对象调度位置的一组规则,这组规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。
节点亲和性规则有两种。一是硬亲和性,实现强执行规则,二是软亲和性,实现柔性调度规则。定义节点亲和规则的关键点有两个,一是为节点配置合乎需求的标签,另一个是为Pod对象定义合理的标签选择器。
1、节点硬亲和性
节点的亲和性可以通过pod.spec.affinity.nodeAffinity字段定义,nodeAffinity字段中支持使用matchExpressions属性构建更为复杂的标签选择器。
# 定义一个pod资源清单,将该pod调度至有标签为zone=foo的节点上
apiVersion: v1
kind: Pod
metadata:
name: required-nodeaffinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
containers:
- name: busybox-test
image: busybox
# 创建并查看该pod资源,由于选择不到合适的节点所以该pod一直处于pending状态
]# kubectl apply -f nodeAffinity-test01.yaml
]# kubectl get pods with-requiredth-nodeaffinity
NAME READY STATUS RESTARTS AGE
with-requiredth-nodeaffinity 0/1 Pending 0 89s
# 当为其中一个节点打上标签zone=foo后,该pod会选择该节点并运行起来
]# kubectl label node node02.dayi123.com zone=foo
]# kubectl get pods with-requiredth-nodeaffinity
NAME READY STATUS RESTARTS AGE
with-requiredth-nodeaffinity 1/1 Running 0 24s
定义节点亲和性时,requiredDuringSchedulingIgnoredDuringExecution字段的值是一个对象列表,用于定义节点的硬亲和性,它可由一个到多个nodeSelectorTerm定义的对象组成,多个对象直接为或的关系。nodeSelectorTerm用于定义节点选择器条目,值为对象列表,可由一个或多个matchExpressions对象定义的匹配规则组成,多个规则之间为逻辑与的关系。matchExmpressions下的多个标签选择器之间的也为逻辑与的关系。
标签选择器表达式中支持使用的操作符有In,NotIn,Exists,DoesNotExists,Lt,Gt等。
2、节点软亲和性
节点软亲和性为节点选择提供了一种柔性的控制器逻辑,当条件不满足时,也能够接受被编排与其他不符合条件的节点之上。同时他还为每种倾向性提供了weight属性以便用于自定义优先级,范围是1-100,越大越优。
# 给节点node打标签
]# kubectl label node node01.dayi123.com server=nginx
# 定义一个基于deployment控制器的软亲和度资源配置清单
]# cat deploy-preferred-nodeAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-with-node-affinity
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp-pod
labels:
app: myapp
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 60
preference:
matchExpressions:
- {key: server, operator: In, values: ["nginx"]}
- weight: 30
preference:
matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
containers:
- name: nginx
image: nginx:1.12
三、Pod资源亲和调度
出于某些需求,将一些Pod对象组织在相近的位置(同一节点、机架、区域等),此时这些这些pod对象间的关系为亲和性;而出于安全或分布式等原因将一些Pod对象在其运行的位置上隔离开,此时这些pod对象的关系称之为反亲和性。
Pod的亲和性调度也存在硬亲和性和软亲和性的区别,他们表示的约束意义同节点亲和性相似。Kubernetes调度器通过内建的MatchInterPodAffinity预选策略为这种调度方式完成节点的预选并基于InterPodAffinityPriority优选函数进行各节点的优选级评估。
在定义Pod对象的亲和性和反亲和性时,需要借助于标签选择器来选择被依赖的Pod对象,并根据选出的Pod对象所在的节点的标签来判定“同一位置”的具体意义。
1、Pod资源的硬亲和性调度
Pod硬亲和性调度也使用requiredDuringSchedulingIgnoreDuringExecution属性进行定义。
# 先创建一个运行tomcat的pod,并打一个标签为server=tomcat
]# kubectl run tomcat -l server=tomcat --image=tomcat:latest
# 定义一个硬亲和性调度的pod
]# cat podaffinity-test01.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: server, operator: In, values: ["tomcat"]}
topologyKey: kubernetes.io/hostname
containers:
- name: server-nginx
image: nginx:1.12
# 创建后查看是两个pod会被调度至同一个node上
]# kubectl get pods tomcat-65865b6bd-2fpkj pod-affinity -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-65865b6bd-2fpkj 1/1 Running 0 35m 10.244.2.51 node02.dayi123.com <none> <none>
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity 1/1 Running 0 3m27s 10.244.2.52 node02.dayi123.com <none> <none>
2、Pod的软亲和性调度
Pod软亲和调度使用方法与Node软亲和调度方法类似。
]# cat deploy-preferred-podaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity
spec:
replicas: 3
selector:
matchLabels:
app: myserver
template:
metadata:
name: myserver
labels:
app: myserver
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["tomcat"]}
topologyKey: zone
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: zone
containers:
- name: nginx-test
image: nginx:1.12
3、Pod的反亲和性调度
Pod的反亲和性调度通过spec.affinity.podAntiAffinity字段定义,反亲和性调度用于分散同一类应用的Pod对象,也用于将不同安全级别的Pod对象调度至不同的区域或节点。Pod的反亲和性调度也支持硬性和柔性调度机制,用法同pod的硬性柔性调度机制类似。
四、污点和容忍度
污点:定义在节点之上的键值性属性数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳节点污点的容忍度
容忍度:定义在Pod对象上的键值型的属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod对象调度至其能够容忍该节点污点的节点之上。
K8s系统使用PodToleratesNodeTaints预选策略和TainTolerationPriority优选函数来完成此种类型的高级调度机制。
1、定义污点和容忍度
污点信息在节点的node.Spec字段中定义,容忍度信息在pod的pod.spec字段中定义,他们都是键值型数据,但都额外支持一个效果(effect)标记,语法格式为“key=value:effect”。Effect主要用于定义对Pod对象的排斥等级,主要的类型如下:
(1)NoSchedule:不能容忍此污点的Pod对象不可调度至当前节点,属于强制约束性的关系,对节点现存Pod对象不受影响。
(2)PreferNoSchedule:NoSchedule的柔性约束版本,不能容忍此污点的Pod对象尽量不要调度至当前节点
(3)NoExecute:不能容忍此污点的新Pod对象不可调度至当前节点,属强制型约束关系,而且节点上现存的Pod对象因节点污点变动或Pod容忍度变动而不再满足匹配规则时Pod对象将被驱逐。
在Pod对象上定义容忍度时,支持两种操作符:一种是等值比较(Equal),表示容忍度与污点必须在key、value、和effect三者之上完全匹配;二是存在性的判断(Exists),表示二者的key和effect必须完全匹配,容忍度中的value值要使用空值。
使用kubeadm部署的kubernetes集群,其master节点将自动添加污点信息以阻止不能容忍此污点的Pod对象调度至此节点。而一些系统应用在创建时就添加了相应的容忍度以确保被DaemonSet控制器创建时能够调度至Master节点运行一个实例。
# 查看master节点的污点
]# kubectl get nodes master01.dayi123.com -o yaml
. . . . . .
spec:
podCIDR: 10.244.0.0/24
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
. . . . . .
# 查看系统及应用的容忍度
]# kubectl get pods coredns-86c58d9df4-jnhz4 -n kube-system -o yaml
. . . . . .
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/master
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
. . . . . .
2、管理节点的污点
节点污点的定义可以使用字母、数字、连接符、点号、和下划线且只能以字母或数字开头,键名的上限为253各字符,值上限为63个字符,向节点添加污点可通过“kubectl taint”名令添加。语法格式如下:
kubectl taint nodes <node-name> <key>=[:<effect>]
对node1节点添加污点并查看污点信息:
]# kubectl taint nodes node01.dayi123.com node-type=production:NoSchedule
]# kubectl get nodes node01.dayi123.com -o go-template={{.spec.taints}}
[map[effect:NoSchedule key:node-type value:production]]
删除污点的语法格式如下:
kubectl taint nodes <node-name> <key>=[:<effect>]-
删除污点的具体用法如下:
# 删除node1节点上node-type的键效用标识为”Noschedule”的污点信息
]# kubectl taint nodes node01.dayi123.com node-type:NoSchedule-
# 删除指定键名的所有污点,只需在删除命令中省略效率标识即能实现
]# kubectl taint nodes node01.dayi123.com noed-type-
# 删除节点上的全部污点信息,通过kubectl patch命令将节点属性spec.taints的值直接置空即可。
]# kubectl patch nodes nodes01.dayi123.com -p '{"spec":{"taints":[]}}'
3、Pod对象的容忍度
Pod对象的容忍度可通过其spec.tolerations字段进行添加,根据使用的操作符不同,主要由两种可用的形式,一种是与污点信息完全匹配的等值关系Equal;另一种是判断污点信息存在性的匹配方式Exists。
# 给node1节点定义一个污点
]# kubectl taint nodes node01.dayi123.com node-type=production:NoExecute
# 定义Pod资源清单并定义该污点容忍度
]# cat pod-tolerations.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-toler
spec:
tolerations:
- key: node-type
operator: "Equal"
value: "production"
effect: "NoExecute"
tolerationSeconds: 3600
containers:
- name: server-nginx
image: nginx:1.12
4、问题节点的标识
问题节点的标识是通过节点控制器在特定条件下自动为节点添加污点信息实现,它们都使用NoExecute效用标识。