节点亲和性-NodeAffinity
节点亲和性核心概念
节点亲和性(Node Affinity)是 Kubernetes 中比 NodeSelector 更高级的调度机制,它允许您通过复杂的逻辑表达式指定 Pod 应该(或不应该)调度到哪些节点上。
与 NodeSelector 的关键区别
| 特性 | NodeSelector | Node Affinity |
|---|---|---|
| 匹配逻辑 | 完全相等匹配 | 支持多种操作符(In, NotIn, Exists等) |
| 约束强度 | 硬性约束 | 支持软性(preferred)和硬性(required)约束 |
| 表达式复杂度 | 简单键值对 | 支持复杂逻辑表达式 |
| 多条件组合 | AND 逻辑 | 支持复杂组合逻辑 |
节点亲和性类型
requiredDuringSchedulingIgnoredDuringExecution (硬性要求)
- 调度时必须满足的条件
- 不满足则 Pod 保持 Pending 状态
- "IgnoredDuringExecution"表示 Pod 运行后节点标签变化不影响已调度 Pod
preferredDuringSchedulingIgnoredDuringExecution (软性偏好)
- 调度时优先考虑的条件
- 不满足仍可调度,但会影响调度评分
- 可设置权重(weight)表示偏好强度
节点亲和性实战案例解析
案例1:多区域部署(硬性要求)
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity: # 亲和性配置,包含节点亲和性和Pod亲和性/反亲和性
nodeAffinity: # 节点亲和性配置,定义Pod与节点的调度关系
requiredDuringSchedulingIgnoredDuringExecution: # 硬性调度要求(必须满足的条件)
# 注意:即使有多个nodeSelectorTerms,也只需满足其中一个即可(OR逻辑)
nodeSelectorTerms: # 节点选择条件列表(每个term内部是AND逻辑)
- matchExpressions: # 节点标签匹配表达式列表
- key: disktype # 要匹配的节点标签键
operator: In # 匹配操作符(In表示标签值在指定列表中)
values: # 允许的标签值列表(OR逻辑)
- ssd # 必须调度到disktype为ssd...
- antarctica-west1 # ...或antarctica-west1的节点上
# 注意:若找不到匹配节点,Pod将保持Pending状态
containers:
- name: with-node-affinity
image: nginx
imagePullPolicy: Never
# 注意:实际生产环境应设置resources.requests/limits确保调度稳定性配置解析:
- 必须调度到带有
topology.kubernetes.io/zone标签且值为antarctica-east1或antarctica-west1的节点 - 若无匹配节点,Pod 将保持 Pending 状态
- 使用场景:跨可用区部署保证高可用性
案例2:偏好特定节点标签(软性偏好)
apiVersion: v1
kind: Pod
metadata:
name: test-node-affinity
spec:
containers:
- name: test-node-affinity
image: nginx
# 注意:生产环境建议设置resources.requests/limits
affinity: # 亲和性配置
nodeAffinity: # 节点亲和性配置
preferredDuringSchedulingIgnoredDuringExecution: # 软性调度偏好(非强制要求)
# 注意:多个preference之间按weight权重竞争,不影响最终调度结果
- weight: 80 # 权重值(1-100),数值越大优先级越高
preference: # 偏好条件
matchExpressions: # 标签匹配规则
- key: app # 节点标签键
operator: In # 操作符(包含在values列表中)
values: # 期望的标签值
- web # 优先调度到app=web的节点(权重80)
- weight: 20 # 次要权重
preference:
matchExpressions:
- key: tier # 次级优先标签键
operator: In
values:
- frontend # 其次考虑tier=frontend的节点(权重20)
# 关键字段说明:
# 1. preferredDuringSchedulingIgnoredDuringExecution 表示:
# - preferredDuringScheduling: 调度时优先考虑(非强制)
# - IgnoredDuringExecution: Pod运行后节点标签变化不影响已调度Pod
#
# 2. weight 权重特点:
# - 相对值(80 vs 20表示4倍优先级差异)
# - 仅影响调度器评分,不保证绝对调度
# - 所有weight累加不需要等于100
#
# 3. 与requiredDuringScheduling的区别:
# - 若无满足preferred条件的节点,仍会调度到其他节点
# - 调度器会选择综合评分最高的节点
#
# 典型应用场景:
# - 优先但不强制使用SSD节点
# - 多区域部署时优先选择低延迟区域
# - 优先使用有GPU的节点(但非必须)
# 使用建议:
# 1. 先标记节点标签:
# kubectl label nodes <node1> app=web
# kubectl label nodes <node2> tier=frontend
#
# 2. 验证调度结果:
# kubectl get pod test-node-affinity -o wide
#
# 3. 查看调度详情:
# kubectl describe pod test-node-affinity | grep -A 10 Events
#
# 4. 生产环境建议配合使用:
# - requiredDuringScheduling(硬性要求)
# - podAntiAffinity(避免同类Pod集中)
# - resource requests/limits(资源保障)配置解析:
- 优先调度到有
app=web标签的节点(权重80) - 其次考虑有
tier=frontend标签的节点(权重20) - 若无匹配节点仍可调度到其他节点
- 使用场景:优化性能但不强制要求
案例3:组合硬性和软性条件
apiVersion: v1
kind: Pod
metadata:
name: combined-affinity
spec:
affinity: # 亲和性配置,包含节点亲和性和Pod亲和性/反亲和性
nodeAffinity: # 节点亲和性配置
# 硬性要求部分(必须满足的条件)
requiredDuringSchedulingIgnoredDuringExecution: # 调度时必须满足的条件
nodeSelectorTerms: # 节点选择条件列表
- matchExpressions: # 标签匹配表达式列表
- key: kubernetes.io/arch # 系统预定义的节点架构标签
operator: In # 操作符:包含在values列表中
values: # 允许的标签值
- amd64 # 必须运行在amd64架构的节点上
# 注意:若不满足此条件,Pod将保持Pending状态
# 软性偏好部分(优先考虑的条件)
preferredDuringSchedulingIgnoredDuringExecution: # 调度偏好(非强制)
- weight: 1 # 权重值(1-100),影响调度评分
preference: # 偏好条件
matchExpressions: # 标签匹配规则
- key: disktype # 自定义磁盘类型标签
operator: In # 操作符:包含在values列表中
values: # 期望的标签值
- ssd # 优先选择disktype=ssd的节点(但不强制)
# 注意:若无SSD节点,仍可调度到其他满足硬性条件的节点
containers:
- name: nginx
image: nginx
# 生产环境建议添加resources配置:
# resources:
# requests:
# cpu: "100m"
# memory: "128Mi"
# limits:
# cpu: "200m"
# memory: "256Mi"
# 关键字段说明:
# 1. requiredDuringSchedulingIgnoredDuringExecution:
# - 这是硬性约束,节点必须满足条件才能被调度
# - kubernetes.io/arch是Kubernetes自动添加的系统标签
# - 多个matchExpressions之间是AND关系
# 2. preferredDuringSchedulingIgnoredDuringExecution:
# - 这是软性偏好,调度器会优先考虑但不强制
# - weight值用于多个偏好之间的优先级比较
# - 即使没有满足偏好的节点,Pod仍会被调度
# 3. 组合使用策略:
# - 先用required保证基本运行条件(如架构、区域)
# - 再用preferred优化运行时性能(如SSD、GPU)
# 使用场景:
# - 必须运行在特定架构(如amd64)但优先使用高性能存储
# - 必须运行在特定区域但优先使用新型实例类型
# - 必须满足安全合规要求但优先使用资源充足的节点
# 操作建议:
# 1. 标记节点:
# kubectl label nodes <node-name> disktype=ssd
# kubectl get nodes --show-labels # 查看标签
# 2. 验证调度:
# kubectl apply -f pod.yaml
# kubectl get pod -o wide # 查看调度结果
# kubectl describe pod combined-affinity # 查看详细事件
# 3. 生产建议:
# - 配合ResourceQuota使用防止资源耗尽
# - 结合PodDisruptionBudget保证高可用
# - 通过NodeSelector简化简单场景的配置配置解析:
- 必须运行在 amd64 架构的节点上(硬性要求)
- 优先选择有 SSD 磁盘的节点(软性偏好)
- 使用场景:保证兼容性同时优化性能
节点亲和性操作符
节点亲和性支持多种匹配操作符:
| 操作符 | 描述 | 示例 |
|---|---|---|
In | 标签值在指定列表中 | values: ["web", "api"] |
NotIn | 标签值不在指定列表中 | values: ["test"] |
Exists | 标签必须存在(不检查值) | 不设置 values 字段 |
DoesNotExist | 标签必须不存在 | 不设置 values 字段 |
Gt | 标签值大于指定值(数字比较) | values: ["3"] (标签值 > 3) |
Lt | 标签值小于指定值(数字比较) | values: ["5"] (标签值 < 5) |
数字比较示例:
- key: gpu-count
operator: Gt
values: ["2"] # 选择 GPU 数量大于2的节点节点标记与管理命令
节点标签管理
# 查看节点标签
kubectl get nodes --show-labels
# 添加/更新标签
kubectl label nodes <node-name> <label-key>=<label-value>
# 示例:标记节点为 web 类型
kubectl label node k8s-worker01 app=web
# 删除标签
kubectl label nodes <node-name> <label-key>-验证调度结果
# 查看 Pod 调度到的节点
kubectl get pod <pod-name> -o wide
# 查看调度失败原因
kubectl describe pod <pod-name>
# 查看节点详情(包括标签)
kubectl describe node <node-name>Pod亲和
pod的亲和性主要用来解决pod可以和哪些pod部署在同一个集群里面,即拓扑域(由node组成的集群)里面;而pod的反亲和性是为了解决pod不能和哪些pod部署在一起的问题,二者都是为了解决pod之间部署问题。需要注意的是,Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度,不建议在具有几百个节点的集群中使用,而且Pod 反亲和需要对节点进行一致的标记,即集群中的每个节点必须具有适当的标签能够匹配 topologyKey。如果某些或所有节点缺少指定的topologyKey 标签,可能会导致意外行为。
Pod亲和性核心概念
基本定义与作用
Pod亲和性是一种调度策略,允许用户定义新Pod与现有Pod之间的部署关系,包括:
- 亲和性(Pod Affinity):使Pod倾向于与特定标签的Pod部署在同一拓扑域
- 反亲和性(Pod Anti-Affinity):使Pod避免与特定标签的Pod部署在同一拓扑域
与节点亲和性不同,Pod亲和性关注的是Pod-to-Pod的关系而非Pod-to-Node的关系这种机制主要用于:
- 实现服务间的紧密协同部署(如前端与缓存)
- 保证服务的高可用性(分散部署相同服务的多个副本)
- 优化网络通信性能(减少跨节点通信)
- 满足数据局部性需求(如计算与存储的协同定位)
拓扑域(Topology Key)概念
拓扑域是理解Pod亲和性的关键概念,它定义了调度决策的范围边界,通过节点标签来划分:
kubernetes.io/hostname:节点级拓扑域(最细粒度)failure-domain.beta.kubernetes.io/zone:可用区级拓扑域failure-domain.beta.kubernetes.io/region:区域级拓扑域- 自定义标签:如
rack、switch等物理拓扑标签
当使用topologyKey: "kubernetes.io/hostname"时,亲和性规则仅在单个节点范围内生效;而使用区域级标签时,规则会跨多个节点生效
Pod亲和性实现
硬性规则(Required)
测试pod
apiVersion: v1
kind: Pod
metadata:
name: demo-app-backend
labels:
app: backend
spec:
containers:
- name: demo-pod
image: nginx
imagePullPolicy: Never添加标签
# 查看demo-app-backend创建的节点位置
kubectl get pod
# 添加节点标签(拓扑)
kubectl label node k8s-node02 Region=bj节点亲和性
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app-redis
labels:
app: redis
spec:
replicas: 2
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
affinity:
# Pod亲和性规则
podAffinity:
# 强制性的调度规则
requiredDuringSchedulingIgnoredDuringExecution:
# pod 标签选择器
- labelSelector:
matchLabels:
app: backend
# 节点拓扑标签
topologyKey: Region
containers:
- name: redis
image: redis:alpine3.22
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
ports:
- containerPort: 6379
name: redis检测
# 查看Deployment是否创建在和demo-app-backend相同的节点
kubectl get pod -owide软性规则(Preferred)
# 偏好性的调度规则, 但不会影响已在节点上运行的Pod
preferredDuringSchedulingIgnoredDuringExecution:
# 权重范围: 1~100, 权重值越大, 优先级越高
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: "app"
operator: "In"
values: ["db"]
topologyKey: "kubernetes.io/hostname"优先但不强制满足条件,通过weight(1-100)表示优先级
Pod反亲和性实现
测试pod
apiVersion: v1
kind: Pod
metadata:
name: demo-app-backend
labels:
app: backend
spec:
containers:
- name: demo-pod
image: nginx
imagePullPolicy: Never添加标签
# 查看demo-app-backend创建的节点位置
kubectl get pod
# 添加节点标签(拓扑)
kubectl label node k8s-node02 Region=bj节点亲和性
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app-redis
labels:
app: redis
spec:
replicas: 2
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
affinity:
# Pod反亲和性规则
podAntiAffinity:
# 强制性的调度规则
requiredDuringSchedulingIgnoredDuringExecution:
# pod 标签选择器
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
# 节点拓扑标签
topologyKey: Region
containers:
- name: redis
image: redis:alpine3.22
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
ports:
- containerPort: 6379
name: redis检测
# 查看Deployment是否创建在和demo-app-backend在不同的节点
kubectl get pod -owide