污点和容忍度
节点亲和性(Node affinity) 是 Pod 的一个属性,它使得 Pod 能够被一组 节点 吸引(可以是偏好,也可以是硬性要求)。 污点(Taints) 则与此相反——它们允许节点排斥一组 Pod。
容忍度(Tolerations) 应用于 Pod。容忍度允许调度器将带有匹配污点的 Pod 调度到相应的节点上。容忍度允许调度但不保证调度:调度器在其功能中还会评估其他参数。
污点和容忍度协同工作,确保 Pod 不被调度到不合适的节点上。一个或多个污点被应用于节点;这表示该节点不应接受任何不能容忍这些污点的 Pod。
概念
你可以使用 kubectl taint 命令为一个节点添加污点。例如,
kubectl taint nodes node1 key1=value1:NoSchedule
在节点 node1
上设置了一个污点。该污点具有键 key1
、值 value1
和污点效果 NoSchedule
。这意味着任何 Pod 都无法调度到 node1
上,除非它具有匹配的容忍度。
要移除上面命令添加的污点,你可以运行
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"
默认的 Kubernetes 调度器在选择一个节点来运行特定 Pod 时会考虑污点和容忍度。然而,如果你手动为 Pod 指定了 .spec.nodeName
,这个操作会绕过调度器;Pod 会被绑定到你指定的节点上,即使该节点上存在你选择的 NoSchedule
污点。如果发生这种情况并且该节点也设置了 NoExecute
污点,则 kubelet 将驱逐该 Pod,除非设置了适当的容忍度。
下面是一个定义了一些容忍度的 Pod 的示例:
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"
operator
的默认值是 Equal
。
如果污点和容忍度的键和效果相同,则容忍度“匹配”污点,并且
operator
是Exists
(此时不应指定value
),或者operator
是Equal
且值相等。
注意
存在两种特殊情况
如果 key
为空,则 operator
必须是 Exists
,这将匹配所有键和值。请注意,effect
仍然需要同时匹配。
空的 effect
匹配所有带有键 key1
的效果。
上面的例子使用了 NoSchedule
效果。你也可以使用 PreferNoSchedule
效果。
effect
字段的允许值包括:
NoExecute
- 这会影响已经在节点上运行的 Pod,具体如下:
- 不能容忍该污点的 Pod 会立即被驱逐。
- 能容忍该污点但在容忍度规范中未指定
tolerationSeconds
的 Pod 将永远绑定在该节点上。 - 能容忍该污点并且指定了
tolerationSeconds
的 Pod 将在指定的时间内绑定在该节点上。时间到期后,节点生命周期控制器会将 Pod 从节点上驱逐。
NoSchedule
- 除非有匹配的容忍度,否则不会有新的 Pod 调度到带有污点的节点上。当前正在节点上运行的 Pod 不会被驱逐。
PreferNoSchedule
PreferNoSchedule
是NoSchedule
的“偏好”或“软性”版本。控制平面会尝试避免将不能容忍该污点的 Pod 调度到节点上,但并不能保证。
你可以在同一个节点上设置多个污点,也可以在同一个 Pod 上设置多个容忍度。Kubernetes 处理多个污点和容忍度的方式就像一个过滤器:从节点的所有污点开始,然后忽略那些 Pod 具有匹配容忍度的污点;剩余的、未被忽略的污点会对 Pod 产生相应的影响。特别地,
- 如果存在至少一个未被忽略的、效果为
NoSchedule
的污点,则 Kubernetes 不会将 Pod 调度到该节点上。 - 如果没有未被忽略的、效果为
NoSchedule
的污点,但存在至少一个未被忽略的、效果为PreferNoSchedule
的污点,则 Kubernetes 会尝试不将 Pod 调度到该节点上。 - 如果存在至少一个未被忽略的、效果为
NoExecute
的污点,则 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 无法调度到节点上,因为它没有与第三个污点匹配的容忍度。但是,如果该 Pod 在添加污点时已经在该节点上运行,它将能够继续运行,因为在这三个污点中,只有第三个污点是该 Pod 不能容忍的。
通常,如果一个效果为 NoExecute
的污点被添加到节点上,则任何不能容忍该污点的 Pod 将立即被驱逐,而能容忍该污点的 Pod 则永远不会被驱逐。但是,效果为 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 添加相应的容忍度(最简单的方法是编写一个自定义准入控制器)。具有容忍度的 Pod 将被允许使用带有污点的(专用)节点以及集群中的任何其他节点。如果你想将节点专用于他们并且确保他们只使用专用节点,则你应该额外为同一组节点添加一个类似于污点的标签(例如dedicated=groupName
),并且准入控制器应该额外添加一个节点亲和性,以要求 Pod 只能调度到带有dedicated=groupName
标签的节点上。带有特殊硬件的节点:在集群中,如果一小部分节点具有专用硬件(例如 GPU),最好让不需要专用硬件的 Pod 不调度到这些节点上,从而为稍后需要专用硬件的 Pod 留出空间。可以通过为具有专用硬件的节点添加污点(例如
kubectl taint nodes nodename special=true:NoSchedule
或kubectl taint nodes nodename special=true:PreferNoSchedule
),并为使用特殊硬件的 Pod 添加相应的容忍度来做到这一点。就像专用节点用例一样,使用自定义准入控制器来应用容忍度可能是最简单的方法。例如,建议使用扩展资源来表示特殊硬件,使用扩展资源名称标记你的特殊硬件节点,并运行 ExtendedResourceToleration 准入控制器。现在,由于节点被标记了污点,没有容忍度的 Pod 不会被调度到上面。但是当你提交一个请求扩展资源的 Pod 时,ExtendedResourceToleration
准入控制器会自动为该 Pod 添加正确的容忍度,然后该 Pod 将被调度到特殊硬件节点上。这将确保这些特殊硬件节点专门用于请求此类硬件的 Pod,并且你无需手动为你的 Pod 添加容忍度。基于污点的驱逐:当节点出现问题时,可以根据 Pod 进行配置的驱逐行为,这将在下一节中介绍。
基于污点的驱逐
Kubernetes v1.18 [stable]
节点控制器在某些条件为真时会自动为节点添加污点。以下污点是内置的:
node.kubernetes.io/not-ready
:节点未就绪。这对应于 NodeConditionReady
的状态为 "False
"。node.kubernetes.io/unreachable
:节点控制器无法触及该节点。这对应于 NodeConditionReady
的状态为 "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 使用“外部”云提供商启动时,此污点会设置在节点上以将其标记为不可用。云控制器管理器中的一个控制器初始化该节点后,kubelet 会移除此污点。
如果节点需要被驱逐(drained),节点控制器或 kubelet 会添加具有 NoExecute
效果的相关污点。对于 node.kubernetes.io/not-ready
和 node.kubernetes.io/unreachable
污点,默认会添加此效果。如果故障条件恢复正常,kubelet 或节点控制器可以移除相关的污点。
在某些情况下,当节点不可达时,API Server 无法与节点上的 kubelet 进行通信。在与 API Server 重新建立通信之前,删除 Pod 的决定无法传达给 kubelet。在此期间,预定要删除的 Pod 可能会继续在分区节点上运行。
注意
控制平面限制了为节点添加新污点的速率。这种速率限制机制控制了当许多节点同时变得不可达(例如:网络中断)时触发的驱逐数量。你可以为 Pod 指定 tolerationSeconds
来定义该 Pod 在发生故障或无响应的节点上保持绑定多久。
例如,你可能希望在网络分区的情况下,让一个拥有大量本地状态的应用长时间绑定在节点上,希望分区能够恢复,从而避免 Pod 驱逐。你为该 Pod 设置的容忍度可能看起来像:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
注意
Kubernetes 会自动为 node.kubernetes.io/not-ready
和 node.kubernetes.io/unreachable
添加一个带有 tolerationSeconds=300
的容忍度,除非你或控制器明确设置了这些容忍度。
这些自动添加的容忍度意味着在检测到这些问题中的任何一个后,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 的节点状况。控制平面也会为 QoS 类别不是 BestEffort
的 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
(仅限宿主网络)
添加这些容忍度是为了确保向后兼容。你还可以向 DaemonSet 添加任意容忍度。
设备污点和容忍度
管理员不仅可以给整个节点添加污点,当集群使用动态资源分配来管理特殊硬件时,也可以给单个设备添加污点。这样做的好处是污点可以精确地针对故障或需要维护的硬件。容忍度也受支持,并在请求设备时指定。与污点类似,它们适用于共享同一已分配设备的所有 Pod。