调度策略基础:节点选择与亲和性/反亲和性
Kubernetes 调度器负责将 Pod 分配到合适的节点上。默认调度器基于资源请求(Requests)和可用资源进行打分与选择,但在实际业务中,我们往往需要更精细的控制:将 Pod 调度到特定硬件(如 GPU 节点)、将高流量 Pod 分散到不同节点、或将相关服务部署在同一台机器上以减少网络延迟。Kubernetes 提供了三种核心调度约束机制:nodeSelector、节点亲和性(nodeAffinity) 以及 Pod 亲和性/反亲和性(podAffinity / podAntiAffinity)。
1. nodeSelector:最简单的节点选择
nodeSelector 是 Kubernetes 中最直接、最古老的节点选择方式。它通过 Pod 定义中的 spec.nodeSelector 字段,要求 Pod 只能调度到包含指定标签(Label)的节点上。
适用场景: 快速将工作负载绑定到具有特定标签的节点池,例如“所有 GPU 任务必须运行在带有
gpu=true 标签的节点上”。
工作原理:
- 运维人员预先为节点打上标签(如
disktype=ssd、region=us-east)。 - Pod 的 YAML 中声明
nodeSelector,键值必须完全匹配节点标签。 - 调度器在过滤阶段(Predicates)直接剔除不满足标签条件的节点。
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
nodeSelector:
gpu: "true" # 节点必须包含 gpu=true 标签
containers:
- name: cuda-container
image: nvidia/cuda:12.0
| 特性 | 说明 |
|---|---|
| 匹配逻辑 | 精确匹配(AND 逻辑),所有键值对必须同时满足 |
| 表达能力 | 弱,仅支持等值匹配,不支持 In、NotIn、Exists 等操作符 |
| 灵活性 | 低,无法表达“优先选择 SSD 节点,但如果没有也可以接受”这类软约束 |
| 维护成本 | 低,适合简单固定的标签匹配 |
注意:
nodeSelector 是硬约束(Required)。如果集群中没有节点满足标签条件,Pod 将一直处于 Pending 状态。对于需要软偏好或复杂逻辑的场景,请使用 nodeAffinity。
2. nodeAffinity:增强的节点选择
nodeAffinity 是 nodeSelector 的升级版,属于节点亲和性(Node Affinity)功能。它提供了更丰富的匹配操作符(In、NotIn、Exists、DoesNotExist、Gt、Lt),并支持硬约束(required)和软偏好(preferred)两种语义。
两种类型:
- requiredDuringSchedulingIgnoredDuringExecution:硬约束。调度时必须满足,否则 Pod 无法调度。节点运行时标签变化不影响已运行的 Pod。
- preferredDuringSchedulingIgnoredDuringExecution:软偏好。调度器会尽量满足,但不强制。可以设置
weight(权重,1-100)来控制优先级。
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
containers:
- name: app
image: nginx
| 操作符 | 示例 | 说明 |
|---|---|---|
In |
key: zone, values: [a, b] |
节点标签值在给定列表中 |
NotIn |
key: zone, values: [c] |
节点标签值不在给定列表中 |
Exists |
key: gpu |
节点存在该标签(不关心值) |
DoesNotExist |
key: spot |
节点不存在该标签 |
Gt / Lt |
key: memory, values: [32] |
标签值大于/小于给定数值(标签值需为数字字符串) |
最佳实践: 使用
preferredDuringScheduling 时,建议将权重设置为 10 的倍数(如 10、20、50、100),便于后期调整优先级。多个偏好规则之间权重可以叠加。
3. Pod 亲和性与反亲和性
节点亲和性关注的是 Pod 与节点标签的关系,而 Pod 亲和性(podAffinity)与反亲和性(podAntiAffinity)关注的是 Pod 与其他 Pod 的拓扑关系。它们基于已经运行在节点上的 Pod 的标签来决定调度位置。
核心概念:
- podAffinity:将 Pod 调度到与某些 Pod 相同的拓扑域中(例如:同一个节点、同一个可用区)。常用于将前端与后端服务部署在一起以减少延迟。
- podAntiAffinity:将 Pod 调度到与某些 Pod 不同的拓扑域中。常用于将同一服务的多个副本分散到不同节点或可用区,提高可用性。
关键参数:
topologyKey:定义拓扑域的键。例如kubernetes.io/hostname表示节点级别,topology.kubernetes.io/zone表示可用区级别。labelSelector:选择目标 Pod 的标签。namespaces:指定命名空间范围(默认是当前命名空间)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
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
实战解读: 上述配置强制要求同一 Deployment 的 3 个副本必须调度到不同的节点上(
topologyKey: kubernetes.io/hostname)。如果集群只有 2 个节点,第 3 个 Pod 将处于 Pending 状态,直到有新的节点加入。
4. 三种调度策略对比总结
| 策略 | 作用对象 | 约束类型 | 操作符 | 典型场景 |
|---|---|---|---|---|
| nodeSelector | 节点标签 | 硬约束 | 等值匹配 | GPU 节点、SSD 节点 |
| nodeAffinity | 节点标签 | 硬约束 + 软偏好 | In、NotIn、Exists、Gt、Lt | 优先选择高性能节点、排除故障域 |
| podAffinity | Pod 标签 | 硬约束 + 软偏好 | In、NotIn、Exists | 将缓存与计算服务部署在同一节点 |
| podAntiAffinity | Pod 标签 | 硬约束 + 软偏好 | In、NotIn、Exists | 分散副本、避免单点故障 |
5. 调度策略选择决策树
在实际业务中,如何选择合适的调度策略?可以参考以下决策逻辑:
- 是否需要绑定到特定硬件或区域?
- 是 → 使用 nodeSelector(简单场景)或 nodeAffinity(复杂场景)。
- 是否需要将 Pod 部署到与其他 Pod 相同的位置?
- 是 → 使用 podAffinity,并指定
topologyKey(如kubernetes.io/hostname或topology.kubernetes.io/zone)。
- 是 → 使用 podAffinity,并指定
- 是否需要将 Pod 副本分散开以避免单点故障?
- 是 → 使用 podAntiAffinity,配合
requiredDuringScheduling实现强制分散。
- 是 → 使用 podAntiAffinity,配合
- 是否需要同时满足多种约束?
- 可以组合使用 nodeAffinity 和 podAntiAffinity,调度器会同时评估所有规则。
性能注意: 使用
podAntiAffinity 的 requiredDuringScheduling 时,如果集群规模较大(数百节点),调度器需要遍历所有节点上的 Pod 进行匹配,可能增加调度延迟。对于大规模集群,建议优先使用软偏好(preferred)或配合 topologyKey 缩小范围。