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 分散到不同拓扑域,避免单点故障或资源争抢 |
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 状态。
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
4.5 四种机制的调度决策流程
下图展示调度器在处理 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 副本,防止单节点过载。
- 在 原地升级 场景中,亲和性规则不会自动随容器镜像升级而更新,需通过变更管理流程确保规则与节点能力同步。