Kubernetes 集群通常混合部署不同架构与硬件能力的节点,例如 x86_64 通用计算节点、ARM 低功耗节点、配备 NVIDIA GPU 的训练节点以及带有 FPGA 的加速节点。如果调度器只根据 CPU 与内存请求做决策,就可能把需要 CUDA 驱动或特定指令集的容器调度到错误的工作节点上,导致容器启动失败、推理延迟升高,甚至造成专用硬件长期空闲。要解决这一问题,需要把节点按照硬件特征划入不同节点池,并在调度链路上叠加标签、污点、容忍、亲和性与拓扑分布约束,使不同类型的负载准确地落在合适的硬件上。

节点池分层模型设计
节点池的核心是把一组具有相同硬件属性或用途的节点抽象为一个逻辑池。例如可以创建 gpu-pool、arm-pool、edge-pool 等节点池,每个节点通过 Kubernetes 标签(Label)标记自己的池归属和硬件能力。常见的标签键包括 node-pool、accelerator、arch、zone 等,值则根据实际硬件填写。标签本身不强制调度,但它是后续所有调度策略的筛选依据。
在实际操作中,给节点打标签可以使用 kubectl label 命令,也可以在云厂商托管集群的节点池创建阶段自动注入。以下命令将 node-1 标记为 GPU 训练节点池,并声明其架构与加速卡型号:
kubectl label nodes node-1 node-pool=gpu arch=amd64 accelerator=nvidia-t4 kubectl label nodes node-2 node-pool=arm arch=arm64 kubectl get nodes --show-labels
节点池分层模型通常分为三层。第一层是集群层,负责全局资源视图与容量规划;第二层是节点池层,按硬件类型、网络位置或业务线划分逻辑边界;第三层是工作负载层,通过调度策略将 Pod 绑定到合适的池。这样的分层能降低调度器的搜索空间,也便于运维团队对不同类型的节点执行独立的升级、扩缩容和监控策略。需要注意的是,节点池只是逻辑分组,并不会自动阻止跨池调度,必须配合污点、容忍或亲和性规则才能真正产生隔离效果。
污点与容忍机制
污点(Taint)的作用是给节点增加排斥标记,只有声明了对应容忍(Toleration)的 Pod 才能被调度到该节点。一个标准的污点由 key=value:effect 三部分组成,其中 effect 可以是 NoSchedule、PreferNoSchedule 或 NoExecute。NoSchedule 会阻止新 Pod 调度到该节点,PreferNoSchedule 只是尽量避免,NoExecute 还会驱逐已经运行但不具备容忍的 Pod。给 GPU 节点添加污点可以防止普通的 Web 服务占用昂贵的加速卡资源。
kubectl taint nodes node-1 accelerator=nvidia-t4:NoSchedule kubectl taint nodes node-2 arch=arm64:NoSchedule kubectl describe node node-1 | grep Taints
在 Pod 规格中声明容忍时,需要保证 key、operator、value 和 effect 与污点匹配。operator 为 Equal 时要求 value 完全一致,operator 为 Exists 时只需 key 存在即可。下面的 YAML 片段展示了如何让一个推理服务容忍 GPU 节点的污点,从而获得调度资格:
tolerations: - key: accelerator operator: Equal value: nvidia-t4 effect: NoSchedule
污点与容忍的优点是简单直接,适合强隔离场景。但仅靠污点并不能把 Pod 主动调度到 GPU 节点,它只是排除了不匹配的节点。如果某个 GPU 节点与多个普通节点同时满足容忍条件,调度器仍可能优先选择普通节点。因此通常需要把污点与节点选择器或节点亲和性结合使用,才能实现正向引导。
节点亲和性与拓扑分布约束
节点亲和性(Node Affinity)用于表达 Pod 对节点属性的偏好或硬性要求。nodeSelector 是最简单的形式,但它只能做精确匹配,无法表达“优先选择 SSD 节点,如果没有 SSD 则接受普通节点”这类软性策略。nodeAffinity 提供了 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 两种模式,前者是硬性条件,后者是软性偏好,调度器会尽量满足。
例如一个 AI 推理服务希望优先运行在 GPU 节点池,同时要求节点必须位于可用区 zone-a,可以这样配置:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- zone-a
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node-pool
operator: In
values:
- gpu
拓扑分布约束(topologySpreadConstraints)解决的是同一工作负载在故障域之间的均衡问题。如果某个 Deployment 有 6 个副本,而集群有 3 个可用区,我们希望每个可用区尽量平均分布 2 个副本,而不是全部集中到一个可用区。结合节点池标签,可以指定按 node-pool 或 zone 作为拓扑键进行打散。这样既能保证硬件亲和,又能降低单点故障影响。
调度策略实战:混合编排示例
下面这个示例整合了资源请求、节点选择器、容忍、节点亲和性与拓扑分布约束,展示如何将一个需要 GPU 的推理服务准确调度到 GPU 节点池并跨可用区打散。其中 nodeSelector 指定基础池标签,tolerations 让它能容忍 GPU 污点,affinity 表达软硬偏好,topologySpreadConstraints 保证副本均衡。
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-service
spec:
replicas: 4
selector:
matchLabels:
app: inference
template:
metadata:
labels:
app: inference
spec:
nodeSelector:
node-pool: gpu
arch: amd64
tolerations:
- key: accelerator
operator: Equal
value: nvidia-t4
effect: NoSchedule
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-t4
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: inference
containers:
- name: inference
image: inference:latest
resources:
limits:
nvidia.com/gpu: 1
cpu: "4"
memory: 8Gi
requests:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
这个配置首先通过 nodeSelector 把候选节点限制为同时具有 node-pool=gpu 与 arch=amd64 标签的节点,这相当于节点池层的粗筛。随后 tolerations 允许 Pod 落在带有 accelerator=nvidia-t4:NoSchedule 污点的专用节点上。拓扑分布约束则保证 4 个副本在可用区之间的最大偏差不超过 1,若无法满足则保持 Pending 而不是强行倾斜分布,从而保证可用性。资源请求中的 nvidia.com/gpu: 1 会触发调度器预留一块 GPU,确保同一块 GPU 不会被多个 Pod 同时请求。
对于需要跨所有节点运行的守护进程类工作负载,例如日志采集或监控 Agent,可以通过容忍所有节点池污点的方式覆盖异构节点。需要注意的是,容忍所有污点可能让 Agent 落到不兼容的硬件上,因此还需要结合节点选择器或使用 DaemonSet 的 nodeSelector 进行二次过滤。另一种做法是利用 Kubernetes 调度框架开发自定义调度插件,在过滤阶段就根据硬件能力与资源扩展信息打分,这样能实现更细粒度的异构调度,但复杂度也更高。
Kubernetes节点池异构资源调度污点与容忍修改时间:2026-08-21 01:33:46