Kubernetes 调度

NodeSelector、亲和性、反亲和性、污点容忍、拓扑分布和调度失败排查。

Kubernetes 调度

调度器负责为 Pending Pod 选择运行节点。它会先过滤不满足条件的节点,再对候选节点打分,最后将 Pod 绑定到得分最高的节点。

Kubernetes 调度流程

调度影响因素

因素说明
requests节点可分配资源必须满足 Pod requests
nodeSelectorPod 只能调度到匹配标签的节点
nodeAffinity更灵活的节点亲和性规则
podAffinity让 Pod 调度到已有目标 Pod 所在的拓扑域
podAntiAffinity让 Pod 避开已有目标 Pod 所在的拓扑域
taints / tolerations节点排斥不容忍污点的 Pod
topologySpreadConstraints控制 Pod 在拓扑域中的分布
PVC / StorageClass存储拓扑可能限制可调度节点

nodeSelector

给节点打标签:

kubectl label node worker-01 node-role=frontend

Pod 指定节点标签:

spec:
  # 通过节点标签做最简单的硬性调度约束。
  nodeSelector:
    # 只调度到带有 node-role=frontend 标签的节点。
    node-role: frontend

nodeSelector 简单直接,但表达能力有限。

Node Affinity

硬性节点亲和性:

affinity:
  # 节点亲和性,根据节点标签选择节点。
  nodeAffinity:
    # 硬性规则,不满足时 Pod 不会被调度。
    requiredDuringSchedulingIgnoredDuringExecution:
      # 节点选择条件列表。
      nodeSelectorTerms:
        # 一个 term 内的 matchExpressions 需要同时满足。
        - matchExpressions:
            # 匹配节点标签 key。
            - key: node-role
              # In 表示标签值必须在 values 列表中。
              operator: In
              # 允许的标签值。
              values:
                - frontend

软性节点亲和性:

affinity:
  nodeAffinity:
    # 软性规则,不满足也可以调度,但满足时得分更高。
    preferredDuringSchedulingIgnoredDuringExecution:
      # 偏好权重,范围 1-100。
      - weight: 100
        # 偏好条件。
        preference:
          matchExpressions:
            - key: zone
              operator: In
              values:
                - zone-a

Pod 亲和性

Pod Affinity 根据已有 Pod 的标签来决定新 Pod 应该调度到哪些拓扑域。它常用于让业务 Pod 靠近依赖服务,减少跨节点或跨可用区访问延迟。

让当前 Pod 尽量调度到已有 app=redis 的节点上:

affinity:
  # Pod 亲和性,根据已有 Pod 的标签选择拓扑域。
  podAffinity:
    # 软性规则,满足时优先调度到对应拓扑域。
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        # Pod 亲和条件。
        podAffinityTerm:
          # 匹配已有 Pod 的标签。
          labelSelector:
            matchLabels:
              app: redis
          # 拓扑域。hostname 表示同一节点。
          topologyKey: kubernetes.io/hostname

硬性 Pod 亲和性:

affinity:
  podAffinity:
    # 硬性规则,不满足时 Pod 不会被调度。
    requiredDuringSchedulingIgnoredDuringExecution:
      # 匹配已有 Pod 的标签。
      - labelSelector:
          matchLabels:
            app: redis
        # 拓扑域。hostname 表示同一节点。
        topologyKey: kubernetes.io/hostname

字段说明:

字段说明
labelSelector匹配已有 Pod 的标签
topologyKey拓扑域标签,例如节点、可用区、地域
requiredDuringSchedulingIgnoredDuringExecution硬性规则,不满足就不调度
preferredDuringSchedulingIgnoredDuringExecution软性偏好,不满足也可以调度

使用建议:

  • 优先使用软性亲和,避免依赖 Pod 不存在时导致 Pending。
  • topologyKey: kubernetes.io/hostname 表示靠近到同一节点。
  • topologyKey: topology.kubernetes.io/zone 表示靠近到同一可用区。
  • 不要为了“靠近”牺牲可用性,关键服务通常更需要分散。

Pod 反亲和性

让同一应用副本尽量分散:

affinity:
  # Pod 反亲和性,根据已有 Pod 的标签避开拓扑域。
  podAntiAffinity:
    # 软性规则,满足时尽量避开对应拓扑域。
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        # Pod 反亲和条件。
        podAffinityTerm:
          # 匹配已有 Pod 的标签。
          labelSelector:
            matchLabels:
              app: order-api
          # 拓扑域。hostname 表示尽量不要和匹配 Pod 落在同一节点。
          topologyKey: kubernetes.io/hostname

生产服务建议至少按节点分散,关键服务可以按可用区分散。

Pod 亲和性和反亲和性的差异:

类型目标
podAffinity靠近匹配标签的 Pod
podAntiAffinity避开匹配标签的 Pod

污点和容忍

给节点添加污点:

kubectl taint nodes worker-01 dedicated=infra:NoSchedule

Pod 添加容忍:

# 容忍列表,允许 Pod 调度到带有匹配污点的节点。
tolerations:
  # 污点 key。
  - key: dedicated
    # Equal 表示 key 和 value 都要匹配。
    operator: Equal
    # 污点 value。
    value: infra
    # 污点效果,需要和节点污点 effect 匹配。
    effect: NoSchedule

污点效果:

effect说明
NoSchedule不允许新 Pod 调度到该节点
PreferNoSchedule尽量不调度到该节点
NoExecute不容忍的已有 Pod 也会被驱逐

移除污点:

kubectl taint nodes worker-01 dedicated=infra:NoSchedule-

拓扑分布

控制副本在不同拓扑域中均匀分布:

# 拓扑分布约束,控制副本在不同拓扑域的分散程度。
topologySpreadConstraints:
  # 单个拓扑域与其他拓扑域之间允许的最大副本数差值。
  - maxSkew: 1
    # 拓扑域标签。hostname 表示按节点分布。
    topologyKey: kubernetes.io/hostname
    # 不满足分布约束时禁止调度。
    whenUnsatisfiable: DoNotSchedule
    # 只统计匹配该标签的 Pod。
    labelSelector:
      matchLabels:
        app: order-api

常用拓扑键:

kubernetes.io/hostname
topology.kubernetes.io/zone
topology.kubernetes.io/region

调度失败排查

kubectl describe pod <pod-name> -n default
kubectl get events -n default --sort-by='.lastTimestamp'
kubectl get nodes --show-labels
kubectl describe node <node-name>

常见事件和处理:

事件处理方向
Insufficient cpu降低 requests 或扩容节点
Insufficient memory降低 requests、扩容节点或拆分服务
node(s) had untolerated taint添加 toleration 或移除节点污点
node(s) didn't match Pod's node affinity/selector检查节点标签和亲和性配置
pod has unbound immediate PersistentVolumeClaims检查 PVC、PV、StorageClass
didn't match pod anti-affinity rules调整反亲和性或增加节点

调度策略建议

  • 普通应用先设置合理 requests,再考虑亲和性。
  • 不要滥用硬性亲和性,容易造成 Pending。
  • 基础设施组件使用污点和容忍隔离。
  • 多副本服务使用反亲和性或拓扑分布提升可用性。
  • 有状态服务要同时考虑存储拓扑。
  • 生产变更前先用 kubectl describe pod 查看历史调度事件。