K8s调度之污点与容忍
前言
前面介绍了节点亲和性调度,它可以使得我们的Pod调度到指定的Node节点上,而污点(Taints)与之相反,它可以让Node拒绝Pod的运行,甚至驱逐已经在该Node上运行的Pod
污点是Node上设置的一个属性,通常设置污点表示该节点有问题,比如磁盘要满了,资源不足,或者该Node正在升级暂时不能提供使用等情况,这时不希望再有新的Pod进来,这个时候就可以给该节点设置一个污点。
但是有的时候其实Node节点并没有故障,只是不想让一些Pod调度进来,比如这台节点磁盘空间比较大,希望是像Elasticsearch、Minio这样需要较大磁盘空间的Pod才调度进来,那么就可以给节点设置一个污点,给Pod设置容忍(Tolerations)对应污点,如果再配合节点亲和性功能还可以达到独占节点的效果
一般时候 Taints 总是与 Tolerations配合使用
污点(Taints)
我们已经知道了设置污点后,一般Pod将不会调度到该节点上来,大家看过我先前的文章会发现,我的K8s集群有三个节点,分别是master、node01、node02,但是每次Pod都不会调度到master节点,那是因为搭建K8s集群的时候,K8s给master自带了一个污点。
查看污点
查看master上是否有污点,通过describe命令可以看到节点上的所有污点
[root@master ~]# kubectl describe node master
...
Taints: node-role.kubernetes.io/master:NoSchedule
...
还可以通过如下命令查看
[root@master ~]# kubectl get nodes master -o go-template={{.spec.taints}}
[map[effect:NoSchedule key:node-role.kubernetes.io/master]]
发现master上自带了一个没有value的污点 node-role.kubernetes.io/master ,effect 为 NoSchedule,由于我们的Pod都没有容忍这个污点,所以一直都不会调度到master
污点类别
上面我们看到了污点有一个effect 属性为NoSchedule,其实还有其它两种类别分别是 PreferNoSchedule与 NoExecute
- NoSchedule: 如果没有容忍该污点就不能调度进来。
- PreferNoSchedule: 相当于NoExecute的一个软限制,如果没有容忍该污点,尽量不要把Pod调度进来,但也不是强制的。
- NoExecute: 如果没有设置容忍该污点,新的Pod肯定不会调度进来, 并且已经在运行的Pod没有容忍该污点也会被驱逐
设置污点
污点分别由key、value(可以为空)、effect 组成,设置命令如下
# 设置污点并不允许Pod调度到该节点
$ kubectl taint node node01 key=value:NoSchedule
# 设置污点尽量不要让Pod调度到该节点
$ kubectl taint node node01 key=value:PreferNoSchedule
# 设置污点,不允许Pod调度到该节点,并且且将该节点上没有容忍该污点的Pod将进行驱逐
$ kubectl taint node node01 key=value:NoExecute
删除污点
# 删除该key的所有污点
$ kubectl taint node node01 key-
# 删除该key的某一个污点
$ kubectl taint node node01 key=value:NoSchedule-
# 删除该key的某一个污点可以不写value
$ kubectl taint node node01 key:NoSchedule-
污点测试
给node01节点设置一个污点,表示只有前台web应用才能调度,后端app应用不能调度
[root@master ~]# kubectl taint node node01 web=true:NoSchedule
node/node01 tainted
编写 web-taint.yaml 内容如下,有三个Pod均没有容忍该污点
apiVersion: v1
kind: Pod
metadata:
name: web-taint1
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
---
apiVersion: v1
kind: Pod
metadata:
name: web-taint2
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
---
apiVersion: v1
kind: Pod
metadata:
name: web-taint3
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
启动Pod,观察Pod是否都被调度到node02节点
# 启动三个Pod
[root@master taint]# kubectl create -f web-taint.yaml
pod/web-taint1 created
pod/web-taint2 created
pod/web-taint3 created
# 查看pod详情发现均被调度到node02节点
[root@master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-taint1 1/1 Running 0 13s 10.244.2.35 node02 <none> <none>
web-taint2 1/1 Running 0 13s 10.244.2.34 node02 <none> <none>
web-taint3 1/1 Running 0 13s 10.244.2.36 node02 <none> <none>
给node02节点天津污点 app=true:NoExecute,表示只有后端服务才能调度进来,并且已经在node02节点上运行的不是后端服务的节点将被驱逐
# 设置污点
[root@master taint]# kubectl taint node node02 app=true:NoExecute
node/node02 tainted
# 查看Pod详情,发现三个Pod已经被驱逐
[root@master taint]# kubectl get pod -o wide
No resources found in default namespace.
从上面可以知道我们虽然设置了污点,但是我们的节点其实很正常,既然是正常节点,那么就可以有服务运行,这就需要用到容忍机制来运行这些Pod
容忍(Toletations)
如果需要Pod忽略Node上的污点,就需要给Pod设置容忍,并且是需要容忍该Pod上的所有污点。
通过 kubectl explain pod.spec.tolerations 可以查看容忍的配置项
配置项
tolerations: # 数组类型,可以设置多个容忍
- key: # 污点key
operator: # 操作符,有两个选项 Exists and Equal 默认是Equal
value: # 污点value,如果使用Equal需要设置,如果是Exists就不需要设置
effect: # 可以设置为NoSchedule、PreferNoSchedule、NoExecute,如果为空表示匹配该key下所有污点,
tolerationSeconds: # 如果污点类型为NoExecute,还可以设置一个时间,表示这一个时间段内Pod不会被驱逐,过了这个时间段会立刻驱逐,0或者负数会被立刻驱逐
设置容忍
给Pod设置容忍node01节点的污点web=true:NoSchedule
编写 web-tolerations.yaml 内容如下,容忍污点web=true:NoSchedule
apiVersion: v1
kind: Pod
metadata:
name: web-tolerations
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
tolerations:
- key: web
operator: Equal
value: "true"
effect: NoSchedule
启动web-tolerations,观察Pod是否正常运行
# 启动web-tolerations
[root@master taint]# kubectl create -f web-tolerations.yaml
pod/web-tolerations created
# Pod正常运行,且运行在node01节点
[root@master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-tolerations 1/1 Running 0 8s 10.244.1.92 node01 <none> <none>
查看该Pod上的容忍
[root@master taint]# kubectl describe pod web-tolerations
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
web=true:NoSchedule
可以看到我们设置的Tolerations在里边,除了我们自己的,K8s还会给每个Pod设置两个默认Tolerations,并且Tolerations时间为五分钟,表示某些节点发生一些临时性问题,Pod能够继续在该节点上运行五分钟等待节点恢复,并不是立刻被驱逐,从而避免系统的异常波动
编写 app-tolerations.yaml 内容如下,容忍污点app=true:NoExecute,并且只容忍60s
apiVersion: v1
kind: Pod
metadata:
name: app-tolerations
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: app
operator: Exists
effect: NoExecute
tolerationSeconds: 60
启动app-tolerations,查看Pod是否能够正常运行,且运行在node02节点,等待60秒,查看Pod是否已被驱逐
# 启动app-tolerations
[root@master taint]# kubectl create -f app-tolerations.yaml
pod/app-tolerations created
# 查看详情,已经运行成功并且调度到了node02节点,此时运行59秒
[root@master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
app-tolerations 1/1 Running 0 59s 10.244.2.37 node02 <none> <none>
# 运行60秒查看,状态已经变为Terminating
[root@master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
app-tolerations 1/1 Terminating 0 60s 10.244.2.37 node02 <none> <none>
# 立刻再次查看,已经被驱逐
[root@master taint]# kubectl get pod -o wide
No resources found in default namespace.
如果一个节点没有任何污点,另一个节点有污点,并且Pod容忍了该污点,那么最终会调度到哪个节点呢?
删除node02节点的污点app=true:NoExecute,保留node01节点的web=true:NoSchedule,再次启动之前的web-tolerations.yaml 观察Pod所在节点
# 删除node02上的污点
[root@master taint]# kubectl taint node node02 app:NoExecute-
node/node02 untainted
# 启动Pod web-tolerations
[root@master taint]# kubectl create -f web-tolerations.yaml
pod/web-tolerations created
# Pod正常运行,并且运行在node02节点
[root@master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-tolerations 1/1 Running 0 2s 10.244.2.38 node02 <none> <none>
上面表明,即使是容忍了该污点,如果有其它节点不需要容忍就可以调度的话,那还是会优先选择没有污点的机器,但是生产上往往设置污点就是为了给你这个Pod使用的,不能调度到其它节点,这个时候需要配合节点亲和性调度一起使用。
Equal 与 Exists
Exists 表示key是否存在所以无需设置value,Equal 则必须设置value需要完全匹配,默认为Equal
空的key配合Exists 可以匹配所有的key-value,也就是容忍所有的污点,生产上非常不建议使用
节点故障后Pod的驱逐行为
上面也提到了,K8s为每个Pod设置了两个容忍,如果是一些临时性故障,可以容忍五分钟不被驱逐
在K8s 1.6之后还引入了Taint的两个新特性,TaintNodesByCondition与TaintBasedEvictions用来改善出现异常时对Pod的调度与驱逐问题
TaintNodesByCondition的特性如下(为节点添加NoSchedule的污点)
- Node节点会不断的检查Node的状态,并且设置对应的Condition
- 不断地根据Condition的变更设置对应的Taint
- 不断地根据Taint驱逐Node上的Pod
主要污点如下
node.kubernetes.io/not-ready 节点未就绪,节点Ready为False
node.kubernetes.io/unreachable 节点不可触达
node.kubernetes.io/out-of-disk 磁盘空间已满
node.kubernetes.io/network-unavailable 网络不可用
node.kubernetes.io/unschedulable 节点不可调度
node.cloudprovider.kubernetes.io/uninitialized 如果 kubelet 从 外部
云服务商启动的,该污点用来标识某个节点当前为不可用状态,当云控制器 cloud-controller-manager 初始化这个节点后,kubelet 会将此污点移除
**TaintBasedEvictions **特性添加的是NoExecute的污点,例如内存与磁盘有压力时,如果Pod没有设置容忍这些污点,则会被驱逐,以保证Node不会崩溃
主要污点如下
node.kubernetes.io/memory-pressure 内存不足
node.kubernetes.io/disk-pressure 磁盘不足
1.13版本之后TaintNodesByCondition 与 TaintBasedEvictions 都是默认开启
污点与容忍就介绍到这里,后面将会介绍Pod的控制器。
欢迎关注,学习不迷路!