4. 节点选择器与亲和性:nodeSelector、nodeAffinity、节点反亲和性配置
在 Kubernetes 集群中,Pod 默认会被调度到任意满足资源需求的节点上。但在生产环境中,我们常常需要将 Pod 精确绑定到特定节点——例如 GPU 节点、SSD 节点或特定可用区。Kubernetes 提供了从简单到复杂的三种调度约束机制:nodeSelector(节点选择器)、nodeAffinity(节点亲和性)和 Pod 反亲和性(Pod Anti-Affinity)。本章将逐一剖析它们的原理、配置语法与最佳实践。
4.1 nodeSelector:最简单的节点选择
nodeSelector 是 Kubernetes 最原始的节点选择方式,它通过 Pod 规约中的 nodeSelector 字段,要求 Pod 只能调度到包含指定标签的节点上。其工作流程如下:
- 运维人员为节点打上标签(如
disktype=ssd、gpu=true)。 - 开发人员在 Pod 的
spec.nodeSelector中声明需要的标签键值对。 - 调度器仅将 Pod 分配给标签完全匹配的节点;若无匹配节点,Pod 将处于 Pending 状态。
局限性: 仅支持精确的键值对匹配,不支持“或”“非”“存在”等逻辑表达式。
4.1.1 节点标签操作
为节点添加标签:
kubectl label nodes node-01 disktype=ssd
kubectl label nodes node-02 gpu=true
查看节点标签:
kubectl get nodes --show-labels
4.1.2 Pod 配置示例
apiVersion: v1
kind: Pod
metadata:
name: ssd-pod
spec:
nodeSelector:
disktype: ssd
containers:
- name: app
image: nginx
| 特性 | 说明 |
|---|---|
| 匹配逻辑 | 所有键值对必须完全匹配(AND 关系) |
| 支持运算符 | 仅支持 =(等于) |
| 多标签组合 | 支持,但必须全部满足 |
| 节点无匹配标签 | Pod 无法调度,保持 Pending |
4.2 nodeAffinity:更灵活的节点亲和性
nodeAffinity 是 nodeSelector 的升级版,属于 调度策略 的一部分,定义在 spec.affinity.nodeAffinity 下。它支持丰富的表达式运算符,并区分两种语义:
- requiredDuringSchedulingIgnoredDuringExecution:硬约束,调度时必须满足,否则 Pod 无法调度。
- preferredDuringSchedulingIgnoredDuringExecution:软约束,调度器会尽量满足,但不保证;权重越高,优先级越大。
4.2.1 常用运算符
| 运算符 | 含义 | 示例 |
|---|---|---|
In |
标签值属于给定集合 | zone in (us-east-1a, us-east-1b) |
NotIn |
标签值不属于给定集合 | zone notin (us-west-2a) |
Exists |
节点存在该标签键(值不限) | gpu exists |
DoesNotExist |
节点不存在该标签键 | spot-instance doesnotexist |
Gt / Lt |
标签值大于/小于指定数值(需为数值字符串) | cpu-core gt 16 |
4.2.2 硬约束 + 软约束组合示例
apiVersion: v1
kind: Pod
metadata:
name: affinity-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- us-east-1b
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
- weight: 20
preference:
matchExpressions:
- key: cpu-model
operator: In
values:
- amd-epyc
containers:
- name: app
image: nginx
上述配置的含义:Pod 必须调度到 us-east-1a 或 us-east-1b 可用区;在此前提下,优先选择 disktype=ssd 的节点(权重 80),其次考虑 cpu-model=amd-epyc 的节点(权重 20)。
4.3 节点反亲和性:避免 Pod 聚集
节点反亲和性(Pod Anti-Affinity)与节点亲和性不同,它关注的是 Pod 与 Pod 之间的分布关系,而非 Pod 与节点的关系。它定义在 spec.affinity.podAntiAffinity 下,同样支持 required 和 preferred 两种模式。
典型场景:
- 将同一应用的多个副本分散到不同节点,提高可用性。
- 将计算密集型的 Pod 与内存密集型的 Pod 隔离,避免资源争抢。
4.3.1 拓扑键(topologyKey)
反亲和性通过 topologyKey 定义“分散的范围”。最常用的拓扑键:
| 拓扑键 | 分散粒度 | 说明 |
|---|---|---|
kubernetes.io/hostname |
节点级别 | 每个节点最多运行一个匹配的 Pod |
topology.kubernetes.io/zone |
可用区级别 | 每个可用区最多运行一个匹配的 Pod |
topology.kubernetes.io/region |
地域级别 | 每个地域最多运行一个匹配的 Pod |
4.3.2 硬反亲和性示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-anti-affinity
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx
该配置强制要求:每个节点上最多只能运行一个带有 app=web 标签的 Pod。如果集群只有 2 个节点,则 3 个副本中只有 2 个能成功调度,第 3 个将处于 Pending 状态。
4.4 三种机制的对比与选择
| 机制 | 作用对象 | 表达能力 | 调度语义 | 典型场景 |
|---|---|---|---|---|
| nodeSelector | Pod → 节点 | 仅精确键值对 | 硬约束 | GPU、SSD 等固定硬件分区 |
| nodeAffinity | Pod → 节点 | 丰富运算符 + 权重 | 硬约束 / 软约束 | 可用区亲和、机型偏好、成本优化 |
| podAntiAffinity | Pod → Pod | 标签选择器 + 拓扑键 | 硬约束 / 软约束 | 高可用分散、故障域隔离 |
4.5 调度决策流程(SVG 图)
下图展示了 Kubernetes 调度器在处理 Pod 调度请求时,依次评估 nodeSelector、nodeAffinity 和 podAntiAffinity 的逻辑顺序:
4.6 最佳实践与避坑指南
- 优先使用 nodeAffinity 替代 nodeSelector:nodeAffinity 完全覆盖 nodeSelector 的功能,且表达能力更强。新项目建议直接使用 nodeAffinity。
- 软约束权重设计:权重范围 1–100,建议按业务重要性分配。例如,SSD 节点权重 80,高内存节点权重 20。
- 避免硬反亲和性过度约束:当副本数 > 节点数时,硬反亲和性会导致部分 Pod 永远无法调度。此时应改用
preferredDuringScheduling。 - 结合 topologyKey 实现故障域隔离:使用
topology.kubernetes.io/zone可将 Pod 分散到不同可用区,提升跨可用区容灾能力。 - 标签命名规范:建议使用
topology.kubernetes.io/*或node.kubernetes.io/*等标准前缀,避免与自定义标签冲突。