前言

前面介绍了节点亲和性调度,它可以使得我们的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的控制器。

欢迎关注,学习不迷路!

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐