4. Kubernetes 亲和性调度基础:nodeSelector、nodeAffinity、podAffinity、podAntiAffinity

在 Kubernetes 集群中,默认调度器(kube-scheduler)基于资源请求与节点可用资源进行“硬匹配”调度。但在 Kata Containers 场景下,由于虚拟机级隔离、特定硬件加速卡(如 QAT、GPU)以及节点内核版本等约束,仅靠资源匹配往往导致 Pod 被调度到不兼容的节点,引发启动失败或性能降级。本章系统梳理 Kubernetes 提供的四种亲和性调度机制,为后续排查 Kata 容器原地升级故障奠定基础。

4.1 调度约束体系总览

Kubernetes 的亲和性调度分为“硬约束”(Required)与“软偏好”(Preferred)两大类,作用于 Pod 与 Node 之间、Pod 与 Pod 之间。下表概括四种机制的核心差异:

机制 作用对象 约束类型 典型用途
nodeSelector Pod → Node 硬约束(简单标签匹配) 将 Pod 固定调度到带有特定标签的节点组
nodeAffinity Pod → Node 硬约束 + 软偏好 基于节点标签的复杂选择,支持多条件逻辑
podAffinity Pod → Pod 硬约束 + 软偏好 将相关 Pod 调度到同一拓扑域(如同一节点、同一可用区)
podAntiAffinity Pod → Pod 硬约束 + 软偏好 将 Pod 分散到不同拓扑域,避免单点故障或资源争抢
关键认知: nodeSelector 是 nodeAffinity 的简化子集。在 Kata 场景中,建议优先使用 nodeAffinity 的 requiredDuringSchedulingIgnoredDuringExecution 来表达“必须运行在支持 Kata 的节点上”这一硬约束。

4.2 nodeSelector:最简标签绑定

nodeSelector 是 Pod spec 中的一个简单字段,要求节点必须包含指定标签且值匹配。语法最简洁,但只支持“相等匹配”,无法表达“不在某集合中”或“标签存在”等逻辑。

apiVersion: v1
kind: Pod
metadata:
  name: kata-pod-simple
spec:
  nodeSelector:
    kata.openshift.io/runtime: "true"
  containers:
  - name: kata-container
    image: busybox
    command: ["sleep", "3600"]

上述配置强制 Pod 只能调度到带有 kata.openshift.io/runtime=true 标签的节点。如果集群中没有节点满足此标签,Pod 将一直处于 Pending 状态。

Kata 实践注意: 不要仅依赖 nodeSelector 做 Kata 节点筛选。当集群中同时存在多个 Kata 节点池(如不同内核版本、不同硬件加速卡)时,nodeSelector 无法表达“优先选择带有 QAT 卡的节点,但如果没有则退回到普通 Kata 节点”这类策略。

4.3 nodeAffinity:灵活的多条件节点选择

nodeAffinity 在 Pod spec 的 spec.affinity.nodeAffinity 字段下定义,支持两种子类型:

  • requiredDuringSchedulingIgnoredDuringExecution:硬约束,调度时必须满足,运行期间节点标签变化不影响已运行 Pod。
  • preferredDuringSchedulingIgnoredDuringExecution:软偏好,调度器尽量满足,但不保证;每个偏好可设置权重(1-100)。

匹配表达式支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 六种操作符,可组合多个 matchExpressions(AND 逻辑)。

apiVersion: v1
kind: Pod
metadata:
  name: kata-pod-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kata.openshift.io/runtime
            operator: In
            values:
            - "true"
          - key: feature.node.kubernetes.io/cpu-cpuid.QAT
            operator: Exists
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: node.kubernetes.io/instance-type
            operator: In
            values:
            - "high-mem"
  containers:
  - name: kata-container
    image: busybox
    command: ["sleep", "3600"]

上述配置表达:Pod 必须调度到同时具备 Kata 运行时标签和 QAT 特性的节点上,且尽量选择 high-mem 实例类型的节点(权重 80)。

4.4 podAffinity 与 podAntiAffinity:Pod 间的拓扑约束

这两种机制通过 spec.affinity.podAffinity 和 spec.affinity.podAntiAffinity 定义,核心参数包括:

  • labelSelector:选择目标 Pod 的标签。
  • topologyKey:定义拓扑域范围的节点标签键(如 kubernetes.io/hostname 表示节点级,topology.kubernetes.io/zone 表示可用区级)。
  • namespaces:指定搜索目标 Pod 的命名空间,默认与当前 Pod 同命名空间。

podAffinity 示例: 将 Kata 运行时 Pod 与对应的 Sidecar 代理调度到同一节点,减少跨节点网络开销。

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - kata-sidecar
        topologyKey: kubernetes.io/hostname

podAntiAffinity 示例: 将同一 Deployment 的 Kata Pod 副本分散到不同节点,避免单节点故障影响所有实例。

spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: kata-workload
          topologyKey: kubernetes.io/hostname
Kata 原地升级关联: 当 Pod 原地升级触发容器重建时,kubelet 不会重新触发调度器决策。如果升级后的 Kata 容器需要新的节点特性(如升级后启用了 SEV 加密),但节点亲和性规则未同步更新,Pod 可能继续运行在不兼容节点上,导致启动失败。这是后续章节排查的核心线索之一。

4.5 四种机制的调度决策流程

下图展示调度器在处理 Pod 亲和性约束时的决策顺序与逻辑分支:

Kubernetes 亲和性调度决策流程 Pod 调度请求 Step 1: nodeSelector 匹配 硬约束:标签键值完全相等 Step 2: nodeAffinity 评估 required(硬)+ preferred(软) 有 podAffinity? Step 3a: podAffinity 匹配 同拓扑域内找目标 Pod Step 3b: podAntiAffinity 匹配 不同拓扑域内避免冲突 Step 4: 综合评分与绑定 软偏好加权 + 节点资源过滤 Pod 调度成功

4.6 常见调度失败模式与排查方向

在 Kata 容器环境中,亲和性配置不当导致的调度失败通常表现为以下模式:

失败模式 可能原因 排查命令/方法
Pod 一直 Pending nodeSelector 或 nodeAffinity required 无匹配节点 kubectl describe pod <pod> 查看 Events 中的 0/3 nodes are available 及具体不匹配原因
Pod 调度到非 Kata 节点 nodeAffinity 使用了 preferred 但权重过低,或标签拼写错误 kubectl get nodes --show-labels 验证节点标签;检查 Pod yaml 中的 key 大小写
同一节点运行多个 Kata Pod 导致性能下降 缺少 podAntiAffinity 约束,或 topologyKey 设置过宽(如 zone 级) 将 topologyKey 改为 kubernetes.io/hostname 实现节点级分散
原地升级后 Pod 启动失败 升级后的容器镜像需要新节点特性,但亲和性规则未更新 检查升级前后 Pod 的 affinity 字段是否一致;对比节点标签变化
排查利器: 使用 kubectl get pod <pod> -o jsonpath='{.spec.affinity}' 快速提取 Pod 的亲和性配置;使用 kubectl describe node <node> | grep -A5 "Labels" 确认节点标签是否满足约束。

4.7 本章小结

nodeSelector、nodeAffinity、podAffinity、podAntiAffinity 构成了 Kubernetes 调度策略的完整工具箱。在 Kata Containers 场景下,正确组合这些机制是保障 Pod 运行在兼容节点上的前提。尤其需要关注:

  • 使用 nodeAffinity required 表达 Kata 运行时硬依赖,避免 Pod 被调度到非 Kata 节点。
  • 使用 podAntiAffinity 分散 Kata Pod 副本,防止单节点过载。
  • 在 原地升级 场景中,亲和性规则不会自动随容器镜像升级而更新,需通过变更管理流程确保规则与节点能力同步。