Kubernetes中的污点和容忍度
污点和容忍度
节点亲和性是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点(这可能处于一种偏好,也可能是硬性要求)。
污点(Taint)则相反,它使节点能够排斥一类特定的 Pod。
容忍度(Toleration)是应用于 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-
我们可以在 Pod 规约中为 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,则它们的值应该相等。
上述例子中 effect 使用的值为 NoSchedule,你也可以使用另外一个值 PreferNoSchedule。
effect 字段的允许值包括:
- NoExecute:这会影响在节点上运行的 Pod,具体影响如下--如果 Pod 不能容忍这类污点,会马上被驱逐。如果 Pod 能够容忍这类污点,但是在容忍度定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个节点上运行。如果 Pod 能够容忍这类污点,而且指定了tolerationSeconds,则 Pod 还能在这个节点上继续运行这个指定的时间长度。这段时间过去后,节点生命周期控制器从这个驱逐这些 Pod。
- NoSchedule:除非具有匹配的容忍度规约,否则新的 Pod 不会被调度到带有污点的节点上。当前正在节点上运行的 Pod 不会被驱逐。
- PreferNoScheduler:它是"偏好"或"软性"的 NoScheduler。控制平面将尝试避免将不能容忍污点的 Pod 调度到节点上,但不能报证完全避免。
我们可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历,过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。需要注意以下情况:
- 如果未被忽略的污点中存在至少一个 effect 值为 NoSchedule 的污点,则 Kubernetes 不会将 Pod 调度到该节点
- 如果未被忽略的污点中不存在 effect 值为 NoSchedule 的污点,但是存在至少一个 effect 值为 PreferNoSchedule 的污点,则 Kubernetes 会尝试不将 Pod 调度到该节点。
- 如果未被忽略的污点中存在至少一个 effect 值为 NoExecute 的污点,则 Kubernetes 不会将 Pod 调度到该节点(如果 Pod 还未在节点上运行),并且会将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。
使用场景
通过污点和容忍度,可以灵活地让 Pod 避开某些节点或者将 Pod 从某些节点驱逐。下面是几个使用例子:
- 专用节点:如果想将某些节点专门分配给特定的一组用户使用,我们可以给这些节点添加一个污点(即,kubectl taint nodes nodename dedicated=groupName:NoSchedule),然后给这组用户的 Pod 添加一个相对应的容忍度(通过编写一个自定义的准入控制器,很容器就能做到)。拥有上述容忍度的 Pod 就能够被调度到上述专用节点,同时也能被调度到集群中的其他节点。如果我们希望这些 Pod 只能被调度到上述专业节点,那么我们还需要给这些专业节点添加一个和上述污点类似的 label (例如:dedicated=groupName),同时还要在上述准入控制器中给 Pod 增加亲和性要求,要求上述 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 中配置的在节点出现问题时的驱逐行为。
基于污点的驱逐
但某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
- 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 所设置的容忍度是这样的:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
DaemonSet 中的 Pod 被创建时,针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds:
- node.kubernetes.io/unreable
- node.kubernetes.io/not-ready
这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐
基于节点状态添加污点
控制平面通过节点控制器,会针对特定的节点状况自动添加具有 NoSchedule 效果的污点。
调度器在进行调度决策时,只检查污点而非直接关注节点状态。这一机制确保了节点状况不会直接影响调度流程。例如:当 DiskPressure(磁盘压力)节点状况激活时,控制平面会添加 node.kubernetes.io/disk-pressure 污点,从而阻止新的 Pod 被调度到受影响的节点上。当MemoryPressure(内存压力)节点状况激活时,控制平面会添加 node.kubernetes.io/memory-pressure 污点。
我们可以通过为新建的 Pod 添加相应的容忍度来忽略这些节点的状况。此外,控制平面会自动会 Qos 等级非 BestEffort 的 Pod 添加 node.kubernetes.io/memory-pressure 容忍度。这是因为 Kubernetes 将 Guaranteed 或 Burstable Qos 类别的 Pod(即使未设置内存请求)视为能够应对内存压力,而新的 BestEffort Pod 则不会被调度到受影响的节点上。
DaemonSet 控制器自动为所有守护进程添加如下 NoSchedule 容忍度,以防 DaemonSet 崩溃:
- node.kubernetes.io/memory-pressure
- node.kubernetes.io/disk-pressure
- node.kubernetes.io/pid-pressure
- node.kubernetes.io/unschedulable
- node.kubernetes.io/network-unavailable(只适合主机网络)
设备污点与容忍度
在使用动态资源分配管理特殊硬件的集群中, 管理员可以选择为单个设备设置污点, 而不是为整个节点打污点。这样做的好处是,污点可以精确地作用于出现故障或需要维护的硬件。 同时也支持容忍度配置,并且可以在请求设备时指定。 与污点类似,容忍度会应用于共享同一分配设备的所有 Pod。
更多推荐


所有评论(0)