4. 调度亲和性基础:nodeSelector、nodeAffinity、podAffinity 与 podAntiAffinity 概念
在 Kubernetes 集群中,调度器(kube-scheduler)负责将 Pod 分配到合适的 Node 上。默认调度策略基于资源请求(Requests)和限制(Limits)进行打分与筛选。但在生产环境中,我们往往需要更精细的控制,例如:将数据库 Pod 部署到 SSD 节点、将前端 Pod 与后端 Pod 放在同一可用区、将同一服务的多个副本分散到不同故障域——这些需求正是调度亲和性(Affinity)机制要解决的问题。
Kubernetes 提供了四种主要的亲和性/反亲和性机制,按作用域可分为节点亲和性和Pod 间亲和性两大类:
| 机制 | 作用域 | 核心语义 | 典型场景 |
|---|---|---|---|
| nodeSelector | 节点 → Pod | 简单标签匹配(硬约束) | 将 Pod 调度到带有特定标签的节点 |
| nodeAffinity | 节点 → Pod | 丰富表达式匹配(硬约束/软约束) | 优先调度到 GPU 节点,或强制调度到特定区域 |
| podAffinity | Pod → Pod | Pod 与 Pod 靠近(同拓扑域) | 将缓存 Pod 与计算 Pod 部署在同一节点或同一可用区 |
| podAntiAffinity | Pod → Pod | Pod 与 Pod 远离(不同拓扑域) | 将同一 Deployment 的副本分散到不同节点或不同可用区 |
4.1 nodeSelector:最简洁的节点选择
nodeSelector 是 Kubernetes 最早提供的节点选择机制,使用简单的键值对匹配。Pod 只会被调度到包含所有指定标签的 Node 上。如果没有任何 Node 满足条件,Pod 将处于 Pending 状态。
apiVersion: v1
kind: Pod
metadata:
name: nginx-ssd
spec:
nodeSelector:
disk-type: ssd # 节点必须包含标签 disk-type=ssd
containers:
- name: nginx
image: nginx:1.25
局限性:
- 仅支持
In语义(等于),不支持NotIn、Exists、DoesNotExist等操作符。 - 多个标签之间是 AND 关系,无法表达 OR 逻辑。
- 无法设置软约束(preferred),所有条件都是硬约束(required)。
4.2 nodeAffinity:增强的节点选择
nodeAffinity 在 nodeSelector 的基础上引入了丰富的表达式支持和软/硬约束分级。它通过 requiredDuringSchedulingIgnoredDuringExecution(硬约束)和 preferredDuringSchedulingIgnoredDuringExecution(软约束)两个字段来定义调度策略。
apiVersion: v1
kind: Pod
metadata:
name: nginx-gpu
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu-type
operator: In
values:
- nvidia-tesla
- nvidia-a100
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values:
- zone-a
containers:
- name: nginx
image: nginx:1.25
操作符支持:
| 操作符 | 语义 | 示例 |
|---|---|---|
In |
标签值在给定集合中 | key: zone, values: [zone-a, zone-b] |
NotIn |
标签值不在给定集合中 | key: zone, values: [zone-c] |
Exists |
节点存在该标签键(忽略值) | key: gpu-type, operator: Exists |
DoesNotExist |
节点不存在该标签键 | key: spot-instance, operator: DoesNotExist |
Gt / Lt |
标签值大于/小于给定数值(标签值需为数字字符串) | key: cpu-cores, operator: Gt, values: [8] |
requiredDuringSchedulingIgnoredDuringExecution 硬约束时,如果集群中所有满足标签条件的节点都已资源耗尽,新 Pod 将无法被调度(Pending)。但如果使用 preferredDuringSchedulingIgnoredDuringExecution 软约束,Pod 可能被调度到不满足偏好的节点上,若该节点资源紧张,则可能引发资源竞争型 CrashLoop。
4.3 podAffinity:Pod 间的“靠近”策略
podAffinity 允许你指定 Pod 应该与哪些已有的 Pod 部署在同一个拓扑域中。拓扑域通过 topologyKey 定义,可以是节点(kubernetes.io/hostname)、可用区(topology.kubernetes.io/zone)或自定义标签。
apiVersion: apps/v1
kind: Deployment
metadata:
name: cache-pod
spec:
replicas: 2
selector:
matchLabels:
app: cache
template:
metadata:
labels:
app: cache
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
topologyKey: kubernetes.io/hostname
containers:
- name: cache
image: redis:7
上述配置要求:cache Pod 必须被调度到已经运行了 app=backend Pod 的节点上。如果没有任何节点满足条件,cache Pod 将处于 Pending 状态。
4.4 podAntiAffinity:Pod 间的“远离”策略
podAntiAffinity 与 podAffinity 相反,它要求 Pod 与某些 Pod 不在同一个拓扑域中。这是实现高可用和故障隔离的核心手段。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.25
上述配置使用软约束(preferred)尽量将同一 Deployment 的 3 个副本调度到不同节点上。如果集群节点数不足 3 个,调度器会尽量分散,但不会强制阻塞。
requiredDuringSchedulingIgnoredDuringExecution 的 podAntiAffinity 配合 topologyKey: kubernetes.io/hostname,确保同一 StatefulSet 的副本不会落在同一节点上,避免单点故障导致整个服务不可用。
4.5 亲和性机制对比总览
下面这张 SVG 图展示了四种亲和性机制的作用域和关系:
4.6 调度阶段与亲和性执行顺序
理解亲和性在调度流程中的执行阶段,有助于定位 CrashLoop 根因。调度器执行亲和性检查的顺序如下:
- 过滤阶段(Predicates):首先检查
requiredDuringScheduling硬约束,不满足的节点被直接过滤掉。 - 打分阶段(Priorities):对剩余节点按
preferredDuringScheduling软约束进行加权打分。 - 资源检查:在满足亲和性约束的节点中,进一步检查资源(CPU、内存、GPU 等)是否满足 Pod 的 Requests。
- 最终选择:选择综合得分最高的节点进行绑定。
kubectl describe pod 发现调度阶段正常(Events 中无 FailedScheduling 事件),则问题通常不在亲和性配置本身,而在于 Pod 启动后与节点上的其他 Pod 发生了资源竞争。此时应检查节点资源使用率(kubectl top node)和同节点其他 Pod 的资源限制配置。
4.7 常见亲和性配置错误与 CrashLoop 关联
| 错误类型 | 表现 | CrashLoop 关联 | 排查方法 |
|---|---|---|---|
| nodeAffinity 标签不匹配 | Pod 一直 Pending | 不会直接导致 CrashLoop,但可能间接导致其他 Pod 被调度到错误节点 | kubectl describe pod 查看 FailedScheduling 事件 |
| podAntiAffinity 过于严格 | 副本数不足,部分 Pod Pending | 已调度的 Pod 可能因负载集中而 OOMKilled | 检查节点资源使用率,调整 required 为 preferred |
| topologyKey 选择不当 | Pod 分散不符合预期 | 副本集中在同一节点,资源竞争导致重启 | 确认节点标签一致性,使用 kubernetes.io/hostname 作为最小拓扑域 |
| 软约束权重失衡 | 调度结果与预期偏差大 | Pod 被调度到高负载节点,引发资源竞争 | 检查节点负载,调整 weight 值或增加硬约束兜底 |