深入理解Kubernetes的容忍
- 2026-03-28 20:02:00
- 丁国栋
- 原创 8
理解 Kubernetes 的容忍
Tolerations(/ˌtɒləˈreɪʃənz/)是 Kubernetes 中一个非常核心且容易混淆的概念。它并不是指“宽容”这种抽象品质,而是一个专有名词,特指 Pod 在调度时能够“忍受”或“允许”被调度到哪些带有“污点”(Taints)的节点上。
简单来说,它是 Pod 的“抗污能力”配置。
一、核心定义与作用
在 Kubernetes 的调度机制中,Node(节点)可以被打上“污点”(Taint),就像给节点贴上一个“危险”或“专用”的标签(例如:node-type=production:NoSchedule),这样没有特殊配置的 Pod 就无法调度上去。
Tolerations 就是 Pod 身上携带的“通行证”,它告诉调度器:“我允许(Tolerate)被调度到带有这些污点的节点上”。
- 没有 Toleration:Pod 无法调度到有 Taint 的节点。
- 有匹配的 Toleration:Pod 可以(但不一定)调度到该节点(还需考虑其他资源等因素)。
二、关键属性解析
一个标准的 Toleration 在 YAML 中通常包含以下三个核心字段:
tolerations:
- key: "node-type" # 污点的键(必须匹配节点污点的key)
operator: "Equal" # 操作符(Equal 或 Exists)
value: "production" # 污点的值(当 operator=Equal 时需指定)
effect: "NoSchedule" # 污点的效果(必须匹配)
1. operator(操作符)
这是最容易出错的地方,它决定了匹配规则:
Equal:精确匹配。Pod 的key和value必须与 Node Taint 的key和value完全相等。Exists:存在匹配。只要 Node 有key这个 Taint(无论value是什么),Pod 就能容忍。此时不需要指定value字段。
2. effect(效果)
必须与 Node Taint 的效果一致,否则容忍无效。常见效果:
NoSchedule:仅允许调度,不影响已运行的 Pod。PreferNoSchedule:软限制,调度器尽量不调度,但非强制。NoExecute:最严格,不仅不能调度,如果节点上的已有 Pod 没有此容忍,还会被驱逐(Evict)。
三、典型使用场景
场景 1:专用节点(GPU/高性能)
给 GPU 节点打上污点,只有需要 GPU 的 Pod(配置了 Toleration)才能调度上去。
# 节点命令(给节点打污点)
kubectl taint nodes node1 gpu=true:NoSchedule
# Pod 配置(声明容忍)
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
场景 2:主节点(Master)调度
Kubernetes 默认给 Master 节点打污点,防止用户 Pod 调度上去干扰系统组件。系统组件(如 CoreDNS)自带对应的 Toleration。
# 查看 Master 污点
kubectl describe node master | grep Taint
# Taints: node-role.kubernetes.io/master:NoSchedule
# 系统 Pod 的容忍配置
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
场景 3:故障节点维护(NoExecute)
当节点需要维护时,可以打上 NoExecute 污点,该节点上所有没有对应容忍的 Pod 会被立即驱逐。
# 驱逐节点上所有 Pod
kubectl taint nodes node1 maintenance=true:NoExecute
四、常见误区与注意事项
-
Toleration 不是 Affinity(亲和性):
- Toleration:是“允许去”(Permission)。
- Affinity:是“想去”(Preference)。
- 一个 Pod 能调度到某节点,必须同时满足:节点资源足够 + Pod 容忍节点污点 + (节点亲和性规则)。
-
operator: Exists的危险性: 使用Exists且不指定key是非常危险的操作,因为它会容忍所有的污点(包括NoExecute),可能导致 Pod 被调度到不合适的节点或无法被驱逐。 -
空键(Empty Key)的特殊情况: 如果 Toleration 的
key为空且operator为Exists,它会匹配所有的污点键、值和效果。这通常用于 DaemonSet(如 kube-proxy)以确保它们能在所有节点上运行。
五、总结对比
| 概念 | 作用对象 | 功能 | 类比 |
|---|---|---|---|
| Taints(污点) | Node(节点) | 排斥 Pod:“此节点不欢迎你” | 节点上的“禁止入内”标志 |
| Tolerations(容忍) | Pod(容器组) | 抵抗排斥:“我有权限进入” | Pod 的“特别通行证” |
一句话定义: Tolerations 是 Pod 定义中的一组规则,用于声明该 Pod 能够容忍(即被调度到)哪些带有特定Taints的 Kubernetes 节点。
问:如果一个节点已经标记为禁止调度,但pod允许容忍污点,在其他节点有资源的情况下,这个节点是否依然会被调度?
这是一个非常经典的 Kubernetes 调度逻辑问题。结论非常明确:
不会。 在其他节点有资源的情况下,调度器绝对不会优先选择那个“被禁止(但有容忍)”的节点。
一、核心逻辑:容忍(Toleration)只是“入场券”,不是“邀请函”
需要严格区分两个概念:
-
容忍(Toleration):解决的是 “能不能” 的问题。
- 没有容忍,Pod 绝对进不去(被 Taint 挡在门外)。
- 有容忍,只是意味着这个节点被加入了“可选节点池”,解除了硬性禁止。
-
调度评分(Scoring):解决的是 “好不好” 的问题。
- 调度器默认的策略是优先选择资源最充足、最空闲的节点。
- 污点(Taint)本身并不加分。一个被打上污点的节点,在调度器眼中通常是“有问题的”或“专用的”,默认评分策略下,它绝不会比一个干净且资源充足的节点更有吸引力。
二、场景推演
假设集群状态如下:
- Node A(问题节点):已打污点
gpu=true:NoSchedule,资源充足。 - Node B(正常节点):无污点,资源充足。
- Pod:配置了容忍
key: gpu。
调度过程:
-
过滤(Filter)阶段:调度器检查所有节点。
- Node B:无污点,通过。
- Node A:有污点,但 Pod 有 Toleration,通过。
- ✅ 此时,Node A 和 Node B 都在候选列表中。
-
评分(Score)阶段:调度器对候选节点排序。
- Node B:标准、干净、无负担的节点 → 高分。
- Node A:带有“特殊标记”的节点 → 默认低分(除非有特殊亲和性)。
-
结果:调度器会选择分数最高的 Node B。Node A 只有在 Node B 资源不足(或通过亲和性强制指定)时才会被选为“备胎”。
三、如何强制调度到该节点?
如果希望 Pod 必须调度到那个有污点的节点(例如,那是唯一有 GPU 的机器),需要使用 节点亲和性(Node Affinity) 或 节点选择器(nodeSelector)。
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
containers:
- name: nginx
image: nginx
逻辑转变:
- 只有 Toleration:Pod 可以去任何地方(包括干净节点),只是不排斥那个脏节点。
- Toleration + NodeAffinity:Pod 必须去带有
gpu=true标签的节点,并且它能容忍该节点的污点。
四、总结
| 配置 | 效果 |
|---|---|
| 仅有 Taint | Pod 无法调度到该节点(被拒之门外) |
| Taint + Toleration | Pod 可以调度到该节点,但优先选择无污点的正常节点 |
| Taint + Toleration + NodeAffinity | Pod 必须调度到该节点(强制绑定) |
一句话回答:容忍只是解除了节点的“禁止访问”限制,并不会让该节点获得任何调度优先级。在资源充足的情况下,Kubernetes 调度器默认会优先使用最干净、最标准的节点。
又问:
如果Pod的容忍是:
tolerations:
- operator: Exists
是不是也会接受调度到标记为不可调度节点?
是的,完全正确。
operator: Exists 是 Kubernetes 容忍机制中最“宽泛”也最“危险”的配置。它意味着 Pod 对污点(Taint)完全不设防,会无条件接受调度到任何标记为不可调度的节点(包括 NoSchedule 和 NoExecute)。
一、为什么 Exists 如此“霸道”?
Kubernetes 的容忍匹配规则非常直接:
operator: Exists:只要节点上存在任意一个污点(Taint),无论这个污点的key是什么、value是什么、effect是什么(NoSchedule/NoExecute),这个 Pod 都会直接匹配。operator: Equal:需要精确匹配key和value,相对安全。
配置解析:
tolerations:
- operator: Exists
这行代码的逻辑是:“我不管节点上有什么脏东西(污点),我全都能忍(Tolerate)”。
二、对“不可调度”节点的具体影响
1. 针对 NoSchedule(禁止调度)
- 普通 Pod:被节点污点阻挡,无法调度。
- 你的 Pod:可以正常调度。因为
Exists容忍了NoSchedule效果,调度器视其为“允许进入”。
2. 针对 NoExecute(驱逐)
- 普通 Pod:如果节点被打上
NoExecute污点,会被立即驱逐。 - 你的 Pod:不会被驱逐。
Exists同样容忍了NoExecute效果,Pod 会像“钉子户”一样继续运行,即使节点正在维护。
三、严重风险警告
tolerations: - operator: Exists 在生产环境中是极其危险的配置,通常被视为“反模式”。
- 失去节点隔离:Master 节点、GPU 专用节点、故障节点上的污点全部失效,Pod 可能被调度到任何地方,破坏集群隔离策略。
- 无法驱逐:运维人员无法通过
kubectl taint ...:NoExecute的手段驱逐该 Pod,导致节点维护困难。 - DaemonSet 专用:这种配置通常只应出现在系统级 DaemonSet(如
kube-proxy、calico-node)中,因为它们需要确保在所有节点上运行。
四、正确做法:精准匹配
除非有绝对特殊的全集群穿透需求,否则永远不要使用裸奔式的 Exists。请使用精确匹配来限制 Pod 的容忍范围:
tolerations:
- key: "node-type" # 明确指定 key
operator: "Equal" # 使用 Equal 而非 Exists
value: "gpu" # 明确指定 value
effect: "NoSchedule" # 明确指定 effect
结论:这个配置不仅会接受调度到标记为不可调度的节点,甚至会接受调度到任何有污点的节点,包括正在被驱逐的节点。请立即检查并修正为精确匹配。