K8s 高阶调度全解
1. 前置:默认调度器基础流程
1.1. 调度整体链路
- Pod 未指定节点 → 进入调度队列
- 预选阶段(Filter):过滤掉不满足条件的节点,输出候选节点列表
- 打分阶段(Score):对候选节点打分,分数最高者胜出
- 绑定(Bind):将 Pod 与目标节点绑定,完成调度
1.2. 内置默认预选/打分规则(了解即可)
- 预选:资源充足、端口不冲突、节点就绪、主机名匹配等
- 打分:资源均衡分配、最少请求优先等
高阶调度本质就是自定义预选规则、自定义打分规则、增加调度约束。
2. 第一部分:节点亲和性 & Pod 亲和/反亲和(Affinity)
用于精准控制 Pod 调度到哪些节点、和哪些 Pod 共处/隔离,是最常用的高阶调度能力,分为三类:
- 节点亲和
nodeAffinity:Pod ↔ 节点 - Pod 亲和
podAffinity:Pod ↔ 其他 Pod(尽量放一起) - Pod 反亲和
podAntiAffinity:Pod ↔ 其他 Pod(尽量分开)
通用语法规则:
两种策略类型:
requiredDuringSchedulingIgnoredDuringExecution:硬约束(必须满足,不满足则调度失败)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. 典型场景
- 分区部署:Pod 优先调度到指定可用区;
- 机型隔离:高性能应用调度到高配置节点;
- 环境隔离:测试 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. 典型场景
- 高可用:无状态服务副本打散到不同节点/可用区;
- 资源隔离:同类高负载应用互相隔离,避免争抢资源;
- 多租户:不同租户 Pod 隔离。
2.4. 亲和/反亲和 综合对比 & 踩坑
| 类型 | 作用对象 | 约束方向 | 核心用途 |
| nodeAffinity | Pod ↔ 节点 | 选节点 | 机型、区域、环境隔离 |
| podAffinity | Pod ↔ Pod | 尽量放一起 | 就近访问、低延迟 |
| podAntiAffinity | Pod ↔ Pod | 尽量分开 | 高可用、故障隔离 |
2.1. 常见坑
- 硬约束设置过严,集群节点不足 → Pod 永久 Pending;
topologyKey节点无对应标签 → 规则失效;- 大规模集群大量硬反亲和,调度性能下降。
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
- 规则:
- 新 Pod 禁止调度;
- 已运行在该节点、无对应容忍的 Pod 会被驱逐;
- 可设置容忍时长
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. 运算符说明
operator: Equal(默认):key=value完全匹配污点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. 典型生产场景
- 专属节点:DB、MQ 中间件节点打
NoSchedule,仅对应组件容忍调度; - 节点维护:节点升级/重启,打
NoExecute驱逐业务 Pod; - 混合集群隔离:GPU 节点打污点,仅 AI 任务 Pod 可调度;
- 集群分区:不同租户、不同环境通过污点隔离节点池。
3.6. 污点+亲和性 组合使用(高阶组合)
流程:
- 节点打标签 + 打污点;
- Pod 通过
nodeAffinity选定节点标签; - Pod 通过
tolerations容忍节点污点。
实现强隔离专属节点池,生产中间件标准方案。
4. 第三部分:Pod 优先级 & 抢占(PriorityClass)
解决:资源不足时,高优先级 Pod 抢占低优先级 Pod 资源,保障核心业务优先运行。
适用:集群资源满载、突发流量、关键任务保障。
4.1. 核心概念
PriorityClass:全局资源对象,定义优先级名称 & 优先级数值priorityClassName:Pod/工作负载引用优先级类- 优先级数值范围:0 ~ 10亿,数值越大优先级越高
- 抢占机制:高优 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. 抢占机制规则
- 集群资源耗尽,高优 Pod 预选通过但资源不足 → 触发抢占;
- 调度器从候选节点中,驱逐优先级更低的 Pod;
- 被驱逐 Pod 会重新调度到其他节点;
- 优先级相同 Pod 不会互相抢占。
4.5. 生产规范 & 限制
- 核心业务(订单、支付)设置高优先级;
- 测试、离线任务、日志采集设置低优先级;
- 禁止普通业务使用超高优先级,避免抢占系统组件;
- 抢占会导致 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 反亲和区别
- Pod 反亲和:一对一隔离(不能同节点);
- 拓扑分布:全局均衡(多节点/多可用区均匀打散);
- 大规模多副本、多可用区集群,优先使用 拓扑分布约束。
6. 第五部分:节点调度限制(Node 端策略)
6.1. 节点不可调度(Cordon / Uncordon)
手动冻结节点,禁止新 Pod 调度,不驱逐已有 Pod(运维常用)
# 标记节点不可调度
kubectl cordon node-01
# 恢复调度
kubectl uncordon node-01
底层:自动给节点添加 node.kubernetes.io/unschedulable 污点。
6.2. 节点标签调度 + 节点污点综合管控
企业标准节点池划分方案:
- 按用途打标签:
node-role=app/db/gpu/log - 按区域打标签:
topology.kubernetes.io/zone=zone-a - 对应节点打污点,隔离不同业务;
- 工作负载使用
nodeAffinity + tolerations绑定节点池。
7. 第六部分:多调度器 & 调度器扩展(深度高阶)
7.1. 多调度器并行
K8s 允许集群运行多个自定义调度器,不同 Pod 使用不同调度器。
7.1. 1)指定 Pod 使用自定义调度器
spec:
schedulerName: my-scheduler # 调度器名称
7.2. 适用场景
- 离线任务、大数据任务使用专属调度器;
- 特殊硬件(GPU、FPGA)自定义调度逻辑。
7.2. 调度器扩展框架
7.1. 扩展方式
- 调度器插件(Scheduling Framework):K8s 内置框架,替换/新增预选、打分插件(主流)
- 外部调度器:独立进程实现全调度逻辑
- Admission 准入钩子:调度后二次校验
7.2. 常用官方插件
- 资源均衡、节点打分、拓扑感知、污点容忍等均以插件形式实现。
8. 第七部分:GPU 设备调度(硬件资源调度)
针对 GPU、RDMA 等异构资源,K8s 通过 Device Plugin 实现调度。
- 节点部署 GPU Device Plugin,上报 GPU 卡数量、UUID;
- 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. 普通无状态服务(高可用标准)
podAntiAffinity硬反亲和:副本打散到不同节点;topologySpreadConstraints:多可用区均匀分布;- 软节点亲和:优先调度到同可用区节点;
- 合理设置 Pod 优先级。
9.2. 中间件(DB/MQ 专属节点)
- 节点打
NoSchedule污点; - Pod 配置对应容忍;
- 节点亲和锁定专属节点标签;
- 高 Pod 优先级,防止被抢占。
9.3. 离线任务(日志、计算、备份)
- 低 Pod 优先级;
- 节点打
PreferNoSchedule污点,业务 Pod 优先避让; - 资源宽松,允许超配。
10. 高频面试题(调度必考)
- 简述 K8s 默认调度器预选、打分流程?
- nodeAffinity、podAffinity、podAntiAffinity 区别与场景?
- 污点三种 effect(NoSchedule / PreferNoSchedule / NoExecute)区别?
- NoExecute 配合 tolerationSeconds 作用?
- Pod 优先级与抢占机制,什么情况下会触发抢占?
- 拓扑分布约束和 Pod 反亲和的区别,各自适用场景?
- cordon 节点和打 NoExecute 污点有什么不同?
- 如何实现中间件专属节点池?(污点 + 容忍 + 节点亲和组合)
- 如何让 Deployment 副本强制打散在不同可用区?
- 多调度器如何使用?schedulerName 作用?
关联文档
- K8s 安全上下文(SecurityContext)全解 — 节点安全隔离
- K8s 高阶配置全解 — 调度与亲和配置