Kubernets 污点与容忍
一、污点与容忍简介
污点taints
是定义在节点之上的键值型属性数据,用于让节点拒绝将Pod
调度运行于其上, 除非该Pod
对象具有接纳节点污点的容忍度。而容忍度tolerations
是定义在 Pod
对象上的键值型属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod
对象调度至其能够容忍该节点污点的节点之上!
节点选择器nodeSelector
和节点亲和性nodeAffinity
两种调度方式都是通过在 Pod
对象上添加标签选择器来完成对特定类型节点标签的匹配,它们实现的是由Pod
选择节点的机制。而污点和容忍度则是通过向节点添加污点信息来控制Pod
对象的调度结果,从而赋予了节点控制何种Pod
对象能够调度于其上的主控权。简单来说,节点亲和性使得Pod
对象被吸引到一类特定的节点,而污点则相反,它提供了让节点排斥特定Pod
对象的能力。
二、定义污点和容忍度
污点定义在节点的node Spec
中,而容忍度则定义在Pod
的podSpec
中,它们都是键值型数据,但又都额外支持一个效果effect
标记,语法格式为key=value:effect
,其中key
和value
的用法及格式与资源注俯-信息相似, 而effect
则用于定义对Pod
对象的排斥等级,它主要包含以下三种类型
- NoSchedule:不能容忍此污点的新
Pod
对象不可调度至当前节点,属于强制型约束关系,节点上现存的Pod
对象不受影响。 - PreferNoSchedule:
NoSchedule
的柔性约束版本,即不能容忍此污点的新Pod
对象尽量不要调度至当前节点,不过无其他节点可供调度时也允许接受相应的Pod
对象。节点上现存的Pod
对象不受影响。 - NoExecute:不能容忍此污点的新
Pod
对象不可调度至当前节点,属于强制型约束关系,而且节点上现存的Pod
对象因节点污点变动或Pod
容忍度变动而不再满足匹配规则时,Pod
对象将被驱逐。
在Pod
对象上定义容忍度时,它支持两种操作符:一种是等值比较Equal
,表示容忍度与污点必须在key
、value
和effect
三者之上完全匹配;另一种是存在性判断Exists
,表示二者的key
和effect
必须完全匹配,而容忍度中的value
字段要使用空值。
一个节点可以配置使用多个污点,一个Pod
对象也可以有多个容忍度,不过二者在进行匹配检查时应遵循如下逻辑。
- 首先处理每个有着与之匹配的容忍度的污点
- 不能匹配到的污点上,如果存在一个污点使用了
NoSchedule
效用标识,则拒绝调度Pod
对象至此节点 - 不能匹配到的污点上,若没有任何一个使用了
NoSchedule
效用标识,但至少有一个使用了PreferNoScheduler
,则应尽量避免将Pod
对象调度至此节点 - 如果至少有一个不匹配的污点使用了
NoExecute
效用标识,则节点将立即驱逐Pod
对象,或者不予调度至给定节点;另外,即便容忍度可以匹配到使用了NoExecute
效用标识的污点,若在定义容忍度时还同时使用tolerationSeconds
属性定义了容忍时限,则超出时限后其也将被节点驱逐。
使用kubeadm
部署的Kubernetes
集群,其Master
节点将自动添加污点信息以阻止不能容忍此污点的Pod
对象调度至此节点,因此,用户手动创建的未特意添加容忍此污点容忍度的Pod
对象将不会被调度至此节点!
$ kubectl describe nodes k8s-master | grep -i taints
Taints: node-role.kubernetes.io/master:NoSchedule
有些系统级应用,如kube-proxy
或者kube-flannel
等,都在资源创建时就添加上了相应的容忍度以确保它们被DaemonSet
控制器创建时能够调度至Master
节点运行一个实例:
$ kubectl -n kube-system describe pod kube-proxy-n5c5z | grep -A 8 Tolerations
Tolerations:
CriticalAddonsOnly
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/pid-pressure:NoSchedule
node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/unschedulable:NoSchedule
这类Pod
是构成Kubernetes
系统的基础且关键性的组件,它们甚至还定义了更大的容忍度。从上面某calico-node
实例的容忍度定义来看,它还能容忍那些报告了磁盘压力或内存压力的节点,以及未就绪的节点和不可达的节点,以确保它们能在任何状态下正常调度至集群节点上运行。
三、管理节点的污点
任何符合其键值规范要求的字符串均可用于定义污点信息:仅可使用字母、数字、连接符、点号和下划线,且仅能以字母或数字开头,其中键名的长度上限为253
个字符,值最长为63
个字符。实践中,污点通常用于描述具体的部署规划,它们的键名形如node-tppe
、node-role
、node-project
或node-geo
等,因此还可在必要时带上域名以描述其额外的信息,如node-type.linux.io
等。使用kubectl taint
命令即可向节点添加污点,命令的语法格式如下:
$ kubectl taint nodes <node-name> <key>=<value>:<effect>
例如,使用node-type=production:NoSchedule
定义节点k8s-node01
:
$ kubectl taint node k8s-node01 node-type=production:NoSchedule
需要注意的是,即便是同一个键值数据,若其效用标识不同,则其也分属于不同的污点信息,例如,将上面命令中的效用标识定义为PreferNoSchedule
再添加一次:
$ kubectl taint node k8s-node01 node-type=production:PreferNoSchedule
删除某污点,仍然通过kubectl taint
命令进行,但要使用如下的命令格式,省略效用标识则表示删除使用指定键名的所有污点,否则就只删除指定键名上对应效用标识的污点:
$ kubectl taint nodes <node-name> <key>:[<effect>]-
例如,删除k8s-nodeO1
上node-type
键的效用标识为NoSchedule
的污点信息:
$ kubectl taint node k8s-node01 node-type=production:NoSchedule-
若要删除使用指定键名的所有污点,则在删除命令中省略效用标识即能实现,例如:
$ kubectl taint node k8s-node01 node-type-
删除节点上的全部污点信息,通过kubectl patch
命令将节点属性spec.taints
的值直接置空即可,例如:
$ kubectl patch nodes k8s-node01 -p '{"spec":{"taints":[]}}'
节点污点的变动会影响到新建Pod
对象的调度结果,而且使用NoExecute
进行标识时,还会影响到节点上现有的Pod
对象。
四、Pod对象的容忍度
Pod
对象的容忍度可通过其spec.tolerations
字段进行添加,根据使用的操作符不同,主要有两种可用的形式:一种是与污点信息完全匹配的等值关系;另一种是判断污点信息存在性的匹配方式。使用Equal
操作符的示例如下所示,其中 tolerationSeconds
用于定义延迟驱逐当前Pod
对象的时长
注意:
- 如果
operator
是Exists
(此时容忍度不能指定 value); - 如果
operator
是Equal
,则它们的value
应该相等;
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
使用存在性判断机制的容忍度示例如下:
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 3600
实践中,若集群中的一组机器专用于为运行非生产型的容器应用而备置,而且它们可能随时按需上下线,那么就应该为其添加污点信息,以确保仅那些能容忍此污点的非生产型Pod
对象可以调度其上。另外,某些有着特殊硬件的节点需要专用于运行一类有着此类硬件资源需求的Pod
对象时,例如,那些有着SSD
或GPU
的设备,也应该为其添加污点信息以排除其他的Pod
对象。
五、问题节点标识
Kubernetes
自1.6
版本起支持使用污点自动标识问题节点,它通过节点控制器在特定条件下自动为节点添加污点信息实现。它们都使用NoExecute
效用标识,因此不能容忍此类污点的现有Pod
对象也会遭到驱逐。目前,内建使用的此类污点包含如下几个
-
node.kubernetes.io/not-ready:节点进入
NotReady
状态时被自动添加的污点; -
node.alpha.kubernetes.io/unreachable:节点进入
NotReachable
状态时被自动添加的污点; -
node.kubernetes.io/out-of-disk:节点进入
OutOfDisk
状态时被自动添加的污点; -
node.kubernetes.io/memory-pressure:节点内存资源面临压力;
-
node.kubernetes.io/disk-pressure:节点磁盘资源面临压力;
-
node.kubernetes.io/network-unavailable:节点网络不可用;
-
node.cloudprovider.kubernetes.io/uninitialized:
kubelet
由外部的云环境程序启动时,它将自动为节点添加此污点,待到云控制器管理器中的控制器初始化此节点时再将其删除;
Kubernetes
的核心组件通常都要容忍此类的污点,以确保其相应的DaemonSet
控制器能够无视此类污点,于节点上部署相应的关键性Pod
对象,例如kube-proxy
或kube- flannel
等。
六、示例
$ kubectl describe node k8s-master | grep -i taints
Taints: node-role.kubernetes.io/master:NoSchedule
# master节点上有污点存在
apiVersion: apps/v1
kind: Deployment
metadata:
name: taint
labels:
app: taint
spec:
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: taint
template:
metadata:
labels:
app: taint
spec:
containers:
- name: nginx
image: 192.168.99.181:5000/wod/nginx:1.15.1
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
$ kubectl get pod -o wide -l app=taint
# 可以看到master节点上也运行了taint容器
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
taint-845fdd48c-5gl26 1/1 Running 0 29s 10.100.85.238 k8s-node01 <none> <none>
taint-845fdd48c-78xfb 1/1 Running 0 29s 10.100.235.224 k8s-master <none> <none>
taint-845fdd48c-m8ppr 1/1 Running 0 29s 10.100.58.254 k8s-node02 <none> <none>