污点和容忍度
节点亲和性 是 Pod 的一种属性,它可以将 Pod 吸引 到一组 节点(作为偏好或硬性要求)。污点 则相反——它们允许节点排斥一组 Pod。
容忍度 应用于 Pod。容忍度允许调度器调度具有匹配污点的 Pod。容忍度允许调度,但不能保证调度:调度器还会 评估其他参数 作为其功能的一部分。
污点和容忍度协同工作,以确保 Pod 不会被调度到不合适的节点。一个或多个污点应用于一个节点;这标志着该节点不应接受任何不 tolerates 污点的 Pod。
概念
您可以使用 kubectl taint 向节点添加污点。例如:
kubectl taint nodes node1 key1=value1:NoSchedule
在节点 node1
上放置一个污点。该污点的键为 key1
,值为 value1
,污点效果为 NoSchedule
。这意味着除非 Pod 具有匹配的容忍度,否则任何 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
且值应该相等。
注意
有两种特殊情况
具有运算符 Exists
的空 key
匹配所有键、值和效果,这意味着这将容忍所有内容。
空 effect
匹配键为 key1
的所有效果。
上面的示例使用了 NoSchedule
的 effect
。或者,您可以使用 PreferNoSchedule
的 effect
。
effect
字段的允许值为
NoExecute
- 这对已经在节点上运行的 Pod 有以下影响
- 不 tolerates 污点的 Pod 会立即被驱逐
- tolerates 污点但未在其容忍度规范中指定
tolerationSeconds
的 Pod 将永远保持绑定状态 - tolerates 污点并指定了
tolerationSeconds
的 Pod 将保持绑定状态指定的时间。在该时间过后,节点生命周期控制器会将 Pod 从节点中驱逐。
NoSchedule
- 除非新 Pod 具有匹配的容忍度,否则不会将它们调度到受污染的节点。当前在节点上运行的 Pod 不会 被驱逐。
PreferNoSchedule
PreferNoSchedule
是NoSchedule
的“偏好”或“软”版本。控制平面将 尝试 避免将不 tolerates 污点的 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 不 tolerates 的三个污点中唯一的一个。
通常,如果将效果为 NoExecute
的污点添加到节点,则任何不 tolerates 该污点的 Pod 将立即被驱逐,而 tolerates 该污点的 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 [稳定]
当某些条件为真时,节点控制器会自动污染节点。以下污点是内置的
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 将删除此污点。
如果要排空节点,节点控制器或 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 的节点条件。控制平面还会在 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 添加任意容忍度。