Kubernetes

K8s 高阶调度全解

·21 分钟阅读·8283 字
📋 目录

K8s 高阶调度全解

1. 前置:默认调度器基础流程

1.1. 调度整体链路

  1. Pod 未指定节点 → 进入调度队列
  2. 预选阶段(Filter):过滤掉不满足条件的节点,输出候选节点列表
  3. 打分阶段(Score):对候选节点打分,分数最高者胜出
  4. 绑定(Bind):将 Pod 与目标节点绑定,完成调度

1.2. 内置默认预选/打分规则(了解即可)

  • 预选:资源充足、端口不冲突、节点就绪、主机名匹配等
  • 打分:资源均衡分配、最少请求优先等

高阶调度本质就是自定义预选规则、自定义打分规则、增加调度约束


2. 第一部分:节点亲和性 & Pod 亲和/反亲和(Affinity)

用于精准控制 Pod 调度到哪些节点、和哪些 Pod 共处/隔离,是最常用的高阶调度能力,分为三类:

  • 节点亲和 nodeAffinity:Pod ↔ 节点
  • Pod 亲和 podAffinity:Pod ↔ 其他 Pod(尽量放一起)
  • Pod 反亲和 podAntiAffinity:Pod ↔ 其他 Pod(尽量分开)

通用语法规则:
两种策略类型:

  1. requiredDuringSchedulingIgnoredDuringExecution硬约束(必须满足,不满足则调度失败)
  2. preferredDuringSchedulingIgnoredDuringExecution软约束(尽量满足,不满足也可调度)
    后缀 IgnoredDuringExecution 含义:运行中不再校验规则,节点/标签变化不驱逐已运行 Pod。

2.1. 节点亲和 nodeAffinity

根据节点标签筛选节点,替代传统 nodeSelector,能力更强(支持多条件、逻辑运算、软硬约束)。

2.1. 1)nodeSelector(传统基础,对比参考)

仅支持简单标签匹配,无软硬区分:

spec:
  nodeSelector:
    node-role: app

2.2. 2)硬节点亲和(必须满足)

节点不匹配则 Pod 一直处于 Pending

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-required
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        # 多个 matchExpressions 之间:逻辑 OR
        - matchExpressions:
            # 单个表达式内:逻辑 AND
          - key: node-role
            operator: In  # 运算符:In / NotIn / Exists / DoesNotExist / Gt / Lt
            values: ["app", "web"]
          - key: node-arch
            operator: In
            values: ["amd64"]

常用运算符

  • In:标签值在列表中
  • NotIn:标签值不在列表中
  • Exists:节点存在该标签(忽略 value)
  • DoesNotExist:节点不存在该标签
  • Gt/Lt:数值大小比较(多用于版本、资源标签)

2.3. 3)软节点亲和(优先满足)

设置权重 weight(1~100),权重越高优先级越高;无合适节点则调度到其他节点。

spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: node-zone
            operator: In
            values: ["zone-a"]
      - weight: 50
        preference:
          matchExpressions:
          - key: node-zone
            operator: In
            values: ["zone-b"]

2.4. 典型场景

  1. 分区部署:Pod 优先调度到指定可用区;
  2. 机型隔离:高性能应用调度到高配置节点;
  3. 环境隔离:测试 Pod 只跑测试节点,生产只跑生产节点。

2.2. Pod 亲和 podAffinity

让当前 Pod 和 已有指定 Pod 调度到拓扑域内(同节点/同机架/同可用区),实现就近访问、降低网络延迟。

2.1. 核心字段

  • labelSelector:匹配目标 Pod 标签

  • topologyKey:拓扑维度(基于节点标签划分域),常用:

  • kubernetes.io/hostname:按节点划分(同节点)

  • topology.kubernetes.io/zone:按可用区划分

  • topology.kubernetes.io/region:按地域划分

2.2. 示例:硬 Pod 亲和(和 app=redis 的 Pod 同节点)

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: redis  # 目标Pod标签
        topologyKey: kubernetes.io/hostname  # 约束到同一节点

2.3. 适用场景

  • 应用+缓存就近部署,减少跨节点网络开销;
  • 一组关联服务强制部署在同一可用区。

2.3. Pod 反亲和 podAntiAffinity(生产高可用核心)

禁止当前 Pod 和 已有同标签 Pod 部署在同一拓扑域,实现副本打散、单点故障隔离。

线上 Deployment 多副本标配,防止节点宕机导致服务全挂。

2.1. 示例1:硬反亲和(同标签 Pod 不能在同一节点)

Deployment 副本强制打散到不同节点:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-demo
spec:
  replicas: 3
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: web  # 自身标签,禁止同标签Pod共处
            topologyKey: kubernetes.io/hostname

效果:3 个副本一定会分布在 3 个不同节点。

2.2. 示例2:软反亲和(尽量打散)

权重控制优先级,资源不足时允许同节点部署:

podAntiAffinity:
  preferredDuringSchedulingIgnoredDuringExecution:
  - weight: 100
    podAffinityTerm:
      labelSelector:
        matchLabels:
          app: web
      topologyKey: kubernetes.io/hostname

2.3. 典型场景

  1. 高可用:无状态服务副本打散到不同节点/可用区;
  2. 资源隔离:同类高负载应用互相隔离,避免争抢资源;
  3. 多租户:不同租户 Pod 隔离。

2.4. 亲和/反亲和 综合对比 & 踩坑

类型作用对象约束方向核心用途
nodeAffinityPod ↔ 节点选节点机型、区域、环境隔离
podAffinityPod ↔ Pod尽量放一起就近访问、低延迟
podAntiAffinityPod ↔ Pod尽量分开高可用、故障隔离

2.1. 常见坑

  1. 硬约束设置过严,集群节点不足 → Pod 永久 Pending;
  2. topologyKey 节点无对应标签 → 规则失效;
  3. 大规模集群大量硬反亲和,调度性能下降。

3. 第二部分:污点 & 容忍(Taint & Toleration)

节点打污点 → 排斥普通 Pod;Pod 配置容忍 → 允许调度到污点节点
核心用于:节点专用化、节点维护、节点隔离、驱逐 Pod

3.1. 核心概念

  • 污点(Taint):定义在 Node 上,格式:key=value:effect
  • 容忍(Toleration):定义在 Pod 上,表示“可以容忍该污点”

3.2. 污点三大策略(effect)

3.1. NoSchedule

  • 规则:新 Pod 不允许调度到此节点;已运行的 Pod 不受影响。
  • 场景:临时禁用节点调度、专属业务节点。

3.2. PreferNoSchedule

  • 规则:调度器尽量不将 Pod 调度到此节点(软约束);实在无节点也可调度。
  • 场景:节点资源不足预警、优先避让。

3.3. NoExecute

  • 规则:
  1. 新 Pod 禁止调度;
  2. 已运行在该节点、无对应容忍的 Pod 会被驱逐
  3. 可设置容忍时长 tolerationSeconds:延迟驱逐。
  • 场景:节点维护、节点故障、 cordon/uncordon

3.3. 命令行:节点打污点 / 删除污点

# 语法:kubectl taint nodes <节点名> key=value:effect

# 1. 给 node-01 打 NoSchedule 污点
kubectl taint nodes node-01 node-type=db:NoSchedule

# 2. 打 NoExecute 污点,用于节点维护
kubectl taint nodes node-01 maintenance=on:NoExecute

# 3. 删除污点(加减号)
kubectl taint nodes node-01 node-type=db:NoSchedule-

3.4. Pod 容忍配置详解

3.1. 基础语法

spec:
  tolerations:
  - key: "node-type"        # 对应污点 key
    operator: "Equal"       # Equal / Exists
    value: "db"             # Equal 时需要匹配 value
    effect: "NoSchedule"    # 对应污点 effect

3.2. 运算符说明

  1. operator: Equal(默认):key=value 完全匹配污点
  2. operator: Exists:只要节点存在该 key 的污点就容忍(忽略 value)

3.3. 场景1:容忍 NoSchedule 污点(专属节点)

数据库节点打污点,仅数据库 Pod 可调度:

spec:
  tolerations:
  - key: node-type
    operator: Equal
    value: db
    effect: NoSchedule

3.4. 场景2:容忍所有污点(万能容忍,不推荐滥用)

tolerations:
- operator: Exists  # 匹配任意 key、value、effect

3.5. 场景3:NoExecute + 延迟驱逐(维护窗口)

设置 tolerationSeconds: 300,Pod 被标记驱逐后延迟 300s 再销毁,预留业务收尾时间:

tolerations:
- key: maintenance
  operator: Equal
  value: on
  effect: NoExecute
  tolerationSeconds: 300

3.5. 典型生产场景

  1. 专属节点:DB、MQ 中间件节点打 NoSchedule,仅对应组件容忍调度;
  2. 节点维护:节点升级/重启,打 NoExecute 驱逐业务 Pod;
  3. 混合集群隔离:GPU 节点打污点,仅 AI 任务 Pod 可调度;
  4. 集群分区:不同租户、不同环境通过污点隔离节点池。

3.6. 污点+亲和性 组合使用(高阶组合)

流程:

  1. 节点打标签 + 打污点;
  2. Pod 通过 nodeAffinity 选定节点标签;
  3. Pod 通过 tolerations 容忍节点污点。
    实现强隔离专属节点池,生产中间件标准方案。

4. 第三部分:Pod 优先级 & 抢占(PriorityClass)

解决:资源不足时,高优先级 Pod 抢占低优先级 Pod 资源,保障核心业务优先运行。
适用:集群资源满载、突发流量、关键任务保障。

4.1. 核心概念

  1. PriorityClass:全局资源对象,定义优先级名称 & 优先级数值
  2. priorityClassName:Pod/工作负载引用优先级类
  3. 优先级数值范围:0 ~ 10亿,数值越大优先级越高
  4. 抢占机制:高优 Pod 调度失败时,主动驱逐节点上低优 Pod,腾出资源

4.2. 内置默认优先级类

  • system-node-critical:最高优先级,集群核心组件(kube-proxy、coredns)
  • system-cluster-critical:次高,集群基础组件

4.3. 自定义 PriorityClass

4.1. 1)创建 PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: priority-high
value: 10000          # 优先级数值
globalDefault: false  # 是否作为全局默认优先级
description: "高优先级核心业务"

4.2. 2)Pod 引用优先级

apiVersion: v1
kind: Pod
metadata:
  name: high-pod
spec:
  priorityClassName: priority-high
  containers:
  - name: app
    image: nginx

4.4. 抢占机制规则

  1. 集群资源耗尽,高优 Pod 预选通过但资源不足 → 触发抢占;
  2. 调度器从候选节点中,驱逐优先级更低的 Pod;
  3. 被驱逐 Pod 会重新调度到其他节点;
  4. 优先级相同 Pod 不会互相抢占。

4.5. 生产规范 & 限制

  1. 核心业务(订单、支付)设置高优先级;
  2. 测试、离线任务、日志采集设置低优先级;
  3. 禁止普通业务使用超高优先级,避免抢占系统组件;
  4. 抢占会导致 Pod 重建、流量抖动,非必要不开启。

5. 第四部分:Pod 拓扑分布约束(Topology Spread Constraints)

K8s 1.19+ 稳定版,新一代 Pod 打散/均衡策略,比 Pod 反亲和更灵活、功能更强。
目标:让 Pod 副本在多个拓扑域(节点/可用区)均匀分布,提升整体容灾能力。

5.1. 核心字段

  • maxSkew:最大倾斜数(不同拓扑域 Pod 数量差值上限)

  • topologyKey:拓扑维度(节点标签)

  • whenUnsatisfiable:不满足规则时的行为

  • DoNotSchedule:硬约束,不满足则不调度(默认)

  • ScheduleAnyway:软约束,尽量均衡,不满足也调度

  • labelSelector:匹配一组 Pod 进行统计

5.2. 示例:可用区间均匀分布

要求:app=web 的 Pod 在不同可用区差值不超过 1

spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: web

5.3. 和 Pod 反亲和区别

  1. Pod 反亲和:一对一隔离(不能同节点);
  2. 拓扑分布:全局均衡(多节点/多可用区均匀打散);
  3. 大规模多副本、多可用区集群,优先使用 拓扑分布约束

6. 第五部分:节点调度限制(Node 端策略)

6.1. 节点不可调度(Cordon / Uncordon)

手动冻结节点,禁止新 Pod 调度,不驱逐已有 Pod(运维常用)

# 标记节点不可调度
kubectl cordon node-01

# 恢复调度
kubectl uncordon node-01

底层:自动给节点添加 node.kubernetes.io/unschedulable 污点。

6.2. 节点标签调度 + 节点污点综合管控

企业标准节点池划分方案:

  1. 按用途打标签:node-role=app/db/gpu/log
  2. 按区域打标签:topology.kubernetes.io/zone=zone-a
  3. 对应节点打污点,隔离不同业务;
  4. 工作负载使用 nodeAffinity + tolerations 绑定节点池。

7. 第六部分:多调度器 & 调度器扩展(深度高阶)

7.1. 多调度器并行

K8s 允许集群运行多个自定义调度器,不同 Pod 使用不同调度器。

7.1. 1)指定 Pod 使用自定义调度器

spec:
  schedulerName: my-scheduler  # 调度器名称

7.2. 适用场景

  • 离线任务、大数据任务使用专属调度器;
  • 特殊硬件(GPU、FPGA)自定义调度逻辑。

7.2. 调度器扩展框架

7.1. 扩展方式

  1. 调度器插件(Scheduling Framework):K8s 内置框架,替换/新增预选、打分插件(主流)
  2. 外部调度器:独立进程实现全调度逻辑
  3. Admission 准入钩子:调度后二次校验

7.2. 常用官方插件

  • 资源均衡、节点打分、拓扑感知、污点容忍等均以插件形式实现。

8. 第七部分:GPU 设备调度(硬件资源调度)

针对 GPU、RDMA 等异构资源,K8s 通过 Device Plugin 实现调度。

  1. 节点部署 GPU Device Plugin,上报 GPU 卡数量、UUID;
  2. Pod 声明 GPU 资源,调度器筛选有空闲 GPU 的节点;

8.1. 配置示例

spec:
  containers:
  - name: gpu-app
    image: nvidia/cuda
    resources:
      limits:
        nvidia.com/gpu: 1  # 申请1块GPU

特点:GPU 属于独占资源,不支持超配。


9. 生产调度组合最佳实践(落地模板)

9.1. 普通无状态服务(高可用标准)

  1. podAntiAffinity 硬反亲和:副本打散到不同节点;
  2. topologySpreadConstraints:多可用区均匀分布;
  3. 软节点亲和:优先调度到同可用区节点;
  4. 合理设置 Pod 优先级。

9.2. 中间件(DB/MQ 专属节点)

  1. 节点打 NoSchedule 污点;
  2. Pod 配置对应容忍;
  3. 节点亲和锁定专属节点标签;
  4. 高 Pod 优先级,防止被抢占。

9.3. 离线任务(日志、计算、备份)

  1. 低 Pod 优先级;
  2. 节点打 PreferNoSchedule 污点,业务 Pod 优先避让;
  3. 资源宽松,允许超配。

10. 高频面试题(调度必考)

  1. 简述 K8s 默认调度器预选、打分流程?
  2. nodeAffinity、podAffinity、podAntiAffinity 区别与场景?
  3. 污点三种 effect(NoSchedule / PreferNoSchedule / NoExecute)区别?
  4. NoExecute 配合 tolerationSeconds 作用?
  5. Pod 优先级与抢占机制,什么情况下会触发抢占?
  6. 拓扑分布约束和 Pod 反亲和的区别,各自适用场景?
  7. cordon 节点和打 NoExecute 污点有什么不同?
  8. 如何实现中间件专属节点池?(污点 + 容忍 + 节点亲和组合)
  9. 如何让 Deployment 副本强制打散在不同可用区?
  10. 多调度器如何使用?schedulerName 作用?

关联文档