污点和容忍度
节点亲和性是 Pod 的一个属性,用于将它们吸引到一组节点(作为偏好或硬性要求)。 污点则相反,它们允许节点排斥一组 Pod。
容忍度应用于 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
的 effect
。 或者,你可以使用 PreferNoSchedule
的 effect
。
effect
字段的允许值是
NoExecute
- 这会按如下方式影响已经在节点上运行的 Pod:
- 不容忍污点的 Pod 会立即被驱逐
- 容忍污点但未在其容忍度规范中指定
tolerationSeconds
的 Pod 将永远保持绑定 - 容忍污点且指定了
tolerationSeconds
的 Pod 将在指定的时间内保持绑定。 过了那个时间,节点生命周期控制器会从节点中驱逐 Pod。
NoSchedule
- 除非新 Pod 具有匹配的容忍度,否则不会在被污点的节点上调度新 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 容忍的污点。
通常,如果将具有效果 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 使用“外部”云提供商启动时,会在节点上设置此污点以将其标记为不可用。 在来自 cloud-controller-manager 的控制器初始化此节点后,kubelet 将删除此污点。
如果需要排空节点,节点控制器或 kubelet 会添加带有 NoExecute
效果的相关污点。对于 node.kubernetes.io/not-ready
和 node.kubernetes.io/unreachable
污点,默认会添加此效果。如果故障条件恢复正常,kubelet 或节点控制器可以移除相关的污点。
在某些情况下,当节点不可达时,API 服务器无法与节点上的 kubelet 通信。在与 API 服务器重新建立通信之前,无法将删除 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 的节点条件。控制平面还在具有除 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
(仅主机网络)
添加这些容忍度可确保向后兼容性。你还可以向 DaemonSet 添加任意容忍度。