Files
cs-note/hhs/MS/05-部署运维/02-Kubernetes/05-调度控制.md
T
2026-05-24 11:42:38 +08:00

5.6 KiB

tags, create time
tags create time
kubernetes
scheduling
affinity
taints
tolerations
topology
2026-05-18 00:30

K8s 调度控制 — Affinity、Taint & Topology

概述

默认调度器会把 Pod 随便分配到任意可用 Node。想让 Pod 之间"互相躲开"或"必须在一起"?需要使用 K8s 的调度控制机制:Affinity/Anti-Affinity(节点间关系)、Taint/Toleration(节点白名单)、TopologySpreadConstraints(跨可用区分布)。

Affinity / Anti-Affinity

Pod 反亲和性:分散到不同节点

spec:
  affinity:
    # ========== 反亲和性:同一服务的 Pod 分散到不同节点 ==========
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - order-service
          topologyKey: kubernetes.io/hostname
      # ^^^ "尽量"把同 app 的 Pod 分配到不同机器

[!question] 为什么要把同一个服务的 Pod 分散?

如果所有副本都在同一台物理机上,这台机器宕机就会导致服务完全不可用。故障域隔离是高可用的基础——把 Pod 分散到不同节点 = 把鸡蛋放进不同篮子。

节点亲和性:只调度到特定标签的节点

spec:
  affinity:
    # ========== 节点亲和性:只调度到特定标签的节点 ==========
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node-role
            operator: In
            values:
            - high-memory          # 只调度到有 high-memory 标签的节点

调度级别对比

级别 含义 推荐度
requiredDuring... 硬约束,不满足就不调度 高(适用于特殊硬件节点)
preferredDuring... 软约束,尽力而为 高(适用于反亲和性拆分)
ignoringDuringExecution 调度时检查,运行时忽略变化 标准做法

Taint & Toleration — 节点级别的"白名单"

Taint 设在 Node 上,Toleration 设在 Pod 上:

# 给节点加污点:只有带 db 容忍度的 Pod 才能调度上来
kubectl taint nodes node1 dedicated=db:NoSchedule
spec:
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "db"
    effect: "NoSchedule"

[!info] Taint effect 三种类型

Effect 含义 场景
NoSchedule 不接受新 Pod 专用节点
PreferNoSchedule 尽量避免但不强制 优先但不是硬性
NoExecute 驱逐已有的 Pod 节点出问题时
flowchart LR
    Node["Node 打 Taint<br/>dedicated=db:NoSchedule"] --> Check{Pod 有 Toleration?}
    
    Check -- 否 --> Reject["❌ 不允许调度"]
    Check -- 是 --> Accept["✅ 允许调度"]
    
    style Reject fill:#ffebee
    style Accept fill:#e8f5e9

TopologySpreadConstraints — K8s 1.19+ 推荐的跨可用区分布

比 anti-affinity 更优雅的实现方式,特别适合多云和混合云架构:

spec:
  topologySpreadConstraints:
  - maxSkew: 1                 # 任何两个可用区最多差 1 个 Pod
    topologyKey: topology.kubernetes.io/zone   # 按可用区分组
    whenUnsatisfiable: DoNotSchedule           # 不满足则不调度
    labelSelector:
      matchLabels:
        app: order-service

[!tip] WhenUnsatisfiable 策略选择

  • DoNotSchedule:宁可等也不破坏分布均衡(严格一致)
  • ScheduleAnyway:先调度上去,能分尽量分(可用性优先)

生产建议:对外服务用 ScheduleAnyway + 反亲和性;内部有状态组件用 DoNotSchedule。

补充:NodeSelector 最简单的限制

spec:
  nodeSelector:
    disktype: ssd             # 只调度到标注了 disktype=ssd 的节点

[!note] NodeSelector vs Node Affinity

NodeSelector 是最基本的调度方式——只能做相等匹配。如果需要 In/NotIn/Exists 等更复杂的逻辑,应使用 nodeAffinity。现代 K8s 项目中建议统一使用 nodeAffinity 以保持风格一致。

组合使用示例

# 一个典型的 Production Web 部署
spec:
  template:
    spec:
      # ① 必须运行在 Linux x86_64 节点上
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values: [linux]
              - key: kubernetes.io/arch
                operator: In
                values: [amd64]
      
      # ② 尽量分散在不同可用区
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: ScheduleAnyway
        labelSelector:
          matchLabels:
            app: web-frontend
      
      # ③ 避免与后台批处理任务混在同一节点
      podAntiAffinity:
        preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 200
          podAffinityTerm:
            labelSelector:
              matchExpressions:
              - key: workload-type
                operator: In
                values: [batch-job]
            topologyKey: kubernetes.io/hostname

关联笔记