污点排斥没有容忍对应效果的 Pod,容忍度只移除这项障碍,不会强制把负载送到专用硬件。专用节点设计通常还需要标签与必要的节点亲和,再分别检查容量和准入策略。不能把通行资格理解成目的地保证,也不能把容忍度当成工作负载的完整权限边界。
给专用者的标记
NoSchedule 污点在未被容忍时,阻止普通的新调度,但不移动已有 Pod。这可以把 GPU 等专用节点留给特定负载。不能进一步概括为所有污点都不影响运行中程序:NoExecute 具有驱逐行为,PreferNoSchedule 则只是调度偏好。首先看清 effect,才能判断这次变更会影响新放置还是已有实例。
污点不是安全隔离。有权提交合适容忍度的主体可能获得池子资格,直接指定 nodeName 还会绕过普通调度检查。准入限制、授权和设备资源请求是另外的控制;一个 GPU 容忍度既没有请求 GPU,也不保证有空闲 GPU。审阅专用池时,需要同时核对谁能修改模板、谁能添加容忍度以及真正需要哪些硬件资源。
效果决定行为
NoSchedule 对未容忍的 Pod 构成硬性调度排除;PreferNoSchedule 提供软性避让偏好,单靠它不会让节点完全失去资格。NoExecute 还可能移除已经绑定、却没有匹配容忍度的 Pod。匹配 NoExecute 的容忍度可以带 tolerationSeconds,限定污点出现之后还能停留多久;不写时长与设置短时长具有不同后果。
执行基于污点驱逐的是相应驱逐控制器,不是调度器把正在运行的进程搬走。这不等同于 kubectl drain,中断预算处理与操作顺序并不相同。维护应选择适合的流程,先评估受影响负载,不要把 NoExecute 当成有同样保护栏的快速排空按钮。容忍过期引发的延迟动作同样需要纳入维护时间线。
配对即约定
本例演示 Equal 和 Exists 两种运算符,匹配由键、运算符、值及效果共同决定。Equal 比较值,Exists 忽略指定键的值;空 effect 覆盖所有效果。合法的空 key 需要 Exists,并把匹配扩展到所有键。应校验 API 格式,不能让不合法输入在本地模型里悄悄变成通配符;未来或其他版本的运算符也不在本例范围内。
匹配完成之后,所有剩余硬性污点都必须被容忍,这项调度过滤才通过。但资源、亲和、卷和其他调度插件仍可以拒绝节点。对专用节点标签设置必要亲和,才补上容忍度刻意不提供的目的地限制。许可进入与要求只能进入,是两条不同契约,缺少后一条时专用负载完全可能落到通用节点。
坏掉的样子
最贵的故障是过宽的容忍度污染了专用池。一条写着 operator: Exists 却不带值的容忍度,或一份被复制进公共图表的容忍度,会匹配该键下的所有取值,于是普通发布突然有资格落到图形处理器或竞价节点上。预留池被廉价负载占满,而它真正要服务的专用负载反而长期等待调度。这恰好与守护进程集的故障互为镜像:那边是缺了容忍度导致专用节点没有覆盖,这边是多了容忍度导致专用节点失去保护。应审计宽泛容忍度,但它不是 RBAC 授权;公共模板里必须写清键、值与效果。
第二种故障是把本该用 NoSchedule 的地方写成了 NoExecute。前者只拦截新负载,后者会把没有匹配容忍度的运行中负载直接驱离,还可能在 tolerationSeconds 倒计时结束后动手。一个维护污点若选错效果,就可能意外驱逐生产负载,而从示例里顺手复制的六十秒容忍倒计时,会把一次预留变成一场无人排期的延迟驱逐。与标准 IgnoredDuringExecution 节点亲和不同,污点侧的效果字段自带破坏力,因此维护污点先在单台节点演练,看过负载分布再向整池推广。
第三种故障是污点与容忍度之间的拼写漂移。节点上的 dedicated=gpu:NoSchedule 永远遇不上清单里的 dedicated=GPU:NoSchedule,写着 PreferNoSchedule 的容忍度也覆盖不了 NoSchedule 的污点。专用负载于是长期等待调度,事件里写着存在污点但负载未能容忍,而池子表面一切健康。请用查看负载详情的输出对照查看节点污点的输出,逐字比对两侧,再把规范的三元组收敛到同一份文档里,避免口头约定慢慢走样。
快速验证
把下面的代码存为污点容忍模拟脚本并运行。它只用标准库,不触碰任何集群,复刻上文四条匹配规则:按键匹配,按运算符与取值匹配,按效果匹配,软偏好永不拦截。
def tolerates(taint, tol):
"""一条容忍度按键、取值、运算符与效果匹配一条污点。"""
operator = tol.get("operator", "Equal")
if operator not in {"Equal", "Exists"}:
raise ValueError("model supports only Equal and Exists")
if not tol.get("key") and operator != "Exists":
raise ValueError("empty key requires Exists")
if operator == "Exists" and tol.get("value"):
raise ValueError("Exists requires an empty value")
if tol.get("key") and tol["key"] != taint["key"]:
return False
if tol.get("operator", "Equal") == "Equal":
if tol.get("value") != taint.get("value"):
return False
effect = tol.get("effect", "")
if effect and effect != taint.get("effect"):
return False
return True
def schedulable(taints, tolerations):
"""只有硬性污点全部被容忍时负载才允许调度。"""
for taint in taints:
if taint["effect"] == "PreferNoSchedule":
continue
if not any(tolerates(taint, tol) for tol in tolerations):
return False
return True
def check(actual, expected, message):
if actual != expected:
raise AssertionError(f"{message}:实际 {actual},期望 {expected}")
print(f"OK: {message} -> {actual}")
def main():
gpu = {"key": "dedicated", "value": "gpu", "effect": "NoSchedule"}
exact = {"key": "dedicated", "value": "gpu", "effect": "NoSchedule"}
check(schedulable([gpu], [exact]), True, "精确匹配允许调度")
check(schedulable([gpu], []), False, "缺失容忍应当拒绝")
wide = {"key": "dedicated", "operator": "Exists", "effect": "NoSchedule"}
other = {"key": "dedicated", "value": "spot", "effect": "NoSchedule"}
check(schedulable([other], [wide]), True, "存在运算符匹配任意取值")
check(schedulable([gpu], [{"key": "dedicated", "value": "spot", "effect": "NoSchedule"}]), False, "错误取值应当拒绝")
evict = {"key": "maintenance", "value": "drain", "effect": "NoExecute"}
any_effect = {"key": "maintenance", "value": "drain"}
check(schedulable([evict], [any_effect]), True, "空效果覆盖任意效果")
check(schedulable([evict], []), False, "未容忍的 NoExecute 不通过调度过滤")
soft = {"key": "cost", "value": "cheap", "effect": "PreferNoSchedule"}
check(schedulable([soft], []), True, "软偏好永不拦截")
print("PASS: 7 scenarios; allow and repel verified")
if __name__ == "__main__":
main()
七个场景锁定关键行为:精确匹配证明放行成立,缺失容忍证明拒绝成立,存在运算符匹配任意取值但错误取值仍被挡回,空效果覆盖驱逐型污点,未容忍的 NoExecute 不通过该过滤,但模型没有执行真实驱逐,而软偏好永不拦截。预期输出以通过信息结尾,说明七组场景成立。
本轮在 Linux、Python 3.12.14 上重新运行两种语言的实验,并额外验证不合法容忍度、空键与效果错配反例。它只验证匹配逻辑,不验证容量亲和与真实污点。上线前请对照负载事件与节点实际污点列表复核,再改生产配置。
延伸阅读
来源
- Kubernetes 污点与容忍,说明效果与调度,已核验 2026-10-15。
- Kubernetes 节点亲和,说明按节点标签匹配负载,已核验 2026-10-15。