在Kubernetes
中,污点
是一种属性,它可以被赋予Node
(节点),用于标记该节点上的Pod
应该避免调度的特定条件,例如特定的硬件限制、安全策略等。而容忍
则是Pod的一个属性,它允许Pod
在特定的污点条件下仍然被调度到对应的节点上。
k8s 集群中可能管理着非常庞大的服务器,这些服务器可能是各种各样不同类型的,比如机房、地理位置、配置等,有些是计算型节点,有些是存储型节点,此时我们希望能更好的将 pod 调度到与之需求更匹配的节点上。
此时就需要用到污点(Taint
)和容忍(Toleration
),这些配置都是 key: value
类型的。
比如我们拿一个三节点的 K8s
集群来说:
Master
节点一般是用来调度从节点实现对应的任务,是更重要的角色,而从节点是用来执行对应的任务的。当创建一个新的任务,无论是 Pod
还是 Deployment
都会先找没有存在污点的节点上执行。
污点
还可以理解为,为了将未分配的任务排挤到没有污点的节点上运行。
而容忍
,就是反过来的概念,你有污点,我这个任务配置了容忍,也就是可以容忍你有污点,这时候我这个任务还是可以调度到这个节点上的。
硬件特性限制:当某些节点具有特定的硬件特性(如GPU、TPU等)时,可以给这些节点打上对应的污点,然后只有带有对应容忍标记的Pod才会被调度到这些节点上,从而保证Pod能够充分利用这些硬件资源。
避免节点负载过高:当节点负载过高时,可以给这些节点打上污点,防止新的Pod被调度到这些节点上,以避免进一步加重节点负载。
安全策略:通过污点和容忍,可以实现对部分节点进行安全隔离,例如将某些特殊的安全策略应用到特定的节点上,以满足安全合规性要求。
节点维护:在节点需要维护时,可以先将节点打上污点,然后逐步驱逐上面的Pod,以确保维护操作不会影响到正在运行的应用程序。
总之,污点和容忍功能为Kubernetes集群的高级调度提供了灵活性和精细化的控制,可以根据实际需求对Pod的调度行为进行定制,使得整个集群能够更好地适应复杂的业务场景和运维需求。
要向节点添加污点,可以使用kubectl命令行工具执行以下命令:
kubectl taint nodes <node-name> key=value:taint-effect
其中:
是要添加污点的节点名称。key=value
是污点的键值对,可以根据实际情况定义不同的键值对,例如gpu=true。taint-effect
指定了污点的效果,包括:
NoSchedule
:表示新的Pod将不会被调度到带有此污点的节点上。NoExecute
:表示已经存在的Pod如果不满足对应的容忍条件,则将被驱逐出该节点。例如,要向名为node1
的节点添加一个gpu=true:NoSchedule
的污点,可以执行以下命令:
kubectl taint nodes node1 gpu=true:NoSchedule
kubectl describe no k8s-master
要删除节点上的污点,可以使用kubectl命令行工具执行以下命令:
kubectl taint nodes <node-name> key-
这里最后的这个 -
号,就是删除这个污点的意思。这跟操作 Label 是一样的。
例如,要删除名为node1
的节点上的gpu=true:NoSchedule
污点,可以执行以下命令:
kubectl taint nodes node1 gpu-
NoSchedule
:不能容忍的 pod 不能被调度到该节点,但是已经存在的节点不会被驱逐NoExecute
:不能容忍的节点会被立即清除,能容忍且没有配置- tolerationSeconds
属性,则可以一直运行,设置了 tolerationSeconds: 3600
属性,则该 pod 还能继续在该节点运行 3600 秒# pod 的 spec 下面配置容忍
tolerations:
- key: "污点的 key"
value: "污点的 value"
offect: "NoSchedule" # 污点产生的影响
operator: "Equal" # 表是 value 与污点的 value 要相等,也可以设置为 Exists 表示存在 key 即可,此时可以不用配置 value
其中:
Equal
: 比较操作类型为 Equal,则意味着必须与污点值做匹配,key/value都必须相同,才表示能够容忍该污点;Exists
:容忍与污点的比较只比较 key,不比较 value,不关心 value 是什么东西,只要 key 存在,就表示可以容忍。要删除Pod的容忍规则,可以通过编辑Pod的配置文件,将tolerations
字段移除
或者修改
为其他的容忍规则。
看下当前集群中 Pod 的调度情况:
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-ptqc6 1/1 Running 318 (42m ago) 13d 10.244.2.96 docker-56 <none> <none>
nginx-deploy-bcc5c945c-vwbrd 1/1 Running 140 (42m ago) 5d20h 10.244.1.61 docker-55 <none> <none>
[root@docker-54 ~]#
可以看到,当前集群中在没有配置任何污点时的调度,docker-55、docker-56 两个节点的调度情况基本一致;
现在给 docker-56 节点添加一个内存低的污点:memory=low:NoSchedule
kubectl tain no docker-56 memory=low:NoSchedule
在执行前可以先看下节点的污点情况:kubectl describe no docker-56
其中污点信息Taints
如下:
CreationTimestamp: Sun, 14 May 2023 23:05:48 +0800
Taints: <none>
Unschedulable: false
看下 Master 节点的污点情况:
[root@docker-54 ~]# kubectl describe no docker-54
CreationTimestamp: Sun, 14 May 2023 22:57:50 +0800
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false
可以看到这里设置了污点的内容是:node-role.kubernetes.io/master:NoSchedule
,也就是如果没有配置容忍为 node-role.kubernetes.io/master
则不会调度到 Master 节点上。
然后对节点 docker-56
添加完污点信息,则变为:
CreationTimestamp: Sun, 14 May 2023 23:05:48 +0800
Taints: memory=low:NoSchedule
Unschedulable: false
接着看下 Pod 的执行情况:
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-ptqc6 1/1 Running 318 (51m ago) 13d 10.244.2.96 docker-56 <none> <none>
nginx-deploy-bcc5c945c-vwbrd 1/1 Running 140 (51m ago) 5d20h 10.244.1.61 docker-55 <none> <none>
[root@docker-54 ~]#
可以看到这时候,是没什么区别的,因为我们上面配置的污点是 NoSchedule
,只是不再往上面调度任务了,对于已经存在的Pod 则不会有影响。
对于 Master 节点的污点,如果删除了该污点,则新创建的 Pod 任务是允许调度到 Master 节点上的。
我们来测试下看看效果:
先删除 Master 节点上的污点
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master-
node/docker-54 untainted
[root@docker-54 ~]#
然后手动删除之前部署的 nginx-deploy
,由于之前是使用 deploy 创建的 Pod,在手动删除后,k8s 会自动再启动两个 Pod;
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-blsgx 1/1 Running 0 28s 10.244.1.72 docker-55 <none> <none>
nginx-deploy-bcc5c945c-ptqc6 1/1 Terminating 319 (5m15s ago) 13d 10.244.2.96 docker-56 <none> <none>
nginx-deploy-bcc5c945c-rf6gl 0/1 ContainerCreating 0 28s <none> docker-54 <none> <none>
nginx-deploy-bcc5c945c-vwbrd 1/1 Terminating 141 (4m21s ago) 5d21h 10.244.1.61 docker-55 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-blsgx 1/1 Running 0 51s 10.244.1.72 docker-55 <none> <none>
nginx-deploy-bcc5c945c-rf6gl 0/1 ContainerCreating 0 51s <none> docker-54 <none> <none>
[root@docker-54 ~]#
可以看到,上面在删除了之后,两个新的 nginx-deploy 的 Pod 又重新启动,并且一个已经被调度到 Master 节点 docker-54
上。
然后调整 Master 节点的污点,新增一个 NoExecute
污点:
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoExecute
node/docker-54 tainted
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-9mcs5 1/1 Running 0 22s 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Running 0 4m13s 10.244.1.72 docker-55 <none> <none>
nginx-deploy-bcc5c945c-rf6gl 1/1 Terminating 0 4m13s 10.244.0.10 docker-54 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-9mcs5 1/1 Running 0 52s 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Running 0 4m43s 10.244.1.72 docker-55 <none> <none>
[root@docker-54 ~]#
可以看到,在新增 NoExecute
污点后,docker-54
节点上的 Pod 被驱离,这时候两个 nginx-deploy
的Pod 都被调度到 docker-55
节点上了。而没有调度到 docker-56
节点,这是因为 docker-56
节点上有个 NoSchedule
的污点。
测试完毕,恢复 Master 节点上的污点:
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master-
node/docker-54 untainted
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoSchedule
node/docker-54 tainted
[root@docker-54 ~]#
[root@docker-54 ~]#
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl taint no docker-56 memory-
node/docker-56 untainted
[root@docker-54 ~]#
由于我的 docker-56 节点的污点设置值为memory=low:NoSchedule
[root@docker-54 ~]# kubectl describe no docker-56 |grep Taints
Taints: memory=low:NoSchedule
[root@docker-54 ~]#
所以接下来测试下设置容忍这个污点。
添加容忍前,Pod 调度情况:
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-9mcs5 1/1 Running 2 (31m ago) 151m 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Running 2 (35m ago) 155m 10.244.1.72 docker-55 <none> <none>
[root@docker-54 ~]#
由于我这里之前运行过 nginx-deploy 这里就直接修改deploy 新增容忍的配置:
[root@docker-54 jobs]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 2/2 2 2 46d
[root@docker-54 jobs]#
然后编辑:kubectl edit deploy nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
tolerations:
- effect: "NoSchedule"
key: "memory"
operator: "Equal"
value: "low"
containers:
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
新增 tolerations
相关的内容,这里需要注意,需要配置在 模板template
下的 spec
里面,跟 containers
并齐。
这里为了测试上面的污点,所以配置了 Equal
。修改完毕保存,看下 Pod 的变化情况;
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bcc5c945c-9mcs5 1/1 Running 2 (31m ago) 151m 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Running 2 (35m ago) 155m 10.244.1.72 docker-55 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-b8886d997-46sm4 1/1 Running 0 4s 10.244.2.118 docker-56 <none> <none>
nginx-deploy-b8886d997-d92xh 0/1 ContainerCreating 0 2s <none> docker-55 <none> <none>
nginx-deploy-bcc5c945c-9mcs5 1/1 Terminating 2 (33m ago) 153m 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Running 2 (37m ago) 157m 10.244.1.72 docker-55 <none> <none>
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-b8886d997-46sm4 1/1 Running 0 14s 10.244.2.118 docker-56 <none> <none>
nginx-deploy-b8886d997-d92xh 1/1 Running 0 12s 10.244.1.76 docker-55 <none> <none>
nginx-deploy-bcc5c945c-9mcs5 1/1 Terminating 2 (33m ago) 153m 10.244.1.75 docker-55 <none> <none>
nginx-deploy-bcc5c945c-blsgx 1/1 Terminating 2 (37m ago) 157m 10.244.1.72 docker-55 <none> <none>
[root@docker-54 ~]#
可以看到,在更新完 deployment
之后,原先 docker-55
节点上的两个 Pod ,停止了一个,然后在 docker-56
节点上启动了一个 Pod。证明上面配置的容忍生效了。这就是容忍的一个效果。
另外由于上面使用的是 Equal
,这个在比对污点的 key 的同时,还会比对 value ,当全部匹配的时候,容忍才能生效。当然也可以使用 Exists
,无需配置 value,因为这样就仅仅匹配污点的 key
即可生效。
测试下再次修改 deploy ,使用 Exists
来配置容忍,删除 value,保存,看下 Pod 的情况:
[root@docker-54 ~]# kubectl get po -o wide |grep nginx-deploy
nginx-deploy-b8886d997-46sm4 1/1 Terminating 0 19m 10.244.2.118 docker-56 <none> <none>
nginx-deploy-b8886d997-d92xh 1/1 Terminating 0 19m 10.244.1.76 docker-55 <none> <none>
nginx-deploy-b8976d7b5-77mgp 1/1 Running 0 29s 10.244.1.77 docker-55 <none> <none>
nginx-deploy-b8976d7b5-fvqn8 1/1 Running 0 31s 10.244.2.119 docker-56 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide |grep nginx-deploy
nginx-deploy-b8976d7b5-77mgp 1/1 Running 0 2m10s 10.244.1.77 docker-55 <none> <none>
nginx-deploy-b8976d7b5-fvqn8 1/1 Running 0 2m12s 10.244.2.119 docker-56 <none> <none>
[root@docker-54 ~]#
可以看到 Pod 被重新调度了,还是分别调度到两个从节点上。
有一点需要注意的是:如果设置了 NoExecute
的污点,这时候配置 NoSchedule
的容忍是没办法生效的。
可以这么理解,由于配置了 NoExecute
的污点,这时候上面所有没有配置容忍的 Pod 都会被驱离,即使 Pod 里面配置了 NoSchedule
的容忍。
在配置NoExecute
污点的容忍规则的时候,还有一种玩法使用tolerationSeconds
参数,可以使用tolerationSeconds
参数来设置Pod在被驱逐之前可以容忍的时间。如果没有配置,则可以一直容忍这个污点,Pod 可以一直运行。如果配置了这个时间,到这个时间就忍不下去了,这个 Pod 就会被停掉。
这里需要注意,在配置 tolerationSeconds
参数的时候,可能会存在一个反复调度到上面,然后到容忍的时间之后被停掉,然后再次调度,再次被停掉的一个循环过程中。
所以,在配置tolerationSeconds
参数时,需要仔细考虑集群的资源和调度策略,以避免出现Pod被循环停止和重新调度的问题。