4. Pod间亲和性(podAffinity):拓扑键(topologyKey)与匹配标签

Pod间亲和性(podAffinity)与反亲和性(podAntiAffinity)是Kubernetes调度策略中用于控制Pod之间相对位置关系的核心规则。与节点亲和性(nodeAffinity)不同,Pod间亲和性不关心节点本身的属性,而是关注集群中已经运行的其他Pod的标签分布情况。

其核心机制可以概括为:调度器根据目标Pod的标签选择器,找到满足条件的Pod集合,然后依据拓扑键(topologyKey)定义的故障域范围,决定当前Pod应该被调度到哪个拓扑域中(亲和性),或者避开哪个拓扑域(反亲和性)。

关键区别:节点亲和性依赖节点标签(如 node-role.kubernetes.io/control-plane),而Pod间亲和性依赖Pod标签(如 app=frontend)和拓扑域(如 kubernetes.io/hostname)。

4.1 核心字段解析

一个完整的Pod间亲和性/反亲和性规则由以下三个核心字段构成:

字段 位置 说明 示例值
labelSelector required / preferred 标签选择器,用于匹配目标Pod。支持 matchLabels 和 matchExpressions。 matchLabels: { app: nginx }
topologyKey required / preferred 拓扑域键名,定义“在哪个范围内”评估亲和性。必须与节点标签的key对应。 kubernetes.io/hostname
namespaces required / preferred 指定搜索目标Pod的命名空间列表。不指定则默认使用当前Pod的命名空间。 ["default", "prod"]
实战提示:topologyKey 是Pod间亲和性中最容易出错的字段。它必须是一个有效的节点标签键,且所有参与调度的节点都必须拥有该标签。常见的拓扑键包括 kubernetes.io/hostname(节点级别)、topology.kubernetes.io/zone(可用区级别)、topology.kubernetes.io/region(地域级别)。

4.2 拓扑键(topologyKey)的工作原理

拓扑键决定了亲和性规则的作用范围。调度器在执行Pod间亲和性检查时,会执行以下步骤:

  1. 查找目标Pod:根据 labelSelector 在指定 namespaces 中查找所有匹配的Pod。
  2. 提取拓扑域:对于每个匹配的Pod,读取其所在节点的 topologyKey 对应的标签值,这个值就是该Pod所属的拓扑域。
  3. 聚合拓扑域:将所有匹配Pod的拓扑域值聚合为一个集合。
  4. 调度决策:
    • 亲和性(podAffinity):当前Pod必须(或尽量)被调度到包含这些拓扑域中任意一个的节点上。
    • 反亲和性(podAntiAffinity):当前Pod必须(或尽量)避开这些拓扑域,即不能调度到包含这些拓扑域的节点上。
常见误区:很多初学者误以为 topologyKey 是Pod的标签键。实际上,topologyKey 是节点标签的键,用于从节点上读取拓扑域信息。Pod本身没有拓扑域概念,Pod的拓扑域由其所在节点的标签决定。
Pod间亲和性调度决策流程 待调度Pod 步骤1:labelSelector 匹配目标Pod 步骤2:读取节点 topologyKey 值 步骤3:聚合匹配Pod的拓扑域集合 反亲和性:避开这些拓扑域 亲和性:调度到这些拓扑域 拓扑域由节点标签决定,例如 topology.kubernetes.io/zone=us-east-1a 表示一个可用区拓扑域

4.3 匹配标签(labelSelector)的两种写法

labelSelector 支持两种表达式,与 nodeAffinity 中的 matchExpressions 语法一致:

表达式类型 YAML示例 语义
matchLabels
matchLabels:
  app: nginx
  tier: frontend
精确匹配:目标Pod必须同时拥有 app=nginx 和 tier=frontend 标签。
matchExpressions
matchExpressions:
  - key: app
    operator: In
    values: [nginx, apache]
  - key: tier
    operator: NotIn
    values: [backend]
表达式匹配:支持 In、NotIn、Exists、DoesNotExist 操作符,更灵活。
性能建议:当 matchExpressions 中的 values 列表超过10个值时,调度器的标签匹配性能会显著下降。对于大规模集群,建议优先使用 matchLabels 或减少 values 数量。

4.4 实际配置示例:微服务就近部署

以下是一个典型的微服务场景:要求 frontend Pod 与 backend Pod 部署在同一节点上,以减少网络延迟。

apiVersion: v1
kind: Pod
metadata:
  name: frontend-pod
  labels:
    app: frontend
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: backend
        topologyKey: kubernetes.io/hostname
  containers:
  - name: frontend
    image: nginx:1.25

上述配置的含义:

  • requiredDuringSchedulingIgnoredDuringExecution:硬性要求,调度时必须满足。
  • labelSelector:匹配标签为 app=backend 的Pod。
  • topologyKey:使用 kubernetes.io/hostname,即节点级别。
  • 调度结果:frontend Pod 必须被调度到至少有一个 app=backend Pod 运行的节点上。

4.5 反亲和性示例:避免单点故障

反亲和性常用于将同一应用的多个副本分散到不同拓扑域,以提高可用性。例如,将 nginx 的3个副本分散到不同节点:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ha
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: nginx
            topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx:1.25
注意:当使用 requiredDuringSchedulingIgnoredDuringExecution 反亲和性且 topologyKey 为 kubernetes.io/hostname 时,如果集群节点数少于副本数(例如3个副本但只有2个节点),则多余的Pod将一直处于 Pending 状态,因为调度器找不到满足条件的节点。此时应改用 preferredDuringSchedulingIgnoredDuringExecution(软性要求)。

4.6 调度策略类型对比

策略类型 YAML关键字 调度行为 典型场景
硬亲和性 requiredDuringSchedulingIgnoredDuringExecution 必须满足,否则Pod无法调度 缓存与计算节点必须同节点
软亲和性 preferredDuringSchedulingIgnoredDuringExecution 尽量满足,不满足时调度到其他节点 优先将同服务Pod部署在同一可用区
硬反亲和性 requiredDuringSchedulingIgnoredDuringExecution 必须避开,否则Pod无法调度 关键服务副本必须分散到不同节点
软反亲和性 preferredDuringSchedulingIgnoredDuringExecution 尽量避开,不满足时允许同节点 非关键服务尽量分散但不强制

4.7 常见问题与避坑指南

  • 拓扑键不存在:如果某些节点缺少 topologyKey 对应的标签,调度器会忽略这些节点,可能导致调度范围缩小。建议使用 kubectl label nodes 确保所有节点都有该标签。
  • 命名空间遗漏:默认情况下,labelSelector 只搜索当前Pod所在的命名空间。如果需要跨命名空间匹配,必须显式指定 namespaces 字段。
  • 循环依赖:两个Deployment互相设置硬亲和性,可能导致所有Pod都无法调度。设计时应避免循环依赖,或使用软亲和性。
  • 性能开销:Pod间亲和性调度需要查询集群中所有匹配的Pod,当集群规模超过1000个节点时,调度延迟会明显增加。建议仅在必要时使用硬亲和性,软亲和性可以配合 weight 字段控制优先级。
亲和性 vs 反亲和性:拓扑域对比 亲和性:调度到同一拓扑域 节点A zone=us-east-1a 节点B zone=us-east-1b app=backend 待调度Pod topologyKey: topology.kubernetes.io/zone 待调度Pod必须调度到 zone=us-east-1a 即节点A上 反亲和性:避开同一拓扑域 节点C zone=us-east-1a 节点D zone=us-east-1b app=backend 待调度Pod topologyKey: topology.kubernetes.io/zone 待调度Pod必须避开 zone=us-east-1a 即调度到节点D上

4.8 软亲和性(preferred)的权重机制

软亲和性/反亲和性通过 weight 字段控制优先级,调度器会计算每个候选节点的得分,得分高的节点优先被选中。

affinity:
  podAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchLabels:
            app: cache
        topologyKey: kubernetes.io/hostname
    - weight: 50
      podAffinityTerm:
        labelSelector:
          matchLabels:
            app: cache
        topologyKey: topology.kubernetes.io/zone

上述配置的含义:

  • weight=100:与 app=cache Pod 同节点是最优选择,得分最高。
  • weight=50:与 app=cache Pod 同可用区是次优选择,得分减半。
  • 调度器行为:优先选择同节点,如果同节点不可用(如资源不足),则退而求其次选择同可用区。
权重叠加风险:当多个软亲和性/反亲和性规则同时生效时,调度器会将所有规则的得分累加。如果权重设置不合理,可能导致调度器做出反直觉的决策。建议在测试环境中使用 kube-scheduler 的 --v=5 日志级别查看得分详情。