污点(Taint) 是应用在节点之上的,从这个名字就可以看出来,是为了排斥pod 所存在的。
容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。
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的节点上。
给节点node1
增加一个污点,它的键名是key1
,键值是value1
,效果是NoSchedule
。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到node1
这个节点。
kubectl taint nodes node1 key1=value1:NoSchedule
若要移除上述命令所添加的污点,你可以执行:
kubectl taint nodes node1 key1=value1:NoSchedule-
可以在 PodSpec 中定义 Pod 的容忍度。 下面两个容忍度均与上面例子中使用kubectl taint
命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都能够被分配到node1
:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"
实例
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: "example-key"
operator: "Exists"
effect: "NoSchedule"
可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。
Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:
NoSchedule
的污点, 则 Kubernetes 不会将 Pod 分配到该节点。NoSchedule
的污点, 但是存在 effect 值为 PreferNoSchedule
的污点, 则 Kubernetes 会 尝试 不将 Pod 分配到该节点。NoExecute
的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。例如,假设您给一个节点添加了如下污点:
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
假定有一个 Pod,它有两个容忍度:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。
但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。
通常情况下,如果给一个节点添加了一个 effect 值为NoExecute
的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐, 任何可以忍受这个污点的 Pod 都不会被驱逐。
但是,如果 Pod 存在一个 effect 值为NoExecute
的容忍度指定了可选属性tolerationSeconds
的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。 如果在此之前上述污点被删除了,则 Pod 不会被驱逐。
通过污点和容忍度,可以灵活地让 Pod避开某些节点或者将 Pod 从某些节点驱逐。
如果您想将某些节点专门分配给特定的一组用户使用,您可以给这些节点添加一个污点(即, kubectl taint nodes nodename dedicated=groupName:NoSchedule
), 然后给这组用户的 Pod 添加一个相对应的 toleration。
拥有上述容忍度的 Pod 就能够被分配到上述专用节点,同时也能够被分配到集群中的其它节点。
如果您希望这些 Pod 只能被分配到上述专用节点,那么您还需要给这些专用节点另外添加一个和上述 污点类似的 label (例如:dedicated=groupName
),同时,还要在上述准入控制器中给 Pod 增加节点亲和性要求上述 Pod 只能被分配到添加了 dedicated=groupName
标签的节点上。
在部分节点配备了特殊硬件(比如 GPU)的集群中, 我们希望不需要这类硬件的 Pod 不要被分配到这些特殊节点,以便为后继需要这类硬件的 Pod 保留资源。
要达到这个目的,可以先给配备了特殊硬件的节点添加 taint (例如 kubectl taint nodes nodename special=true:NoSchedule
或 kubectl taint nodes nodename special=true:PreferNoSchedule
), 然后给使用了这类特殊硬件的 Pod 添加一个相匹配的 toleration。
和专用节点的例子类似,添加这个容忍度的最简单的方法是使用自定义准入控制器。 比如,我们推荐使用扩展资源来表示特殊硬件,给配置了特殊硬件的节点添加污点时包含扩展资源名称, 然后运行一个ExtendedResourceToleration准入控制器。此时,因为节点已经被设置污点了,没有对应容忍度的 Pod 不会被调度到这些节点。但当你创建一个使用了扩展资源的 Pod 时, ExtendedResourceToleration
准入控制器会自动给 Pod 加上正确的容忍度, 这样 Pod 就会被自动调度到这些配置了特殊硬件件的节点上。 这样就能够确保这些配置了特殊硬件的节点专门用于运行需要使用这些硬件的 Pod, 并且您无需手动给这些 Pod 添加容忍度。
这是在每个 Pod 中配置的在节点出现问题时的驱逐行为。
前文提到过污点的 effect 值 NoExecute
会影响已经在节点上运行的 Pod
NoExecute
的污点,那么 Pod 将马上被驱逐NoExecute
的污点,但是在容忍度定义中没有指定 tolerationSeconds
,则 Pod 还会一直在这个节点上运行。NoExecute
的污点,而且指定了 tolerationSeconds
, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
node.kubernetes.io/not-ready
:节点未准备好。这相当于节点状态 Ready
的值为 "False
"。node.kubernetes.io/unreachable
:节点控制器访问不到节点. 这相当于节点状态 Ready
的值为 "Unknown
"。node.kubernetes.io/memory-pressure
:节点存在内存压力。node.kubernetes.io/disk-pressure
:节点存在磁盘压力。node.kubernetes.io/pid-pressure
: 节点的 PID 压力。node.kubernetes.io/network-unavailable
:节点网络不可用。node.kubernetes.io/unschedulable
: 节点不可调度。node.cloudprovider.kubernetes.io/uninitialized
:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。在节点被驱逐时,节点控制器或者 kubelet 会添加带有NoExecute
效应的相关污点。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。
实例
一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段较长的时间, 愿意等待网络恢复以避免被驱逐。在这种情况下,Pod 的容忍度可能是下面这样的:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
说明:
Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready
的容忍度 并配置 tolerationSeconds=300
,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/not-ready
的容忍度。
同样,Kubernetes 会给 Pod 添加一个 key 为 node.kubernetes.io/unreachable
的容忍度 并配置 tolerationSeconds=300
,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/unreachable
的容忍度。
这种自动添加的容忍度意味着在其中一种问题被检测到时 Pod 默认能够继续停留在当前节点运行 5 分钟。
注意:DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute
的容忍度将不会指定 tolerationSeconds
:
node.kubernetes.io/unreachable
node.kubernetes.io/not-ready
这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。
控制平面使用节点控制器 自动创建 与 节点状况 对应的带有NoSchedule
效应的污点。
调度器在进行调度时检查污点,而不是检查节点状况。这确保节点状况不会直接影响调度。
如果DiskPressure
节点状况处于活跃状态,则控制平面 添加node.kubernetes.io/disk-pressure
污点并且不会调度新的 pod 到受影响的节点。
如果MemoryPressure
节点状况处于活跃状态,则控制平面添加node.kubernetes.io/memory-pressure
污点。
对于新创建的 Pod,可以通过添加相应的 Pod 容忍度来忽略节点状况。 控制平面还在具有除BestEffort
之外的QoS类的 pod 上 添加node.kubernetes.io/memory-pressure
容忍度。 这是因为 Kubernetes 将Guaranteed
或Burstable
QoS 类中的 Pod(甚至没有设置内存请求的 Pod) 视为能够应对内存压力,而新创建的BestEffort
Pod 不会被调度到受影响的节点上。
DaemonSet 控制器自动为所有守护进程添加如下 NoSchedule
容忍度以防 DaemonSet 崩溃:
node.kubernetes.io/memory-pressure
node.kubernetes.io/disk-pressure
node.kubernetes.io/pid-pressure
(1.14 或更高版本)node.kubernetes.io/unschedulable
(1.10 或更高版本)node.kubernetes.io/network-unavailable
(只适合主机网络配置)