Kubernetes v1.35:扩展容忍度运算符以支持数值比较(Alpha)

许多生产级 Kubernetes 集群会混合使用按需(on-demand,高 SLA)节点与 spot/可抢占(preemptible,低 SLA)节点, 以在保证关键工作负载可靠性的同时优化成本。平台团队需要一个“安全默认值”,让大多数工作负载远离风险容量, 同时又允许特定工作负载用明确阈值显式选择接受(opt-in),例如“我可以容忍失败概率最高 5% 的节点”。

目前,Kubernetes 的污点与容忍度(taints and tolerations)可以匹配精确值或检查键是否存在, 但无法进行数值阈值比较。你不得不创建离散的污点类别、使用外部准入控制器,或接受不够理想的放置决策。

在 Kubernetes v1.35 中,我们以 Alpha 形式引入 扩展容忍度运算符(Extended Toleration Operators)。 该增强为 spec.tolerations 增加 Gt(Greater Than)与 Lt(Less Than)运算符, 使调度器能够进行基于阈值的调度决策,从而为基于 SLA 的放置、成本优化以及面向性能的工作负载分发打开新可能。

容忍度的演进

从历史上看,Kubernetes 主要支持两种容忍度运算符:

  • Equal:当 key 与 value 完全相等时,容忍度匹配该污点
  • Exists:只要 key 存在(无论 value 是什么),容忍度就匹配该污点

这两者对“类别型”场景很好用,但在数值比较方面就显得力不从心。从 v1.35 开始,我们在补齐这一缺口。

请看一些真实世界的场景:

  • SLA 要求:只把高可用工作负载调度到失败概率低于某个阈值的节点上
  • 成本优化:允许对成本敏感的批处理作业运行在更便宜、且“每小时成本”超过某个特定值的节点上
  • 性能保障:确保对延迟敏感的应用只运行在磁盘 IOPS 或网络带宽高于最低阈值的节点上

在缺少数值比较运算符的情况下,集群运维人员不得不采用一些变通方案,例如创建多个离散的污点值, 或使用外部准入控制器。但这些方案既难以规模化,也无法提供“动态阈值调度”所需的灵活性。

为什么要扩展容忍度,而不是用节点亲和性(NodeAffinity)?

你可能会问:NodeAffinity 已经支持数值比较运算符,为什么还要扩展容忍度? NodeAffinity 虽然很适合表达 Pod 的偏好,但污点与容忍度提供了一些关键的运维收益:

  • 策略导向:NodeAffinity 是按 Pod 配置的,需要每个工作负载显式选择“避开”风险节点。 污点则把控制反转:由节点声明风险等级,只有带有匹配容忍度的 Pod 才能落到这些节点上。 这提供了更安全的默认值:大多数 Pod 会默认避开 spot/可抢占节点,除非它们显式选择接受。
  • 驱逐语义:NodeAffinity 不具备驱逐能力。污点支持 NoExecute 效果以及 tolerationSeconds, 使运维人员可以在节点 SLA 降级或 spot 实例收到终止通知时,排空(drain)并驱逐 Pod。
  • 运维易用性:集中式、节点侧的策略与磁盘压力、内存压力等其他安全污点一致,让集群管理更直观。

该增强在保留污点与容忍度这一成熟安全模型的基础上,为 SLA 感知调度提供了基于阈值的放置能力。

引入 Gt 与 Lt 运算符

Kubernetes v1.35 为容忍度引入两个新运算符:

  • Gt(Greater Than):当污点的数值 小于 容忍度的数值时,容忍度匹配
  • Lt(Less Than):当污点的数值 大于 容忍度的数值时,容忍度匹配

当一个 Pod 使用 Lt 来容忍某个污点时,它表达的是:“我可以容忍该指标小于我的阈值的节点”。 由于“容忍度”本质上允许调度,因此该 Pod 也可以运行在污点值 大于 容忍度值的节点上。 你可以把它理解为:“我容忍满足我最低要求之上的节点”。

这些运算符适用于数值型污点值,使调度器能基于连续指标(continuous metrics)而不是离散类别做出更精细的放置决策。

使用场景与示例

下面我们通过几个例子看看扩展容忍度运算符如何解决真实调度挑战。

示例 1:用 SLA 阈值限制 spot 实例的使用

许多集群会混合按需与 spot/可抢占节点以优化成本。Spot 节点能显著节省费用,但失败率更高。 你希望大多数工作负载默认避开 spot 节点,同时允许某些工作负载在清晰的 SLA 边界内显式选择接受。

首先,用“失败概率”给 spot 节点打上污点(例如:年化失败率 15%):

apiVersion: v1
kind: Node
metadata:
  name: spot-node-1
spec:
  taints:
  - key: "failure-probability"
    value: "15"
    effect: "NoExecute"

按需节点的失败率要低得多:

apiVersion: v1
kind: Node
metadata:
  name: ondemand-node-1
spec:
  taints:
  - key: "failure-probability"
    value: "2"
    effect: "NoExecute"

关键工作负载可以指定严格的 SLA 要求:

apiVersion: v1
kind: Pod
metadata:
  name: payment-processor
spec:
  tolerations:
  - key: "failure-probability"
    operator: "Lt"
    value: "5"
    effect: "NoExecute"
    tolerationSeconds: 30
  containers:
  - name: app
    image: payment-app:v1

这个 Pod 将只会被调度到 failure-probability 小于 5 的节点上(也就是 2% 的 ondemand-node-1, 而不是 15% 的 spot-node-1)。带有 tolerationSeconds: 30NoExecute 效果意味着: 如果节点 SLA 降级(例如云厂商改变了污点值),该 Pod 会获得 30 秒的时间用于优雅终止,然后才会被强制驱逐。

与此同时,一个具备容错能力的批处理作业可以显式选择接受 spot 实例:

apiVersion: v1
kind: Pod
metadata:
  name: batch-job
spec:
  tolerations:
  - key: "failure-probability"
    operator: "Lt"
    value: "20"
    effect: "NoExecute"
  containers:
  - name: worker
    image: batch-worker:v1

该批处理作业可容忍失败概率最高 20% 的节点,因此既能运行在按需节点上,也能运行在 spot 节点上, 在接受更高风险的同时最大化节省成本。

示例 2:基于 GPU 分层的 AI 工作负载放置

AI 与机器学习工作负载通常对硬件有明确要求。通过扩展容忍度运算符,你可以建立 GPU 节点分层, 并确保工作负载落到性能匹配的硬件上。

用“算力评分”给 GPU 节点打上污点:

apiVersion: v1
kind: Node
metadata:
  name: gpu-node-a100
spec:
  taints:
  - key: "gpu-compute-score"
    value: "1000"
    effect: "NoSchedule"
---
apiVersion: v1
kind: Node
metadata:
  name: gpu-node-t4
spec:
  taints:
  - key: "gpu-compute-score"
    value: "500"
    effect: "NoSchedule"

重训练(heavy training)工作负载可以要求更高性能的 GPU:

apiVersion: v1
kind: Pod
metadata:
  name: model-training
spec:
  tolerations:
  - key: "gpu-compute-score"
    operator: "Gt"
    value: "800"
    effect: "NoSchedule"
  containers:
  - name: trainer
    image: ml-trainer:v1
    resources:
      limits:
        nvidia.com/gpu: 1

这将确保训练 Pod 只会被调度到算力评分大于 800 的节点上(如 A100 节点),避免落到低档 GPU 上而拖慢训练。

而对性能要求没那么高的推理工作负载则可以使用任何可用 GPU:

apiVersion: v1
kind: Pod
metadata:
  name: model-inference
spec:
  tolerations:
  - key: "gpu-compute-score"
    operator: "Gt"
    value: "400"
    effect: "NoSchedule"
  containers:
  - name: inference
    image: ml-inference:v1
    resources:
      limits:
        nvidia.com/gpu: 1

示例 3:面向成本优化的工作负载放置

对于批处理或非关键工作负载,你可能希望即使牺牲一些性能特征,也通过运行在更便宜的节点上来尽量降低成本。

节点可以用成本评级来打污点:

spec:
  taints:
  - key: "cost-per-hour"
    value: "50"
    effect: "NoSchedule"

对成本敏感的批处理作业可以表达它对昂贵节点的容忍度:

tolerations:
- key: "cost-per-hour"
  operator: "Lt"
  value: "100"
  effect: "NoSchedule"

该批处理作业会被调度到成本低于 100 美元/小时的节点上,并避开更昂贵的节点。 结合 Kubernetes 的调度优先级能力,你可以实现更精细的成本分层策略:关键工作负载使用高配节点, 而批处理作业高效利用更经济的资源。

示例 4:基于性能的放置

存储密集型应用通常需要最低磁盘性能保障。通过扩展容忍度运算符,你可以在调度层面强制执行这些要求。

tolerations:
- key: "disk-iops"
  operator: "Gt"
  value: "3000"
  effect: "NoSchedule"

该容忍度确保 Pod 只会被调度到 disk-iops 超过 3000 的节点上。 Gt 运算符表达的是:“我需要指标高于这个最低值的节点”。

如何使用该特性

扩展容忍度运算符是 Kubernetes v1.35 中的 Alpha 特性。要试用它:

  1. 在 API server 与 scheduler 上启用特性门控

    --feature-gates=TaintTolerationComparisonOperators=true
    
  1. 用数值型污点给节点打标,其值代表你调度所关心的指标:

    kubectl taint nodes node-1 failure-probability=5:NoSchedule
    kubectl taint nodes node-2 disk-iops=5000:NoSchedule
    
  1. 在 Pod 规约中使用新运算符

      spec:
        tolerations:
        - key: "failure-probability"
          operator: "Lt"
          value: "1"
          effect: "NoSchedule"
    

下一步计划是什么?

这次 Alpha 发布只是开始。随着我们收集社区反馈,我们计划:

  • 在容忍度与节点亲和性(node affinity)中增加对 CEL(Common Expression Language)表达式 的支持,以提供更灵活的调度逻辑(包括语义化版本比较)
  • 改进与集群自动扩缩容(cluster autoscaling)的集成,以支持“阈值感知”的容量规划
  • 将该特性升级为 Beta,并最终达到具备生产级稳定性的 GA

我们尤其希望听到你的使用场景!你是否有一些问题可以通过“基于阈值的调度”来解决? 你还希望看到哪些额外运算符或能力?

参与其中

该特性由 SIG Scheduling 社区推动。 欢迎加入我们,与社区交流并分享你对该特性及其他相关议题的想法与反馈。

你可以通过以下方式联系该特性的维护者:

如果你对扩展容忍度运算符有疑问或具体咨询,请联系 SIG Scheduling 社区。我们期待你的反馈!

如何了解更多?