题干与适用场景
共享计算集群同时承载交互式任务、批处理和可抢占训练作业。租户可以突发提交任务,但不能无限占用 CPU、内存或 GPU;高优先级任务需要更短等待时间,低优先级租户也不能永久饥饿。
请设计队列、资源账本、调度策略、抢占和恢复流程。Kubernetes 将 PriorityClass、ResourceQuota 和抢占分开建模;IETF 的队列管理资料也强调公平调度与拥塞控制需要共同约束,不能只按单一全局 FIFO 排队。
面试官考察点
- 能否区分租户配额、作业优先级和节点可行性。
- 能否给出不会让低优先级作业永久饥饿的公平规则。
- 能否限制抢占的副作用、重试放大和资源碎片。
- 能否处理调度器故障、重复派发和作业恢复。
- 能否用租户级指标证明公平,而不是只展示平均利用率。
回答前需要澄清的问题
- 资源是 CPU、内存、GPU 还是带本地盘的异构节点?
- 配额按租户、项目、队列还是组织层级计算?
- 优先级是否允许抢占,检查点恢复成本多高?
- 作业是否可拆分、可取消、可重试?结果写入是否幂等?
- 公平目标是最大最小份额、加权份额,还是每个租户的等待时间上限?
30 秒回答框架
我会为每个租户维护资源配额、当前使用量和可借用额度,把作业放入按租户分层的队列。调度器先过滤节点约束,再用加权公平或最小虚拟完成时间选择租户;等待时间会增加有效优先级,避免饥饿。只有明确允许抢占且受配额保护时才驱逐低优先级作业。所有分配都带租约和 fencing token,重启后从持久化状态重建,指标按租户、队列和资源类型验证。
分步骤深入解答
第一步:建立资源与配额账本
把 CPU、内存、GPU 和节点标签建成可计算的资源向量。租户配额限制长期占用,突发额度设过期时间;调度前原子预留,任务完成、取消或租约失效时归还。配额检查必须在入口和调度器都存在,避免用户直接创建高优先级任务绕过限制。
第二步:分层队列与公平选择
先按组织、租户和作业类别分层,再在可运行租户之间按权重选择。记录每个租户的虚拟服务量或最近资源使用,选择欠服务租户;等待超过阈值后增加老化分数。一个全局 priority queue 可能让大租户持续占据队头,租户级轮转能把公平边界写入算法。
第三步:优先级、借用和抢占
优先级表达业务紧急度,不自动授予无限资源。租户只能在全局空闲容量或明确借用窗口内超额运行。抢占前计算释放量、检查点成本和受害者预算;优先选择低优先级且可恢复作业。无法证明恢复安全时宁可让高优先级作业等待,避免重复副作用。
第四步:节点过滤与碎片控制
先过滤架构、GPU 型号、区域、亲和性和容量,再排序剩余节点。把大作业与小作业混在同一队列会产生碎片;可为大资源请求保留有限预留池,并设置等待上限。调度记录拒绝原因,区分没有总容量、没有合适形状和配额耗尽。
第五步:派发、租约与幂等恢复
调度器写入带版本的 reservation,工作节点领取短租约并携带 fencing token。重复派发时,执行器用 (job_id, attempt) 做幂等检查;租约过期只允许新 token 接管。结果提交后再释放预留,调度器重启从日志或数据库重建未完成任务,不能依据内存队列猜测状态。
第六步:故障、取消与重试
节点失联先标记未知,再依据租约和心跳窗口决定回收。可恢复作业从检查点重试,非幂等副作用必须走状态查询或补偿。重试消耗租户和作业预算,指数退避并设置上限;调度器故障恢复后禁止把所有超时任务同时重新派发。
第七步:容量扩展与策略变更
增加节点或修改权重时发布版本化策略,保留旧策略处理已入队任务,逐步迁移新任务。权重变化不能让某租户瞬间失去已承诺份额。GPU、区域和本地盘等稀缺资源分别统计,借用和回收都记录审计事件。
第八步:验证公平与效率
用合成租户和真实作业混合压测:一个租户持续满载、多个租户低速提交、节点随机失联、检查点恢复和策略热更新。观察每租户等待 p50/p95、资源份额、最大连续饥饿时长、抢占次数、重复执行、队列年龄、碎片率和恢复时间。比较 FIFO、纯优先级和公平策略,确认公平成本可接受。
高质量示范回答
我会把配额、优先级和节点可行性拆成三个阶段。租户级队列用加权欠服务量选择,等待时间增加老化分数;借用只使用有期限的空闲额度,抢占必须经过可恢复性、配额和检查点成本检查。每次分配持久化 reservation、租约和 fencing token,作业结果按 attempt 幂等提交。调度器从日志恢复,重试有租户预算。压测持续制造 noisy neighbor 和节点故障,按租户等待、份额、饥饿、重复执行、碎片和恢复时间判断设计是否达标。
常见错误
- 只用全局优先队列 → 大租户长期占据队头 → 先选择租户,再选择租户内作业。
- 把配额当成优先级 → 高优先级请求绕过资源边界 → 配额检查独立且原子。
- 无限抢占 → 检查点、重复副作用和抖动失控 → 设抢占预算、恢复协议和冷却时间。
- 仅依赖内存队列 → 重启后重复或丢失派发 → 持久化 reservation、租约和 token。
- 只看集群平均利用率 → 局部租户可能饥饿 → 记录租户级等待和份额分布。
- 节点失联立即重试 → 旧执行可能仍在运行 → 等待 fencing 或走可证明的幂等路径。
追问及应对
如何证明不会饥饿?
给每个可运行租户保留最小服务份额,并让老化分数有上界;在固定容量和持续可调度作业假设下,监控最大等待时间是否受策略上限约束。
抢占后如何避免重复扣费?
把计费绑定到逻辑作业或成功提交的阶段,而不是每次执行尝试;外部副作用使用幂等键和状态查询。
配额与空闲资源冲突怎么办?
允许有期限的借用,并记录可回收额度。配额恢复时先停止新借用,再等待任务自然结束或按策略抢占可恢复工作。
调度器需要强一致吗?
资源 reservation、租约和 fencing 需要线性化条件更新;统计和排行榜可以异步。不要让最终一致的缓存决定是否分配最后一块 GPU。
什么时候不用公平调度?
单租户、固定批处理窗口或严格优先级且业务接受饥饿时,简单优先队列更易验证。需求改变后再引入权重和老化。
哪个信号会触发回滚?
若重复执行、抢占恢复失败、最大等待超标或租户份额偏离预算,立即停止新策略,恢复旧版本并保留 reservation 日志供重放。