目录
API Server在接受客户端提交Pod对象创建请求后,然后是通过调度器(kube-schedule)从集群中选择一个可用的最佳节点来创建并运行Pod。而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点。如图:
当我们有需求要将某些Pod资源运行在特定的节点上时,我们可以通过组合节点标签,以及Pod标签或标签选择器来匹配特定的预选策略并完成调度,如MatchInterPodAfinity、MatchNodeSelector、PodToleratesNodeTaints等预选策略,这些策略常用于为用户提供自定义Pod亲和性或反亲和性、节点亲和性以及基于污点及容忍度的调度机制。
预选策略实际上就是节点过滤器,例如节点标签必须能够匹配到Pod资源的标签选择器(MatchNodeSelector实现的规则),以及Pod容器的资源请求量不能大于节点上剩余的可分配资源(PodFitsResource规则)等等。执行预选操作,调度器会逐一根据规则进行筛选,如果预选没能选定一个合适的节点,此时Pod会一直处于Pending状态,直到有一个可用节点完成调度。其常用的预选策略如下:
CheckNodeLabelPressure和CheckServiceAffinity可以在预选过程中结合用户自定义调度逻辑,这些策略叫做可配置策略。其他不接受参数进行自定义配置的称为静态策略。
预选策略筛选出一个节点列表就会进入优选阶段,在这个过程调度器会向每个通过预选的节点传递一系列的优选函数来计算其优先级分值,优先级分值介于0-10之间,其中0表示不适用,10表示最适合托管该Pod对象。
另外,调度器还支持给每个优选函数指定一个简单的值,表示权重,进行节点优先级分值计算时,它首先将每个优选函数的计算得分乘以权重,然后再将所有优选函数的得分相加,从而得出节点的最终优先级分值。权重可以让管理员定义优选函数倾向性的能力,
选择: 根据以上筛选 排序 在多个分数相同的节点选择一个
节点亲和性是用来确定Pod对象调度到哪一个节点的规则,这些规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。
定义节点亲和性规则有2种:硬亲和性(require)和软亲和性(preferred)
需要注意的是preferredDuringSchedulingIgnoredDuringExecution和requiredDuringSchedulingIgnoredDuringExecution名字中后半段字符串IgnoredDuringExecution表示的是,在Pod资源基于节点亲和性规则调度到某个节点之后,如果节点的标签发生了改变,调度器不会讲Pod对象从该节点上移除,因为该规则仅对新建的Pod对象有效。
[root@master manifests]# mkdir schedule
[root@master manifests]# cd schedule/
[root@master schedule]# vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
nodeSelector:
disktype: ssd
[root@master schedule]# kubectl apply -f pod-demo.yaml
pod/pod-demo created
~
查看
[root@master schedule]# kubectl apply -f pod-demo.yaml
pod/pod-demo created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-demo 1/1 Running 0 2m20s 10.244.1.2 node01 <none> <none>
[root@master schedule]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 12d v1.13.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master=
node01 Ready <none> 12d v1.13.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node01
node02 Ready <none> 12d v1.13.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node02
由于集群中只有node01存在disktype标签,所以pod会被部署到node01上,如果所有的node都没有nodeSelector中指定的标签会怎样呢
[root@master schedule]# kubectl delete -f pod-demo.yaml
pod "pod-demo" deleted
[root@master schedule]# vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
nodeSelector:
disktype: hdd
[root@master schedule]# kubectl apply -f pod-demo.yaml
pod/pod-demo created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-demo 0/1 Pending 0 4s <none> <none> <none> <none>
此时pod处于pending状态,可知nodeSelector是一种强制约束,预选阶段就无法通过,
此时为node02打标签 disktype: hdd
[root@master schedule]# kubectl label nodes node02 disktype=hdd
node/node02 labeled
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-demo 1/1 Running 0 2m14s 10.244.2.5 node02 <none> <none>
查看可知,node02打上标签后,满足pod的创建要求,pod-demo就会立刻被创建到node02上。
[root@master ~]# kubectl explain pods.spec.affinity.nodeAffinity.
preferredDuringSchedulingIgnoredDuringExecution #软亲和性,优先选择满足要求的节点,不满足也可以运行
requiredDuringSchedulingIgnoredDuringExecution #硬亲和性,必须满足
requiredDuringSchedulingIgnoredDuringExecution硬亲和
[root@master schedule]# vim pod-nodeaffinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
执行
[root@master schedule]# kubectl apply -f pod-nodeaffinity-demo.yaml
pod/pod-node-affinity-demo created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-node-affinity-demo 0/1 Pending 0 9s <none> <none> <none> <none>
由于集群中node都没有zoo为foo或bar的标签,所以挂起.
preferredDuringSchedulingIgnoredDuringExecution软亲和
[root@master schedule]# vim pod-nodeaffinity-demo-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo-2
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #修改为软亲和性
- preference:
matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
weight: 60
执行并查看
[root@master schedule]# kubectl apply -f pod-nodeaffinity-demo-2.yaml
pod/pod-node-affinity-demo-2 created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-node-affinity-demo-2 1/1 Running 0 10s 10.244.1.3 node01 <none> <none>
查看可知,虽然没有节点能满足亲和性要求,但是由于是配置的软亲和性,pod也正常运行。如果给node01或者node02打标签满足nodeAffinity配置,node会比较倾向于运行在该node节点,但是如果不满足也不会pending挂起。
在出于高效通信的需求,有时需要将一些Pod调度到相近甚至是同一区域位置(比如同一节点、机房、区域)等等,比如业务的前端Pod和后端Pod,此时这些Pod对象之间的关系可以叫做亲和性。
同时出于安全性的考虑,也会把一些Pod之间进行隔离,此时这些Pod对象之间的关系叫做反亲和性(anti-affinity)。
调度器把第一个Pod放到任意位置,然后和该Pod有亲和或反亲和关系的Pod根据该动态完成位置编排,这就是Pod亲和性和反亲和性调度的作用。Pod的亲和性定义也存在硬亲和性和软亲和性的区别,其约束的意义和节点亲和性类似。
Pod的亲和性调度要求各相关的Pod对象运行在同一位置,而反亲和性则要求它们不能运行在同一位置。这里的位置实际上取决于节点的位置拓扑,拓扑的方式不同,Pod是否在同一位置的判定结果也会有所不同。
如果基于各个节点的kubernetes.io/hostname 标签作为评判标准,那么会根据节点的hostname去判定是否在同一位置区域。
[root@master ~]# kubectl explain pods.spec.affinity.podAntiAffinity.
preferredDuringSchedulingIgnoredDuringExecution #软亲和性
requiredDuringSchedulingIgnoredDuringExecution #硬亲和性
requiredDuringSchedulingIgnoredDuringExecution硬亲和
[root@master schedule]# vim pod-reqyest-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
namespace: default
labels:
app: db
tier: db
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 360000"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]} #匹配规则
topologyKey: kubernetes.io/hostname #判断依据,每个节点都有一个kubernetes.io/hostname标签,此处判断只要kubernetes.io/hostname是一个就表示在一个节点上
执行
[root@master schedule]# kubectl apply -f pod-request-affinity-demo.yaml
pod/pod-first created
pod/pod-second created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 2m53s 10.244.2.6 node02 <none> <none>
pod-second 1/1 Running 0 74s 10.244.2.7 node02 <none> <none>
于单一节点的Pod亲和性相对来说使用的情况会比较少,通常使用的是基于同一地区、区域、机架等拓扑位置约束。比如部署应用程序(myapp)和数据库(db)服务相关的Pod时,这两种Pod应该部署在同一区域上,可以加速通信的速度。
preferredDuringSchedulingIgnoredDuringExecuttion软亲和
调度器会尽力满足亲和约束的调度,在满足不了约束条件时,也允许将该Pod调度到其他节点上运行。
[root@master schedule]# vim pod-preferrd-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
namespace: default
labels:
app: myapp
tier: frontend
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
namespace: default
labels:
app: db
tier: db
annotations:
white.con/created-by: "cluster admin"
spec:
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 360000"]
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 60
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In , values: ["cache"]}
topologyKey: kubernetes.io/hostname
运行
[root@master schedule]# kubectl apply -f pod-preferrd-affinity-demo.yaml
pod/pod-first created
pod/pod-second created
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 6m27s 10.244.1.4 node01 <none> <none>
pod-second 1/1 Running 0 2m59s 10.244.2.8 node02 <none> <none>
pod的软亲和调度需要将Pod调度到标签为app=cache并在区域zone当中,或者调度到app=db标签节点上的,但是我们的节点上并没有类似的标签,所以调度器会根据软亲和调度进行随机调度到node02节点之上。
podAffinity定义了Pod对象的亲和约束,而Pod对象的反亲和调度则是用podAntiAffinty属性进行定义,下面的配置清单中定义了由同一Deployment创建但是彼此基于节点位置互斥的Pod对象:
[root@master schedule]# vim pod-request-antiaffinity-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-with-pod-anti-affinity
spec:
replicas: 4
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp
labels:
app: myapp
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app,operator: In,values: ["myapp"]}
topologyKey: kubernetes.io/hostname
containers:
- name: myapp
image: ikubernetes/myapp:v1
执行
[root@master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-with-pod-anti-affinity-68dc795b5-774st 0/1 Pending 0 5s <none> <none> <none> <none>
myapp-with-pod-anti-affinity-68dc795b5-7w57m 0/1 Pending 0 5s <none> <none> <none> <none>
myapp-with-pod-anti-affinity-68dc795b5-nlx2m 1/1 Running 0 5s 10.244.1.6 node01 <none> <none>
myapp-with-pod-anti-affinity-68dc795b5-tqj69 1/1 Running 0 5s 10.244.2.10 node02 <none> <none>
由于在配置清单中定义了强制性反亲和性,所以创建的4个Pod副本必须 运行在不同的节点当中呢,但是集群中只存在32个节点,因此,肯定会有2个Pod对象处于Pending的状态。
污点(taints)是定义在节点上的一组键值型属性数据,用来让节点拒绝将Pod调度到该节点上,除非该Pod对象具有容纳节点污点的容忍度。
容忍度(tolerations)是定义在Pod对象上的键值型数据,用来配置让Pod对象可以容忍节点的污点。
节点选择器和节点亲和性的调度方式都是通过在Pod对象上添加标签选择器来完成对特定类型节点标签的匹配,实现的是Pod选择节点的方式。而污点和容忍度则是通过对节点添加污点信息来控制Pod对象的调度结果,让节点拥有了控制哪种Pod对象可以调度到该节点上的 一种方式。
Kubernetes使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数来完成这种调度方式。
污点的定义是在节点的nodeSpec,而容忍度的定义是在Pod中的podSpec,都属于键值型数据,两种方式都支持一个effect标记,语法格式为key=value: effect,其中key和value的用户和格式和资源注解类似,而effect是用来定义对Pod对象的排斥等级,主要包含以下3种类型:
在Pod对象上定义容忍度时,其支持2中操作符:Equal和Exists
在使用kubeadm部署的集群中,master节点上将会自动添加污点信息,阻止不能容忍该污点的Pod对象调度到该节点上,如下:
[root@master ~]# kubectl describe nodes master
......
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false
......
统级别的应用在创建时,就会添加相应的容忍度来确保被创建时可以调度到master节点上,如flannel插件:
[root@master ~]# kubectl describe pods -n kube-system kube-flannel-ds-amd64-5zrk7
......
Tolerations: :NoSchedule
node.kubernetes.io/disk-pressure:NoSchedule
node.kubernetes.io/memory-pressure:NoSchedule
node.kubernetes.io/network-unavailable:NoSchedule
node.kubernetes.io/not-ready:NoExecute
node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/unschedulable:NoSchedule
......
使用命令行向节点添加污点
Usage:
kubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options]
#定义node01上的污点
[root@master ~]# kubectl taint nodes node01 node-type=production:NoSchedule
[root@master ~]# kubectl get nodes node01 -o go-template={{.spec.taints}}
[map[key:node-type value:production effect:NoSchedule]]
此时,node01节点上已经存在的Pod对象不受影响,仅对新建Pod对象有影响,需要注意的是,如果是同一个键值数据,但是最后的标识不同,也是属于不同的污点信息,比如再给node01上添加一个污点的标识为:PreferNoSchedule
[root@master ~]# kubectl taint nodes node01 node-type=production:PreferNoSchedule
node/node01 tainted
[root@master ~]# kubectl get nodes node01 -o go-template={{.spec.taints}}
[map[effect:PreferNoSchedule key:node-type value:production] map[effect:NoSchedule key:node-type value:production]]
删除污点
#删除node01上的node-type标识为NoSchedule的污点
[root@master ~]# kubectl taint nodes node01 node-type:NoSchedule-
node/node01 untainted
#删除指定键名的所有污点
[root@master ~]# kubectl taint nodes node01 node-type-
node/node01 untainted
#补丁方式删除节点上的全部污点信息
[root@master ~]# kubectl patch nodes node01 -p '{"spec":{"taints":[]}}'
Pod对象的容忍度可以通过spec.tolerations字段进行添加,同一的也有两种操作符:Equal和Exists方式。
Equal等值方式如下:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "Noexecute"
tolerationSeconds: 3600
Exists方式如下:
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 3600
https://www.cnblogs.com/linuxk
马永亮. Kubernetes进阶实战 (云计算与虚拟化技术丛书)
Kubernetes-handbook-jimmysong-20181218
原文:https://www.cnblogs.com/wlbl/p/10694381.html