Skip to content

节点亲和性-NodeAffinity

节点亲和性核心概念

节点亲和性(Node Affinity)是 Kubernetes 中比 NodeSelector 更高级的调度机制,它允许您通过复杂的逻辑表达式指定 Pod 应该(或不应该)调度到哪些节点上。

与 NodeSelector 的关键区别

特性NodeSelectorNode Affinity
匹配逻辑完全相等匹配支持多种操作符(In, NotIn, Exists等)
约束强度硬性约束支持软性(preferred)和硬性(required)约束
表达式复杂度简单键值对支持复杂逻辑表达式
多条件组合AND 逻辑支持复杂组合逻辑

节点亲和性类型

  1. requiredDuringSchedulingIgnoredDuringExecution (硬性要求)

    • 调度时必须满足的条件
    • 不满足则 Pod 保持 Pending 状态
    • "IgnoredDuringExecution"表示 Pod 运行后节点标签变化不影响已调度 Pod
  2. preferredDuringSchedulingIgnoredDuringExecution (软性偏好)

    • 调度时优先考虑的条件
    • 不满足仍可调度,但会影响调度评分
    • 可设置权重(weight)表示偏好强度

节点亲和性实战案例解析

案例1:多区域部署(硬性要求)

yaml
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-east1antarctica-west1 的节点
  • 若无匹配节点,Pod 将保持 Pending 状态
  • 使用场景:跨可用区部署保证高可用性

案例2:偏好特定节点标签(软性偏好)

yaml
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:组合硬性和软性条件

yaml
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)

数字比较示例

yaml
- key: gpu-count
  operator: Gt
  values: ["2"] # 选择 GPU 数量大于2的节点

节点标记与管理命令

节点标签管理

bash
# 查看节点标签
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>-

验证调度结果

bash
# 查看 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:区域级拓扑域
  • 自定义标签:如rackswitch等物理拓扑标签

当使用topologyKey: "kubernetes.io/hostname"时,亲和性规则仅在单个节点范围内生效;而使用区域级标签时,规则会跨多个节点生效

Pod亲和性实现

硬性规则(Required)

测试pod

yaml
apiVersion: v1
kind: Pod
metadata:
  name: demo-app-backend
  labels:
    app: backend
spec:
  containers:
    - name: demo-pod
      image: nginx
      imagePullPolicy: Never

添加标签

bash
# 查看demo-app-backend创建的节点位置
kubectl get pod
# 添加节点标签(拓扑)
kubectl label node k8s-node02 Region=bj

节点亲和性

yaml
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

检测

bash
# 查看Deployment是否创建在和demo-app-backend相同的节点
kubectl get pod -owide

软性规则(Preferred)

yaml
# 偏好性的调度规则, 但不会影响已在节点上运行的Pod
preferredDuringSchedulingIgnoredDuringExecution:
  # 权重范围: 1~100, 权重值越大, 优先级越高
  - weight: 100
    podAffinityTerm:
      labelSelector:
        matchExpressions:
          - key: "app"
            operator: "In"
            values: ["db"]
      topologyKey: "kubernetes.io/hostname"

优先但不强制满足条件,通过weight(1-100)表示优先级

Pod反亲和性实现

测试pod

yaml
apiVersion: v1
kind: Pod
metadata:
  name: demo-app-backend
  labels:
    app: backend
spec:
  containers:
    - name: demo-pod
      image: nginx
      imagePullPolicy: Never

添加标签

bash
# 查看demo-app-backend创建的节点位置
kubectl get pod
# 添加节点标签(拓扑)
kubectl label node k8s-node02 Region=bj

节点亲和性

yaml
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

检测

bash
# 查看Deployment是否创建在和demo-app-backend在不同的节点
kubectl get pod -owide