代表性面试主题

Kubernetes 面试:如何让 kube-scheduler 的 API 调用不阻塞?

系统设计困难
Offer.cc 编辑团队发布 更新

题干

kube-scheduler 在调度周期中执行慢 API 调用时,如何改造成非阻塞处理,同时保证 Pod 顺序、重试和可观测性?

题干与适用场景

一个集群启用了会调用 API Server 的调度插件。API 延迟升高时,调度周期被同步调用占住,待调度 Pod 越积越多。请设计 KEP-5229 所描述的异步处理方案,说明优先级队列、请求去重、失败重试、取消、资源公平和回滚条件。

面试官考察点

  • 能否区分调度周期串行语义与 API 副作用的异步执行。
  • 是否用有界优先级队列和请求去重避免线程饥饿与重复写入。
  • 是否定义幂等键、超时、取消、过期结果和不可重试错误。
  • 是否考虑高优先级 Pod、公平性、背压、指标与 feature gate 灰度。

回答前需要澄清的问题

  1. API 调用是只读查询、幂等写入,还是创建外部资源?不同副作用决定重试边界。
  2. 调度插件能否接受最终一致的外部状态,还是必须在绑定前得到确认?
  3. 失败时 Pod 回到 unschedulable 队列,还是只重试 API 操作?两者不可混为一谈。
  4. 集群的 API QPS、并发调度数和优先级队列预算是多少?

30 秒回答框架

我会把慢 API 操作从调度线程移到有界的优先级队列,调度线程提交带幂等键的任务后继续处理其他 Pod。队列按 Pod 优先级和等待时间服务,并对相同键的请求去重。完成事件只触发安全的重新评估,过期结果不能直接绑定。错误按可重试与永久失败分类,设置超时、取消、退避和并发上限。用待处理数量、队列等待、成功率和调度延迟做灰度门槛,异常时关闭 feature gate 回退同步路径。

分步骤深入解答

1. 划分同步边界

调度周期负责选择节点和维护调度上下文;异步任务负责可延迟的 API 操作。若操作结果是绑定决策的硬前置,就不能简单地“丢到后台”,应先把状态建模为 pending,并在确认前阻止绑定。只有不影响当前周期安全性的工作才可异步化。

2. 设计有界优先级队列

每个任务携带 Pod UID、操作类型、幂等键、截止时间和取消上下文。队列必须有容量上限;满载时向调度器返回明确的 backpressure 信号,而不是无界堆积。高优先级任务先服务,同时用 aging 或配额防止低优先级任务永久饥饿。

text
submit(key, priority, deadline, operation)
if same key is pending: coalesce(operation)
else if queue is full: return Backpressure
else enqueue(operation)

3. 请求去重与幂等

去重只解决同一逻辑操作的并发合并,不等于 API Server 的幂等保证。写入应使用稳定的资源键、条件更新或服务端幂等语义;重试前检查上一次结果,避免重复创建外部资源。不同版本或不同目标的请求不能因为字符串相似而错误合并。

4. 完成、取消与过期结果

任务完成后发布事件,事件只负责让相关 Pod 重新评估,不直接假设调度状态仍然有效。Pod 被删除、抢占或进入新调度上下文时取消旧任务。超过 deadline 的结果丢弃并记录原因;API 调用成功但上下文已过期时也不能执行旧绑定。

5. 重试、背压与公平

超时、临时网络错误和 429 可按指数退避重试;鉴权失败、参数错误和冲突需要进入永久失败或重新计算路径。并发 worker 数、每插件配额和 API QPS 必须有上限。对待调度 Pod 的重试要和 API 任务重试分开计数,否则一个慢操作会放大整个队列。

6. 兼容、灰度与回滚

保留插件 API 和调度结果语义,用 feature gate 控制异步路径。先在低风险插件、低并发集群启用,比较调度 P99、队列等待、API 延迟、任务失败、重复请求和 unschedulable 重试次数。若队列积压、绑定错误或 API 压力超过基线,停止新任务并排空或取消队列,再回退同步路径。

高质量示范回答

我会先确认哪些 API 操作可以延迟,哪些是绑定前置条件;前者进入有界优先级队列,后者仍保留明确的 pending 状态。任务带 Pod UID、操作类型、幂等键、deadline 和取消上下文,相同键的请求合并,队列满时施加背压。worker 只执行幂等或可安全重试的操作,临时错误退避,永久错误回到插件的失败处理。完成事件触发重新评估,过期结果绝不直接绑定。发布使用 feature gate 和小范围灰度,监测调度 P99、队列深度、API QPS、重复率、失败率和绑定正确性;异常时停止异步提交、清理任务并回退同步路径。

常见错误

  • 把所有 API 调用都放后台 → 绑定前置条件可能被绕过 → 先画出调度上下文和安全状态机。
  • 只有一个全局 FIFO → 高优先级 Pod 被低优先级任务阻塞 → 使用优先级、aging 和配额。
  • 以请求字符串去重 → 不同资源版本或副作用被错误合并 → 使用资源键和明确幂等语义。
  • 无限重试 → API 故障变成队列风暴 → 设 deadline、退避、最大尝试次数和熔断。
  • 只看吞吐 → 调度延迟和错误被掩盖 → 同时观察队列、API、重试和绑定正确性。

追问及应对

如果相同 Pod 同时提交读和写,能合并吗?

不能仅凭 Pod UID 合并。读请求可按资源版本合并,写请求需按操作类型、目标资源和幂等键判断,并保持必要的先后关系。

队列满时应该丢弃哪个任务?

先依据 deadline、优先级和可重建性做策略;不可丢弃的绑定前置任务应产生 backpressure。任何丢弃都要让 Pod 进入可解释的重试或失败状态,而不是静默删除。

API 调用成功但 Pod 已被抢占怎么办?

用取消上下文和对象版本检查阻止旧上下文继续写入。成功结果只记录为审计信息,新的调度上下文重新评估,不复用过期绑定意图。

公开来源

同类题目

相关面试工具

用 Solve 整理系统设计回答

从澄清需求开始,展开规模、架构、组件选择和取舍。

查看工具