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间亲和性检查时,会执行以下步骤:
- 查找目标Pod:根据
labelSelector在指定namespaces中查找所有匹配的Pod。 - 提取拓扑域:对于每个匹配的Pod,读取其所在节点的
topologyKey对应的标签值,这个值就是该Pod所属的拓扑域。 - 聚合拓扑域:将所有匹配Pod的拓扑域值聚合为一个集合。
- 调度决策:
- 亲和性(podAffinity):当前Pod必须(或尽量)被调度到包含这些拓扑域中任意一个的节点上。
- 反亲和性(podAntiAffinity):当前Pod必须(或尽量)避开这些拓扑域,即不能调度到包含这些拓扑域的节点上。
常见误区:很多初学者误以为
topologyKey 是Pod的标签键。实际上,topologyKey 是节点标签的键,用于从节点上读取拓扑域信息。Pod本身没有拓扑域概念,Pod的拓扑域由其所在节点的标签决定。
4.3 匹配标签(labelSelector)的两种写法
labelSelector 支持两种表达式,与 nodeAffinity 中的 matchExpressions 语法一致:
| 表达式类型 | YAML示例 | 语义 |
|---|---|---|
| matchLabels | |
精确匹配:目标Pod必须同时拥有 app=nginx 和 tier=frontend 标签。 |
| matchExpressions | |
表达式匹配:支持 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=backendPod 运行的节点上。
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字段控制优先级。
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=cachePod 同节点是最优选择,得分最高。 - weight=50:与
app=cachePod 同可用区是次优选择,得分减半。 - 调度器行为:优先选择同节点,如果同节点不可用(如资源不足),则退而求其次选择同可用区。
权重叠加风险:当多个软亲和性/反亲和性规则同时生效时,调度器会将所有规则的得分累加。如果权重设置不合理,可能导致调度器做出反直觉的决策。建议在测试环境中使用
kube-scheduler 的 --v=5 日志级别查看得分详情。