2. 调度队列基础:FIFO队列原理、默认调度队列结构、队列与调度周期的关系
调度队列是Kubernetes调度器的核心数据缓冲层,它决定了Pod被调度器感知、排序和出队的顺序。理解队列结构是掌握多队列排序与延迟优化的前提。本章从最原始的FIFO队列出发,逐步拆解默认调度队列的内部组件,并阐明队列如何驱动调度周期。
2.1 FIFO队列原理与局限性
FIFO(First-In-First-Out)队列是最简单的调度队列模型。Pod按创建时间顺序入队,调度器按相同顺序依次取出并执行调度流程。
| 特性 | 说明 |
|---|---|
| 入队顺序 | Pod创建时间戳严格递增 |
| 出队顺序 | 与入队顺序完全一致 |
| 优先级支持 | 无,所有Pod平等 |
| 公平性 | 严格按到达时间,无抢占 |
| 实现复杂度 | 极低,仅需一个链表或数组 |
FIFO的致命缺陷: 在真实集群中,高优先级的紧急任务(如关键业务Pod)可能被大量低优先级Pod阻塞,导致调度延迟不可控。此外,FIFO无法处理Pod的“背压”和“重试”场景——一个调度失败的Pod会重新排到队尾,造成活锁。
Kubernetes v1.0时代曾短暂使用FIFO,但很快被更复杂的优先级队列取代。FIFO目前仅作为理解调度队列演进的起点,以及某些特殊场景(如测试环境)的备选方案。
2.2 默认调度队列结构
Kubernetes默认调度队列(Scheduling Queue)是一个多级优先级队列,内部由三个子队列组成:activeQ(活跃队列)、backoffQ(退避队列)和unschedulableQ(不可调度队列)。
2.2.1 三个子队列的职责
| 队列名称 | 存储对象 | 排序依据 | 出队条件 |
|---|---|---|---|
| activeQ | 所有就绪且可调度的Pod | 优先级(Priority)→ 创建时间 | 调度器主动Pop |
| backoffQ | 调度失败但可重试的Pod | 退避到期时间 | 退避计时器到期 |
| unschedulableQ | 因资源/约束无法调度的Pod | 插入时间 | 集群事件触发或周期扫描 |
关键设计意图: 三队列分离实现了“快速路径”与“慢速路径”的解耦。activeQ处理正常Pod,backoffQ处理瞬时失败(如端口冲突),unschedulableQ处理长期阻塞(如节点资源不足)。这种设计避免了低效的全局重试。
2.2.2 队列间的流转规则
- 入队: 新Pod或重试Pod默认进入activeQ,按优先级排序。
- 调度失败 → backoffQ: 如果Pod因可重试原因(如节点临时过载)失败,进入backoffQ,等待指数退避(base=1s,max=120s)。
- 调度失败 → unschedulableQ: 如果Pod因不可变约束(如节点污点不匹配)失败,进入unschedulableQ,等待集群事件(如新节点加入)唤醒。
- backoffQ → activeQ: 退避计时器到期后,Pod自动移回activeQ。
- unschedulableQ → activeQ: 当集群发生“可能改变调度结果”的事件(如新增节点、删除Pod、节点标签变更)时,unschedulableQ中的Pod被批量移回activeQ。
2.3 队列与调度周期的关系
调度器的主循环(Scheduling Cycle)与队列紧密耦合。一个完整的调度周期包含以下步骤:
2.3.1 调度周期的关键时序
| 阶段 | 队列操作 | 延迟影响 |
|---|---|---|
| 出队 | activeQ.Pop() | O(log n) 堆操作,n为队列长度 |
| 过滤(Predicate) | 无队列操作 | 取决于节点数量和过滤插件 |
| 打分(Priority) | 无队列操作 | 取决于节点数量和打分插件 |
| 绑定(Bind) | 无队列操作 | API Server响应时间 |
| 失败处理 | AddUnschedulableIfNotPresent() 或 AddToBackoffQ() | O(1) 插入,但退避等待增加端到端延迟 |
优化启示: 调度周期的总延迟 = 出队延迟 + 调度算法延迟 + 失败处理延迟。其中出队延迟受activeQ大小影响,而失败处理延迟受backoffQ和unschedulableQ的流转策略影响。后续章节将深入探讨如何通过多队列排序和参数调优来压缩这些延迟。
2.4 队列参数与默认配置
调度队列的行为由多个参数控制,这些参数在kube-scheduler的配置文件中设置:
# 调度器配置文件片段 (KubeSchedulerConfiguration)
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
queueSort:
# 默认使用 PrioritySort 插件
order: "PrioritySort"
queue:
# 活跃队列最大容量
maxActiveQueueSize: 1000
# 退避基础时间(秒)
backoffBaseSeconds: 1
# 退避最大时间(秒)
backoffMaxSeconds: 120
# 不可调度队列扫描间隔(秒)
unschedulableQInterval: 60
| 参数 | 默认值 | 作用 |
|---|---|---|
maxActiveQueueSize |
1000 | activeQ最大Pod数量,超过时新Pod被拒绝 |
backoffBaseSeconds |
1 | 退避计时器初始值,每次失败翻倍 |
backoffMaxSeconds |
120 | 退避计时器上限,防止无限等待 |
unschedulableQInterval |
60 | 强制扫描unschedulableQ的周期(秒) |
实践建议: 在大型集群(>5000节点)中,建议将
maxActiveQueueSize提升至5000-10000,并适当降低unschedulableQInterval到30秒,以减少Pod在不可调度队列中的等待时间。但需注意,过短的扫描间隔会增加调度器CPU开销。
2.5 本章小结
- FIFO队列是调度队列的原始形态,简单但缺乏优先级和公平性支持,已被多级队列取代。
- 默认调度队列采用三队列结构(activeQ / backoffQ / unschedulableQ),实现了优先级排序、失败重试和长期阻塞的分离管理。
- 调度周期与队列的交互决定了Pod的端到端调度延迟,出队和失败处理是两个主要延迟来源。
- 队列参数(容量、退避时间、扫描间隔)直接影响调度器的吞吐和响应速度,需要根据集群规模合理调整。