Kubernetes 面试:如何让 kube-scheduler 的 API 调用不阻塞?
题干与适用场景
一个集群启用了会调用 API Server 的调度插件。API 延迟升高时,调度周期被同步调用占住,待调度 Pod 越积越多。请设计 KEP-5229 所描述的异步处理方案,说明优先级队列、请求去重、失败重试、取消、资源公平和回滚条件。
面试官考察点
- 能否区分调度周期串行语义与 API 副作用的异步执行。
- 是否用有界优先级队列和请求去重避免线程饥饿与重复写入。
- 是否定义幂等键、超时、取消、过期结果和不可重试错误。
- 是否考虑高优先级 Pod、公平性、背压、指标与 feature gate 灰度。
回答前需要澄清的问题
- API 调用是只读查询、幂等写入,还是创建外部资源?不同副作用决定重试边界。
- 调度插件能否接受最终一致的外部状态,还是必须在绑定前得到确认?
- 失败时 Pod 回到 unschedulable 队列,还是只重试 API 操作?两者不可混为一谈。
- 集群的 API QPS、并发调度数和优先级队列预算是多少?
30 秒回答框架
我会把慢 API 操作从调度线程移到有界的优先级队列,调度线程提交带幂等键的任务后继续处理其他 Pod。队列按 Pod 优先级和等待时间服务,并对相同键的请求去重。完成事件只触发安全的重新评估,过期结果不能直接绑定。错误按可重试与永久失败分类,设置超时、取消、退避和并发上限。用待处理数量、队列等待、成功率和调度延迟做灰度门槛,异常时关闭 feature gate 回退同步路径。
分步骤深入解答
1. 划分同步边界
调度周期负责选择节点和维护调度上下文;异步任务负责可延迟的 API 操作。若操作结果是绑定决策的硬前置,就不能简单地“丢到后台”,应先把状态建模为 pending,并在确认前阻止绑定。只有不影响当前周期安全性的工作才可异步化。
2. 设计有界优先级队列
每个任务携带 Pod UID、操作类型、幂等键、截止时间和取消上下文。队列必须有容量上限;满载时向调度器返回明确的 backpressure 信号,而不是无界堆积。高优先级任务先服务,同时用 aging 或配额防止低优先级任务永久饥饿。
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 已被抢占怎么办?
用取消上下文和对象版本检查阻止旧上下文继续写入。成功结果只记录为审计信息,新的调度上下文重新评估,不复用过期绑定意图。