4. 节点亲和性配置实战:通过YAML配置节点亲和性规则、多条件组合
节点亲和性(Node Affinity)是Kubernetes调度器将Pod调度到特定节点集合的核心机制。与简单的nodeSelector相比,节点亲和性支持逻辑表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt)和优先级权重(preferred vs required),能够实现精细化的调度策略。
4.1 节点亲和性两种模式对比
| 模式 | 语义 | 调度行为 | 典型场景 |
|---|---|---|---|
requiredDuringSchedulingIgnoredDuringExecution |
硬约束(必须满足) | 找不到匹配节点则Pod Pending | 合规要求、GPU节点绑定、数据本地化 |
preferredDuringSchedulingIgnoredDuringExecution |
软约束(尽量满足) | 优先调度到匹配节点,不匹配则调度到其他节点 | 性能优化、成本控制、负载均衡 |
关键理解:两种模式名称中的"IgnoredDuringExecution"表示运行时忽略——即使节点标签在Pod运行后被修改,已运行的Pod也不会被驱逐。这是K8s调度器设计上对稳定性的保障。
4.2 节点亲和性规则语法结构
节点亲和性规则通过Pod的spec.affinity.nodeAffinity字段定义,支持多条件组合(同一规则内AND,不同规则间OR)。
apiVersion: v1
kind: Pod
metadata:
name: gpu-inference-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-tesla-v100
- nvidia-a100
- key: gpu-count
operator: Gt
values:
- "2"
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- weight: 20
preference:
matchExpressions:
- key: node-type
operator: In
values:
- spot
containers:
- name: inference
image: nvidia/cuda:12.0-base
规则解析:
- 硬约束:节点必须同时满足
accelerator In (v100, a100)ANDgpu-count Gt 2,两个条件在同一matchExpressions内,逻辑为AND。 - 软约束:优先选择
us-east-1a可用区(权重80),其次选择spot节点(权重20)。调度器会计算所有软约束的加权总分,选择分数最高的节点。
4.3 多条件组合逻辑详解
节点亲和性支持两层组合逻辑,理解层级关系是正确配置的关键:
4.4 常用操作符及使用场景
| 操作符 | 语义 | 示例 | 适用场景 |
|---|---|---|---|
In |
标签值在给定集合中 | region In (us-east, us-west) |
指定可用区、节点池 |
NotIn |
标签值不在给定集合中 | disk-type NotIn (hdd) |
排除特定类型节点 |
Exists |
节点存在该标签键(值不限) | gpu Exists |
检测节点是否具备某特性 |
DoesNotExist |
节点不存在该标签键 | spot DoesNotExist |
排除带某标签的节点 |
Gt |
标签值大于指定数值(字符串比较) | memory-gb Gt 64 |
按资源容量筛选 |
Lt |
标签值小于指定数值 | cpu-cores Lt 8 |
选择低配节点 |
⚠️ 操作符使用注意:
Gt和Lt仅支持整数或浮点数字符串比较,不支持语义版本号(如"v1.2.3")。Exists和DoesNotExist不需要values字段,只需指定key。- 一个
matchExpressions内最多支持128个条件,但建议控制在10个以内以保持可读性。
4.5 实战:多条件组合配置示例
以下示例展示一个生产级推理服务的节点亲和性配置,同时使用硬约束和软约束:
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-inference
spec:
replicas: 5
selector:
matchLabels:
app: llm-inference
template:
metadata:
labels:
app: llm-inference
spec:
affinity:
nodeAffinity:
# 硬约束:必须满足
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
# Term 1:GPU节点 + 内存≥128GB
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-a100
- nvidia-h100
- key: memory-gb
operator: Gt
values:
- "128"
# Term 2:或使用CPU推理 + 高内存节点
- matchExpressions:
- key: node-type
operator: In
values:
- cpu-highmem
- key: memory-gb
operator: Gt
values:
- "256"
# 软约束:尽量满足
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 90
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- weight: 10
preference:
matchExpressions:
- key: cost-tier
operator: In
values:
- reserved
containers:
- name: inference
image: registry.example.com/llm:v2.1
resources:
requests:
memory: "64Gi"
cpu: "8"
配置解读:
- 硬约束(OR逻辑):调度器会尝试匹配Term 1(A100/H100 GPU + 内存>128GB)或Term 2(CPU高内存节点 + 内存>256GB)。只要满足任意一个Term,Pod即可调度。
- 软约束(加权评分):在满足硬约束的节点中,优先选择
us-east-1a可用区(权重90),其次选择reserved预留实例(权重10)。调度器会计算所有软约束的加权总分。 - 调度决策:如果
us-east-1a中有可用节点,得分最高;否则回退到其他可用区的预留实例节点。
4.6 节点亲和性 vs 节点选择器
| 特性 | nodeSelector |
nodeAffinity |
|---|---|---|
| 表达式支持 | 仅等值匹配(key=value) | In、NotIn、Exists、Gt、Lt等6种操作符 |
| 多条件逻辑 | 多个条件之间AND | 支持AND(同一Term)和OR(不同Term) |
| 软约束 | 不支持 | 支持权重优先级 |
| 可读性 | 简单直观 | 复杂但灵活 |
| 推荐场景 | 简单标签匹配 | 复杂调度策略、多条件组合 |
迁移建议:如果现有
nodeSelector配置已经满足需求,无需强行迁移到nodeAffinity。但当需要排除节点(NotIn)、检测标签存在性(Exists)或优先级调度时,必须使用节点亲和性。
4.7 调试与验证
配置完成后,使用以下命令验证节点亲和性是否生效:
# 查看Pod调度到的节点
kubectl get pod llm-inference-xxxxx -o wide
# 查看Pod调度事件
kubectl describe pod llm-inference-xxxxx | grep -A 10 Events
# 检查节点标签是否匹配
kubectl get node <node-name> --show-labels
# 模拟调度(dry-run)
kubectl create -f pod.yaml --dry-run=server -o json | jq '.spec.nodeName'
常见问题排查:
- Pod一直Pending:检查
kubectl describe pod中的Events,查看是否提示"0/5 nodes are available"及具体不匹配条件。 - 软约束未生效:确认
weight值范围在1-100之间,且preferredDuringScheduling拼写正确。 - 标签值类型错误:所有标签值在K8s中都是字符串,
Gt和Lt比较时按字典序比较数字字符串,确保格式一致。