污点和容忍度
节点亲和性 是 Pod 的属性,可以将其吸引到一组 节点(作为首选或硬性要求)。污点则相反,它允许节点排斥一组 Pod。
容忍应用于 Pod。容忍允许调度器将具有匹配污点的 Pod 调度到节点上。容忍允许调度,但不保证调度:调度器还会评估其他参数,作为其功能的一部分。
污点和容忍一起工作以确保 Pod 不被调度到不合适的节点上。一个或多个污点被应用于一个节点;这表示节点不应接受任何不容忍污点的 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,如下所示:
- 不容忍污点的 Pod 会立即被弹出
- 容忍污点的 Pod 在其容忍规范中未指定
tolerationSeconds
的 Pod 将永远保持绑定状态 - 容忍污点的 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 已经在节点上运行,那么它将能够继续运行,因为第三个污点是三个污点中唯一一个不被 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 [稳定]
节点控制器在满足某些条件时会自动给节点打上污点。以下污点是内置的
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
效果的相关污点。对于 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 添加任意容忍度。