在生产环境中,经常会有这样的需求:
面对这样的需求,Kubernetes 抽象了污点(Taint)和容忍(Toleration)的概念,可以非常方便的实现这些需求。
比如有一批 GPU 服务器只能部署要使用 GPU 的 Pod。每个节点上都可以应用一个或多个 Taint,这表示对于那些不能容忍这些 Taint 的 Pod 是不能部署在该服务器上的。如果 Pod 配置了 Toleration,则表示这些 Pod 可以被调度到设置了 Taint 的节点上,当然没有设置 Taint 的节点也是可以部署的。
Taint(污点)和 Toleration(容忍)可以作用于 node 和 pod 上,其目的是优化 pod 在集群间的调度,这跟节点亲和性类似,只不过它们作用的方式相反,具有 taint 的 node 和 pod 是互斥关系,而具有节点亲和性关系的 node 和 pod 是相吸的。另外还有可以给 node 节点设置 label,通过给 pod 设置 nodeSelector 将 pod 调度到具有匹配标签的节点上。
Taint 和 Toleration 相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint,这表示对于那些不能容忍这些 taint 的 Pod,是不会被该节点接受的。如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以(但不一定)被调度到具有匹配 taint 的节点上。
如果一个节点被标记为有污点,那么意味着不允许 pod 调度到该节点,除非 pod 也被标记为可以容忍污点节点。
在使用 kubeadm 部署的 k8s 集群的时候应该会发现,通常情况下,应用是不会调度到 master 节点的。因为 kubeadm 部署的 k8s 集群默认给 master 节点加了 Taints(污点)。
给节点 node01 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点
[root@k8s-master ~]#kubectl taint nodes k8s-node01 key1=value1:NoSchedule
移除上述命令所添加的污点,在后面加一个横杠即可,可以执行:
[root@k8s-master~]#kubectl taint nodes k8s-node01 key1=value1:NoSchedule-
每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用效果,语法为key=value:effect
当前 taint effect 支持如下三个选项:
备注:
NoExecute 这个 Taint 效果对节点上正在运行的 pod 有以下影响:
tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间,即 60 秒后该 pod 会被驱逐掉,默认的是一天。
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍(Toleration),意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。
[root@k8s-master~]#kubectl taint node k8s-node01 check=mycheck: NoExecute
[root@k8s-master ~]#kubect1 taint node k8s-node02 check=mycheck:NoExecute
[root@k8s-master~]#vim pod1.yam1
apiVersion:V1
kind: Pod
metadata:
name: myapp01
labels:
app:myapp01
spec:
containers:
name:with-node-affinity
image: nginx:1.7.9
[root@k8s-master ~]#kubectl apply -f pod1.yam1
[root@k8s-master~]#kubectl get pods -o wide
[root@k8s-master~]#kubectl delete-fpod1.yam1
在 pod1.yam1 的基础上增加容忍度参数
[root@k8s-master ~]#vim pod2.yam1
apiVersion: v1
kind:Pod
metadata :
name: myapp01
1abels:
app:myapp01
spec:
containers:
name: with-node-affinity
image:nginx:1.7.9
tolerations:
key:check
operator:Equal
value:mycheck
effect: NoExecute
tolerationSeconds:60
备注:
tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间,即 60 秒后该 pod 会被驱逐掉,默认的是一天。其中的 key、vaule、effect 都要与 Node 上设置的 taint 保持一致。
[root@k8s-master ~]#kubectl apply -f pod2.yam1
[root@k8s-master ~]#kubectl get pods-owide
注意:
完成此实验后,将污点取消
[root@k8s-master ~]#kubect1 taint node k8s-node1 check=mycheck: NoExecute-
[root@k8s-master ~]#kubect1 taint node k8s-node02 check=mycheck:NoExecute-
pod 的 Toleration 声明中的 key 和 effect 需要与 Taint 的设置保持一致,并且满足以下条件之一:
#不完全匹配,operator的值为Exists,这时无需指定value
tolerations:
- key:check
operator:"Exists"
effect:NoExecute
#完全匹配,operator的值为Equal并且value相等(可以设置多个tolerations)
tolerations:
- key:check
operator:Equal
value:mycheck
effect: NoExecute
#如果不指定operator,则默认值为Equa1
tolerations :
- key:check
value:mycheck
effect:NoExecute
#空的key、value、effect配合Exists操作符能够匹配所有的键和值,即能容忍所有污点。
tolerations:
operator:"Exists"
#空的effect匹配所有的effect,即能容忍任何key名为check的污点。
tolerations :
- key:check
operator:"Exists"
#有多个Master存在时,为了防止资源浪费,可以将master节点设置为PreferNoSchedule污点,让 Pod可在Master上临时创建:
kubectl taint node k8s-master node-role.kubernetes.io/master=:PreferNoSchedu1e
#如果某个Node更新升级系统组件,为了防止业务长时间中断,可以先在该Node设置NoExecute污 点,把该Node上的Pod都驱逐出去
kubectl taint node k8s-node01 check=mycheck:NoExecute
#此时如果别的Node资源不够用,可临时给Master设置PreferNoSchedule污点,让Pod可在 Master上临时创建
kubectltaint node k8s-master node-role.kubernetes.io/master=:PreferNoSchedule
#待所有Node的更新操作都完成后,再去除污点
kubectl taint node node01 check=mycheck:NoExecute-
系统允许在同一个 node 上设置多个 taint,也可以在 pod 上设置多个 Toleration
Kubernetes 调度器处理多个 Taint 和 Toleration 能够匹配的部分,剩下的没有忽略掉的 Taint 就是对 Pod 的效果了。下面是几种特殊情况:
kubectl taint nodes k8s-node01 key1=value1:NoSchedule
kubectl taint nodes k8s-node01 key1=value1:NoExecute
kubect1 taint nodes k8s-node01 key2=value2:NoSchedule
去除污点:
kubectl taint nodes k8s-node01 key1=value1:NoSchedule-
kubect1 taint nodes k8s-node01 key1=value1:NoExecute-
kubect1 taint nodes k8s-node01 key2=value2:NoSchedule-
警戒是指将 Node 标记为不可调度的状态,这样就不会让新创建的 Pod 在此 Node 上运行,命令如下:
[root@k8s-master ~]#kubect1 cordon k8s-node01
该 node 将会变为 SchedulingDisabled 状态:
[root@k8s-master ~]#kubect1 get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master 4d12h v1.23.0
k8s-node01 Ready, SchedulingDisabled 4d12hv1.23.0
k8s-node02 Ready 4d12h v1.23.0
[root@k8s-master ~]#kubect1 uncordon k8s-node01
node/k8s-node01 uncordoned
[root@k8s-master ~]# kubect1 get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master 4d12h v1.23.0
k8s-node01 Ready 4d12h v1.23.0
k8s-node02 Ready 4d12h v1.23.0
kubect1 drain 可以让 Node 节点开始释放所有 pod,并且不接收新的 pod 进程。drain 本意排水,意思是将出问题的 Node 下的 Pod 转移到其它 Node 下运行。
[root@k8s-master~]#kubectl drain k8s-node01\
--ignore-daemonsets\
-delete-1ocal-data\
--force
执行 drain 命令,会自动做了两件事情:
备注:
注意:
实验完成后,将节点设置为可调度
kubect1 uncordon k8s-node01
关于 K8S 对 Pod 的调度,通常情况下 Pod 被分配到哪些 Node 是不需要我们操心的,这个过程会由 scheduler 自动实现。但有时,我们需要让 Pod 按照我们的预想运行在 Node 上(例如某些应用 “必须 / 或者尽量” 跑在具有 SSD 存储的节点上,有些彼此相关的 Pod 应用应该跑在同一个节点上)。为此,k8s 为我们提供了这样的策略,我们可以通过使用 “亲和性 / 非亲和性” 制定一些规则来实现我们的需求。
requiredDuringSchedulingIgnoredDuringExecution
。preferredDuringSchedulinggnoredDuringExecution
。分为 Node 亲和性、Pod 亲和性、Pod 反亲和性:
节点的亲和性可以通过pod.spec.affinity.nodeAffinity
字段定义,nodeAffinity
字段中支持使用matchExpressions
属性构建更为复杂的标签选择器。
首先对两个 node 加标签,设置两个 node 标签分别为 node1 和 node2。加标签的语法如下:
kubect1labelnodes <1abel-key>=
[root@k8s-master ~]#kubect1 labe1 nodes k8s-node01 type=node01
[root@k8s-master~]#kubect1 label nodes k8s-node02 type=node02
备注:
如果需要重命名 node 标签,可以使用如下命令:
[root@k8s-master ~]#kubect1 label nodes k8s-node01type=node1--overwrite
如果要删除node标签(标签名为"type"),可以使用如下命令:
```bash
[root@k8s-master ~]#kubect1 label node k8s-node01 type-
[root@k8s-master~]#kubectl get node--show-labels
[root@k8s-master ~]#vim test01.yam1
#定义一个pod资源清单,将该pod调度至有标签为type=node1的节点上
apiVersion:v1
kind:Pod
metadata:
name:pod01
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
#硬策略
requiredDuringSchedulingIgnoredDuringExecution:
#指定亲和性
nodeSelectorTerms:
-matchExpressions:
-{key:type,operator:In,values:["node01"]}
containers:
-name:pod01
image: nginx:1.7.9
备注:values:["node01"] 中 node01 为节点的标签,该 pod 要调度到标签为 node01 的节点,即 k8s-node011。
[root@k8s-master~]# kubectl apply -f test01.yam1
[root@k8s-master ~]#kubectl get pod pod01-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod01 1/1 Running 0 9s 10.244.85.203 k8s-node01
[root@k8s-master ~]#vim test02.yam1
#定义一个pod资源清单,将该pod调度至有标签为type=node02的节点上
apiVersion: v1
kind:Pod
metadata:
name:pod02
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
#硬策略
requiredDuringSchedulingIgnoredDuringExecution:
#指定亲和性
nodeSelectorTerms:
-matchExpressions:
-{key:type,operator:In,values:["node02"]}
containers:
-name:pod02
image: nginx:1.7.9
备注:
values:["node02"] 中 node02 为节点的标签,该 pod 要调度到标签为 node02 的节点,即 k8s-node022。
[root@k8s-master ~]# kubectl apply -f test02.yam1
[root@k8s-master~]#kubectl get pod pod02-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod02 1/1 Running 0 5s 10.244.58.195 k8s-node02
节点软亲和性为节点选择提供了一种柔性的控制器逻辑,当条件不满足时,也能够接受被编排在其他不符合条件的节点之上。同时他还为每种倾向性提供了 weight 属性以便用于自定义优先级,范围是 1-100,越大越优先3。
[root@k8s-master ~]# vi test03.yam1
apiVersion: v1
kind:Pod
metadata :
name:pod03
spec:
#亲和性调度
affinity:
#节点亲和性调度
nodeAffinity:
#软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight:60
preference:
matchExpressions:
-(key: type,operator:In, values:["node1"]}
- weight:30
preference:
matchExpressions:
-{key:type,operator:In,values:["node02"]}
containers:
-name:pod03
image:nginx:1.7.9
[root@k8s-master ~]# kubectl apply -f test03.yam1
[root@k8s-master~]#kubectl get pod pod03-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod03 1/1 Running 0 17s 10.244.85.204 k8s-node01
可以看到,由于 Pod 对 node01 的亲和性权重更高,被部署到了 node01 上4。
注意:
出于某些需求,将一些 Pod 对象组织在相近的位置(同一节点、机架、区域等),此时这些 pod 对象间的关系为 pod 亲和性7。
Pod 的亲和性调度也存在硬亲和性和软亲和性的区别,他们表示的约束意义同节点亲和性相似8。
Pod 硬亲和性调度也使用 requiredDuringSchedulingIgnoreDuringExecution 属性进行定义9。
首先创建两个基础 Pod,并对它打标签,分别部署到 node01 和 node02 上10。
[root@k8s-master~]#vi test04.yam1
apiVersion: v1
kind:Pod
metadata:
name:pod04
labels:
app:pod04
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
-{key:type,operator:In,values:["node01"]}
containers:
-name:pod04
image:nginx:1.7.9
注意:
pod04 的标签是 pod04,所在的节点标签是 node0111。
[root@k8s-master ~]# kubectl apply -f test04.yam1
[root@k8s-master~]#kubectl get pod pod04-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod04 1/1 Running 0 12s 10.244.85.205 k8s-node01
[root@k8s-master ~]#vi test05.yam1
apiVersion: v1
kind:Pod
metadata:
name:pod05
labels:
app:pod05
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
-matchExpressions:
-{key:type,operator:In,values:["node02"]}
containers:
-name:pod05
image: nginx:1.7.9
注意:
pod05 的标签是 pod05,所在的节点标签是 node0212。
[root@k8s-master ~]# kubectl apply -f test05.yam1
[root@k8s-master~]#kubectl get pod pod05 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod05 1/1 Running 0 5s 10.244.58.196 k8s-node02
可以看到,一个位于 node01,一个位于 node0213。
[root@k8s-master~]#vim test06.yam1
apiVersion: v1
kind:Pod
metadata:
name:pod06
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
-labelSelector:
matchExpressions:
-{key:app,operator:In, values:["pod05"]}
topologyKey: kubernetes.io/hostname
containers:
-name:pod06
image:nginx:1.7.9
注意:
[root@k8s-master ~]# kubect1 create -f test06.yam1
[root@k8s-master ~]#kubect1 get pod -owide
Pod 软亲和调度使用方法与 Node 软亲和调度方法类似17。
[root@k8s-master ~]#vim test07.yam1
apiVersion:v1
kind:Pod
metadata:
name:pod07
spec:
#亲和性调度
affinity:
#pod亲和性调度
podAffinity:
#pod软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight:80
podAffinityTerm:#亲和标签
labelSelector:#标签选择
matchExpressions:#正则匹配
-{key: app,operator:In, values:["pod05"]}
topologyKey: kubernetes.io/hostname
- weight:20
podAffinityTerm:#亲和标签
labelSelector:#标签选择
matchExpressions:#正则匹配
-{key:app,operator:In,values:["pod04"]}
topologyKey:kubernetes.io/hostname
containers:
-name:pod07
image: nginx:1.7.9
注释:
注意:
pod 软亲和中的权重值是会影响调度到的节点的,pod 软亲和中谁的权重高就调度到谁那里21。
[root@k8s-master ~]# kubectl create -ftest07.yam1
[root@k8s-master~]#kubectl get pod pod07-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod07 1/1 Running 0 64s 10.244.85.204 k8s-node02
修改 yaml 文件中的权重,使其与 pod04 亲和性更高,因为 pod04 在 node01 上,pod08 也会被部署到 node01 上。
[root@k8s-master ~]# vim test08.yam1
apiVersion:v1
kind: Pod
metadata:
name:pod08
spec:
#亲和性调度
affinity :
#pod亲和性调度
podAffinity:
#软策略
preferredDuringSchedulingIgnoredDuringExecution:
#指定亲和性及权重
- weight:20
podAffinityTerm:
labelSelector:
matchExpressions:
-{key:app,operator:In,values:["pod05"]}
topologyKey: kubernetes.io/hostname
- weight:80
podAffinityTerm:
labelSelector:
matchExpressions:
-{key:app,operator:In,values:["pod04"]}
topologyKey: kubernetes.io/hostname
containers:
-name:pod08
image: nginx:1.7.9
[root@k8s-master ~]# kubectl create -ftest08.yam1
[root@k8s-master~]#kubectl get pod pod08-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod08 1/1 Running 0 2m14s 10.244.85.205 k8s-node01
备注:
匹配了 "pod04" 标签的亲和度对应的权重值更高,按照此权重,pod08 会被调度到标签为 "pod04" 的 pod 所在的 node 节点,即 node0123。
[root@k8s-master ~]#vim test09.yam1
apiVersion:v1
kind: Pod
metadata:
name:pod09
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
-labelSelector:
matchExpressions:
-{key:app,operator:In,values:["pod05"]}
topologyKey:kubernetes.io/hostname
containers:
-name:pod09
image: nginx:1.7.9
[root@k8s-master ~]#kubectl create -ftest09.yam1
[root@k8s-master ~]#kubectl get pod -owide
查看 pod09 所在的节点,因为设置的反亲和 pod05,所以,pod05 在 node02 的话,pod09 就会在 node0124。