Kubernetes 调度进阶:NodeAffinity、Taint/Toleration、PodTopologySpread 实战
深入讲解 Kubernetes 高级调度机制:NodeSelector/NodeAffinity 精确选节点、Taint/Toleration 实现专属节点池、PodAffinity/AntiAffinity 控制 Pod 间关系、PodTopologySpread 实现跨区域均匀分布,覆盖 GPU 节点隔离、高可用跨 AZ 部署等生产场景。
Kubernetes 默认调度器(kube-scheduler)通过打分算法把 Pod 分配到合适的节点,但默认算法无法满足“GPU 任务只跑 GPU 节点”、“数据库 Pod 跨 AZ 分散”、“前端 Pod 和缓存 Pod 部署在同一节点”这类精细化需求。本文讲解四类高级调度机制的生产用法。
一、NodeSelector(基础标签筛选)
# 最简单的节点选择:匹配节点标签
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-training
spec:
template:
spec:
nodeSelector:
accelerator: nvidia-gpu # 只调度到有此标签的节点
node-type: high-memory
containers:
- name: trainer
image: pytorch/pytorch:2.4
# 给节点打标签
kubectl label node gpu-node-01 accelerator=nvidia-gpu
kubectl label node gpu-node-01 node-type=high-memory
# 查看节点标签
kubectl get nodes --show-labels
kubectl get nodes -l accelerator=nvidia-gpu
NodeSelector 只支持“精确匹配”,不支持 OR 逻辑(多个候选值)。这就需要 NodeAffinity。
二、NodeAffinity:灵活的节点亲和性
NodeAffinity 支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 六种操作符。
硬亲和(requiredDuringScheduling)
spec:
affinity:
nodeAffinity:
# 硬性要求:不满足则 Pod 一直 Pending
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: [amd64, arm64] # OR 逻辑:amd64 或 arm64 节点都可以
- key: node.kubernetes.io/instance-type
operator: NotIn
values: [spot] # AND 逻辑:同时要求非 spot 实例
软亲和(preferredDuringScheduling)
spec:
affinity:
nodeAffinity:
# 软性偏好:优先选但不强求
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80 # 权重 1-100
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: [cn-shanghai-a] # 优先 A 区,没有就去其他区
- weight: 20
preference:
matchExpressions:
- key: node-type
operator: In
values: [high-cpu] # 次优先高 CPU 节点
生产场景:GPU 任务调度
# GPU 训练任务:必须有 GPU,优先 A100
apiVersion: batch/v1
kind: Job
metadata:
name: model-training
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values: [nvidia-gpu]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values: [NVIDIA-A100-SXM4-80GB]
- weight: 60
preference:
matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values: [NVIDIA-H100-80GB-HBM3]
containers:
- name: trainer
resources:
limits:
nvidia.com/gpu: "4"
三、Taint/Toleration:专属节点池
Taint(污点)打在节点上,阻止普通 Pod 调度;Toleration(容忍)加在 Pod 上,允许调度到有对应污点的节点。
# 给节点打污点
kubectl taint nodes gpu-node-01 dedicated=gpu-only:NoSchedule
# effect 三种:
# NoSchedule → 不容忍的 Pod 不能调度(已运行的不受影响)
# PreferNoSchedule → 尽量不调度(软限制)
# NoExecute → 不容忍的 Pod 不能调度,且已运行的会被驱逐
# 查看节点污点
kubectl describe node gpu-node-01 | grep Taint
# 移除污点
kubectl taint nodes gpu-node-01 dedicated=gpu-only:NoSchedule-
# 末尾加 - 表示删除
GPU 专属节点池配置
# GPU 节点污点:dedicated=gpu:NoSchedule
# 普通 Pod 不会调度到 GPU 节点,节省昂贵资源
# GPU 任务 Pod 添加 Toleration
spec:
tolerations:
- key: dedicated
operator: Equal
value: gpu
effect: NoSchedule
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: dedicated
operator: In
values: [gpu]
系统组件独占节点(master 节点保护)
# K8s master 节点默认有污点,普通 Pod 不会调度上去
kubectl describe node master-01 | grep Taint
# Taints: node-role.kubernetes.io/control-plane:NoSchedule
# 如果需要在 master 上运行某些系统 Pod(如监控),加容忍:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
节点维护驱逐(NoExecute)
# 节点维护时,驱逐 Pod 但给宽限时间
kubectl taint nodes node-01 maintenance=true:NoExecute
# 给关键服务 Pod 设置容忍宽限时间(默认 0 秒立即驱逐)
spec:
tolerations:
- key: maintenance
operator: Equal
value: "true"
effect: NoExecute
tolerationSeconds: 300 # 允许 Pod 继续运行 300 秒,期间可以完成请求处理
四、PodAffinity/AntiAffinity:Pod 间关系调度
控制 Pod 之间是“靠近”还是“远离”。
让 Pod 与特定 Pod 在同一节点(PodAffinity)
# 场景:缓存 Pod 希望和 Web Pod 在同一节点(减少网络延迟)
apiVersion: apps/v1
kind: Deployment
metadata:
name: cache
spec:
template:
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web # 亲近 web Pod
topologyKey: kubernetes.io/hostname # 在节点级别匹配
让 Pod 分散到不同节点(PodAntiAffinity)
# 场景:同一个 Deployment 的多个副本必须分散到不同节点(高可用)
spec:
affinity:
podAntiAffinity:
# 硬反亲和:同一节点上不能有相同 app 的 Pod
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: redis-master
topologyKey: kubernetes.io/hostname
跨 AZ 高可用部署
# 场景:数据库 Pod 必须分布在不同可用区
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: postgres
topologyKey: topology.kubernetes.io/zone # AZ 级别分散
五、PodTopologySpread:精确控制分布
PodAffinity 只能“尽量分散”,PodTopologySpread 可以精确控制“最多允许多少个 Pod 在同一域”。
spec:
topologySpreadConstraints:
- maxSkew: 1 # 各域 Pod 数量差异不超过 1
topologyKey: kubernetes.io/hostname # 以节点为域
whenUnsatisfiable: DoNotSchedule # 不满足则不调度(硬约束)
labelSelector:
matchLabels:
app: web
- maxSkew: 2
topologyKey: topology.kubernetes.io/zone # 以 AZ 为域
whenUnsatisfiable: ScheduleAnyway # 软约束(尽力满足)
labelSelector:
matchLabels:
app: web
完整生产示例:跨 AZ 均匀分布
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
spec:
replicas: 6
selector:
matchLabels:
app: web-frontend
template:
metadata:
labels:
app: web-frontend
spec:
# 1. 跨 AZ 均匀分布(软约束,AZ 不够时不卡住)
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web-frontend
# 2. 同一节点不超过 2 个副本(防止节点故障影响太大)
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-frontend
# 3. 反亲和:硬性保证不在同一节点堆叠(与上面配合)
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web-frontend
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: nginx:1.27
六、调度优先级(PriorityClass)
# 创建优先级类
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000 # 数字越大优先级越高
globalDefault: false
description: "关键业务服务"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 100
globalDefault: false
description: "批处理任务"
# Pod 使用优先级
spec:
priorityClassName: high-priority
# 当集群资源不足时,低优先级 Pod 会被抢占(Preemption)
调度问题排查
# Pod 一直 Pending,查看调度失败原因
kubectl describe pod <pod-name> | grep -A 20 Events
# Events:
# Warning FailedScheduling 0/5 nodes are available:
# 3 node(s) had untolerated taint {dedicated: gpu},
# 2 node(s) didn't match Pod's node affinity/selector.
# 查看调度器日志
kubectl logs -n kube-system -l component=kube-scheduler --tail=50
# 模拟调度(不实际创建 Pod)
kubectl get nodes -o json | \
kubectl debug -it --image=bitnami/kubectl -- \
kubectl explain pod.spec.affinity
# 查看节点可用资源
kubectl describe nodes | grep -A 5 "Allocatable"
kubectl top nodes
小结
四类调度机制的使用场景:NodeAffinity 用于“选什么样的节点”(GPU 节点、高内存节点);Taint/Toleration 用于“节点隔离”(GPU 专属、master 保护、维护驱逐);PodAntiAffinity 用于“同类 Pod 分散”(高可用、防止单点);PodTopologySpread 是最精细的跨域分布控制(精确控制跨 AZ/节点的 Pod 数量差异)。生产中这四类通常组合使用:用 Taint 隔离 GPU 节点,用 NodeAffinity 精确选型,用 PodAntiAffinity + TopologySpread 保证高可用分布。
