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 的副本分散到不同节点或不同可用区
关键区分:nodeSelector 和 nodeAffinity 描述的是 Pod 与 Node 之间的关系;podAffinity 和 podAntiAffinity 描述的是 Pod 与 Pod 之间的关系。在排查 CrashLoop 时,错误的亲和性配置可能导致 Pod 被调度到资源紧张的节点,进而引发资源竞争。

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]
CrashLoop 关联风险:当使用 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 图展示了四种亲和性机制的作用域和关系:

Kubernetes 调度亲和性机制总览 目标 Pod 节点亲和性 nodeSelector nodeAffinity Pod 间亲和性 podAffinity(靠近) podAntiAffinity(远离) 调度亲和性在 CrashLoop 排查中的关键作用 • 错误的 nodeAffinity 硬约束可能导致 Pod 被调度到资源紧张的节点 • podAntiAffinity 配置不当可能使副本过度集中,引发 CPU/内存资源竞争

4.6 调度阶段与亲和性执行顺序

理解亲和性在调度流程中的执行阶段,有助于定位 CrashLoop 根因。调度器执行亲和性检查的顺序如下:

  1. 过滤阶段(Predicates):首先检查 requiredDuringScheduling 硬约束,不满足的节点被直接过滤掉。
  2. 打分阶段(Priorities):对剩余节点按 preferredDuringScheduling 软约束进行加权打分。
  3. 资源检查:在满足亲和性约束的节点中,进一步检查资源(CPU、内存、GPU 等)是否满足 Pod 的 Requests。
  4. 最终选择:选择综合得分最高的节点进行绑定。
CrashLoop 排查要点:如果 Pod 处于 CrashLoopBackOff 状态,且通过 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 值或增加硬约束兜底