污点(Taint)作用于 node 节点上,可以阻止不满足要求的 pod 调度到该节点。每个节点可以有多个污点配置。
容忍(Toleration)作用于 pod 上,提供一定的配置使其满足特定节点(如有污点的节点)要求,使其能够调度到该节点。当有 pod 容忍配置时,表明该 pod 具有调度到配置了污点节点的能力,也可以调度其他没有污点的节点。每个 pod 可以有多个容忍配置。
污点和容忍相互配合,可以用来避免 Pod 被分配到不合适的节点上。例如集群中 GPU 节点只运行需要 GPU 运算的任务调度。例如节点 NotReady 了,不允许任何任务调度到该异常的节点。
1.1. 污点(Taint)
spec:
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
污点(Taint)配置是一对 key-value 键值对,同时还附带了影响效果 effect。
污点阻止 pod 调度有3种 effect,分别是 NoSchedule、NoExecute 和 PreferNoSchedule。
| effect | 描述 |
|---|---|
| NoSchedule | **禁止** 不满足要求的 pod 调度到该节点。 |
| NoExecute | **禁止** 不满足要求的 pod 调度到该节点。如果在配置污点前 pod 已经调度了,那么该pod将会被驱逐。 |
| PreferNoSchedule | **尽量** 避免将 Pod 调度到存在其不能容忍污点的节点上,但这不是强制的。 |
- 为 node1 设置 taint:
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
- 删除上面的 taint:
kubectl taint nodes node1 key1:NoSchedule-
kubectl taint nodes node1 key1:NoExecute-
kubectl taint nodes node1 key2:NoSchedule-
- 查看 node1 上的 taint:
kubectl describe nodes node1
kubectl get node node1 -o yaml
kubectl edit node node1
1.2. 容忍(Toleration)
您可以在 PodSpec 中定义 Pod 的容忍。
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
容忍中
key/value和 污点的key/value对应。容忍中
effect和污点的effect对应。容忍中
tolerationSeconds表示pod在运行时节点配置了NoExecute污点,该 pod 还能继续运行的时长。
通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute 的污点,
则任何不能忍受这个污点的 Pod 都会马上被驱逐,
任何可以忍受这个污点的 Pod 都不会被驱逐。
但是,如果 Pod 存在一个 effect 值为 NoExecute 的容忍度指定了可选属性
tolerationSeconds 的值,则表示在给节点添加了上述污点之后,
Pod 还能继续在节点上运行的时间。
例如 tolerationSeconds: 3600。这表示如果这个 Pod 正在运行,同时一个不匹配的污点被添加到其所在的节点,
那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。
如果在此之前上述污点被删除了,则 Pod 不会被驱逐。
operator的是匹配策略
一个容忍和一个污点相“匹配”是指它们有一样的键名和效果,并且:
- 如果
operator是Exists,则只要key存在即可,此时容忍度不能指定value - 如果
operator是Equal,则它们的value应该相等
默认值是 Equal。
存在两种特殊情况:
如果一个容忍度的
key为空且 operator 为Exists, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意 taint。如果
effect为空,则可以与所有键名key的效果相匹配。
1.3. 容忍匹配策略
您可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:
- 如果未被过滤的污点中存在至少一个 effect 值为
NoSchedule的污点, 则 Kubernetes 不会将 Pod 分配到该节点。 - 如果未被过滤的污点中不存在 effect 值为
NoSchedule的污点, 但是存在 effect 值为PreferNoSchedule的污点, 则 Kubernetes 会 尝试 将 Pod 分配到该节点。 - 如果未被过滤的污点中存在至少一个 effect 值为
NoExecute的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。
1.4. 应用场景
通过污点和容忍度,可以灵活地让 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 中配置的在节点出现问题时的驱逐行为,接下来的章节会描述这个特性。
1.5. 基于污点的驱逐
前文提到过污点的 effect 值 NoExecute会影响已经在节点上运行的 Pod
- 如果 Pod 不能忍受 effect 值为
NoExecute的污点,那么 Pod 将马上被驱逐 - 如果 Pod 能够忍受 effect 值为
NoExecute的污点,但是在容忍度定义中没有指定tolerationSeconds,则 Pod 还会一直在这个节点上运行。 - 如果 Pod 能够忍受 effect 值为
NoExecute的污点,而且指定了tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。
当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态Ready的值为 "False"。node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态Ready的值为 "Unknown"。node.kubernetes.io/out-of-disk:节点磁盘耗尽。node.kubernetes.io/memory-pressure:节点存在内存压力。node.kubernetes.io/disk-pressure:节点存在磁盘压力。node.kubernetes.io/network-unavailable:节点网络不可用。node.kubernetes.io/unschedulable: 节点不可调度。node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。
在节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute 效应的相关污点。
如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。
为了保证由于节点问题引起的 Pod 驱逐 速率限制行为正常, 系统实际上会以限定速率的方式添加污点。在像主控节点与工作节点间通信中断等场景下, 这样做可以避免 Pod 被大量驱逐。
使用这个功能特性,结合 tolerationSeconds,Pod 就可以指定当节点出现一个
或全部上述问题时还将在这个节点上运行多长的时间。
比如,一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段较长的时间, 愿意等待网络恢复以避免被驱逐。在这种情况下,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/unreachablenode.kubernetes.io/not-ready
这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。
1.6. 基于节点状态添加污点
Node 生命周期控制器会自动创建与 Node 条件相对应的带有 NoSchedule 效应的污点。
同样,调度器不检查节点条件,而是检查节点污点。这确保了节点条件不会影响调度到节点上的内容。
用户可以通过添加适当的 Pod 容忍度来选择忽略某些 Node 的问题(表示为 Node 的调度条件)。
DaemonSet 控制器自动为所有守护进程添加如下 NoSchedule 容忍度以防 DaemonSet 崩溃:
node.kubernetes.io/memory-pressurenode.kubernetes.io/disk-pressurenode.kubernetes.io/out-of-disk(只适合关键 Pod)node.kubernetes.io/unschedulable(1.10 或更高版本)node.kubernetes.io/network-unavailable(只适合主机网络配置)
添加上述容忍度确保了向后兼容,您也可以选择自由向 DaemonSet 添加容忍度。
1.7. 示例
1.7.1. 多污点多容忍
例如,假设您给一个节点添加了如下污点
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 不会被分配到上述节点,因为其没有容忍度和第三个污点相匹配。 但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。
1.7.2. 为 pod 设置 toleration
只要在 pod 的 spec 中设置 tolerations 字段即可,可以有多个 key,如下所示:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
value的值可以为NoSchedule、PreferNoSchedule或NoExecute。tolerationSeconds是当 pod 需要被驱逐时,可以继续在 node 上运行的时间。
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"
1.8. 参考
- Taints and Tolerations - kuberentes.io
- 阅读资源耗尽的处理,以及如何配置其行为
- 阅读 Pod 优先级