3. 亲和性调度基础:nodeSelector、nodeAffinity、podAffinity、podAntiAffinity 概念与区别
在 Kubernetes 集群中,默认调度器(kube-scheduler)基于资源请求和可用节点进行 Pod 分配。但在证书轮换等场景中,我们需要更精细的控制:确保新 Pod 与旧 Pod 运行在同一节点(共享本地证书文件),或强制 Pod 分散到不同节点(避免单点故障)。Kubernetes 提供了四种亲和性调度机制,从简单到复杂,覆盖节点选择与 Pod 间关系。
3.1 四种调度机制总览
| 机制 | 作用对象 | 匹配目标 | 语义 | 强制/偏好 |
|---|---|---|---|---|
| nodeSelector | Pod → 节点 | 节点标签(简单键值对) | 必须匹配所有指定标签 | 强制(硬约束) |
| nodeAffinity | Pod → 节点 | 节点标签(支持表达式) | 匹配一组规则(In/NotIn/Exists等) | 强制 + 偏好 |
| podAffinity | Pod → Pod | 目标 Pod 的标签 | Pod 倾向于与目标 Pod 同拓扑域 | 强制 + 偏好 |
| podAntiAffinity | Pod → Pod | 目标 Pod 的标签 | Pod 倾向于与目标 Pod 不同拓扑域 | 强制 + 偏好 |
核心区别:nodeSelector 和 nodeAffinity 控制 Pod 与节点的关系;podAffinity 和 podAntiAffinity 控制 Pod 与其他 Pod 的关系。证书轮换零停机部署主要依赖 podAffinity(新旧 Pod 同节点)和 podAntiAffinity(避免同一节点上多个副本同时更新)。
3.2 nodeSelector:最简节点选择
nodeSelector 是 Kubernetes 最早提供的节点选择机制,通过在 Pod spec 中添加 nodeSelector 字段,指定一组键值对,调度器只会将 Pod 分配到同时满足所有标签的节点上。
apiVersion: v1
kind: Pod
metadata:
name: cert-renewer
spec:
nodeSelector:
disktype: ssd
zone: us-east-1a
containers:
- name: renewer
image: cert-renewer:1.0
- 优点:语法简单,适合快速固定节点类型(如 GPU 节点、SSD 节点)。
- 缺点:仅支持等值匹配,无法表达“不在某组节点上”或“节点标签值大于某个版本号”等逻辑;所有条件必须同时满足,没有“偏好”模式。
- 适用场景:证书轮换中,如果所有证书节点都打上统一标签(如
cert-node: true),可用 nodeSelector 快速定位。
3.3 nodeAffinity:增强的节点选择
nodeAffinity 是 nodeSelector 的进化版,支持丰富的匹配表达式和强制(required)与偏好(preferred)两种模式。它定义在 Pod spec 的 affinity.nodeAffinity 字段下。
apiVersion: v1
kind: Pod
metadata:
name: cert-renewer-advanced
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: renewer
image: cert-renewer:1.0
| 字段 | 说明 | 示例 |
|---|---|---|
requiredDuringSchedulingIgnoredDuringExecution |
硬约束:调度时必须满足,运行期间忽略节点标签变化 | 节点必须属于 amd64 或 arm64 架构 |
preferredDuringSchedulingIgnoredDuringExecution |
软约束:调度时尽量满足,不满足仍可调度 | 优先选择 SSD 节点(权重 80) |
operator |
匹配操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt | In 表示值在列表中 |
weight |
偏好权重(1-100),越高优先级越大 | 80 表示较高优先级 |
注意:nodeAffinity 的“运行期间忽略”意味着如果节点标签在 Pod 运行后被修改,已经运行的 Pod 不会重新调度。证书轮换场景中,我们通常依赖 Pod 启动时的节点选择,运行期间标签变化不影响已运行的 Pod。
3.4 podAffinity 与 podAntiAffinity:Pod 间关系控制
podAffinity 和 podAntiAffinity 将调度决策从“Pod 与节点”扩展到“Pod 与其他 Pod”。它们使用 拓扑域(topologyKey) 定义“同位置”的范围,例如同一节点、同一可用区或同一区域。
apiVersion: v1
kind: Pod
metadata:
name: cert-consumer
labels:
app: cert-app
version: v2
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cert-app
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cert-app
topologyKey: kubernetes.io/hostname
containers:
- name: consumer
image: cert-consumer:2.0
| 参数 | podAffinity | podAntiAffinity |
|---|---|---|
| 语义 | Pod 倾向于与匹配的 Pod 位于同一拓扑域 | Pod 倾向于与匹配的 Pod 位于不同拓扑域 |
| 典型拓扑键 | kubernetes.io/hostname(同节点)topology.kubernetes.io/zone(同可用区) |
同上 |
| 证书轮换用途 | 新 Pod 与旧 Pod 同节点,共享本地证书文件 | 同一应用的多个副本分散到不同节点,避免同时更新 |
| 性能影响 | 调度器需检查所有已调度 Pod 的标签,集群规模大时可能增加调度延迟 | 同上,且 anti-affinity 通常需要更多计算 |
证书轮换关键组合:在零停机部署中,我们通常同时使用 podAffinity(强制新 Pod 与旧 Pod 同节点)和 podAntiAffinity(偏好同一应用的不同副本分散到不同节点)。前者保证证书文件本地可用,后者保证更新过程中服务不中断。
3.5 四种机制对比 SVG 图
3.6 证书轮换场景中的选择建议
- 定位证书节点:使用
nodeSelector或nodeAffinity将证书管理 Pod 调度到打了特定标签的节点组(如cert-node: true)。 - 新旧 Pod 同节点:使用
podAffinity配合topologyKey: kubernetes.io/hostname,确保新 Pod 与旧 Pod 运行在同一节点,共享本地证书文件。 - 避免同时更新:使用
podAntiAffinity配合topologyKey: kubernetes.io/hostname,确保同一应用的多个副本分散到不同节点,更新时逐个替换,服务不中断。 - 性能考量:大规模集群中,Pod 亲和性调度会增加调度器负载。建议将关键场景设为
required,非关键场景设为preferred以降低调度压力。
最佳实践:在证书轮换部署中,通常将 podAffinity 设为
requiredDuringScheduling(必须同节点),将 podAntiAffinity 设为 preferredDuringScheduling(尽量分散)。这样既保证证书文件本地可用,又尽量提高可用性,同时避免因强制反亲和导致调度失败。