系统设计面试:如何设计 Kubernetes Pod 级资源伸缩控制器?
题干与适用场景
一个多容器 Pod 包含 API 代理、缓存 sidecar 和批处理 worker。团队想根据延迟与队列长度动态调整 Pod 级 CPU/内存预算,避免删除 Pod 带来的中断。请设计控制器,说明观测、决策、/resize 子资源写入、状态跟踪、并发保护、不可行请求和回滚。
面试官考察点
- 能否区分期望资源、实际资源和容器重启策略。
- 能否设计幂等 reconcile、状态条件和 deferred/infeasible 重试。
- 能否处理 Pod 级预算与容器级请求的边界及安全护栏。
- 能否把指标、权限、故障恢复和渐进式发布纳入系统设计。
回答前需要澄清的问题
- 集群版本是否支持 Pod-level resize,feature gates 和 kubectl 版本是多少?
- 控制器调整的是 CPU、内存,还是同时调整 Pod 与容器资源?
- 哪些容器允许重启,哪些容器有不可中断的连接或状态?
- 目标是节省成本、维持延迟 SLO,还是应对突发队列?
30 秒回答
我会把控制器做成事件驱动的幂等 reconcile:读取指标与 Pod 当前状态,按预算、SLO、节点容量和变更冷却时间计算目标,然后通过 /resize 子资源提交小步变更。状态中记录 observedGeneration、PodResizePending、PodResizeInProgress、Infeasible 或 Deferred 原因;重试使用指数退避并受优先级和最大次数限制。CPU 与内存分开评估,尊重容器 resizePolicy,并在内存风险或指标回归时恢复上一个已验证预算。所有写入都带 owner、审计和权限边界。
分步骤深入解答
定义资源模型与安全边界
Pod 级 spec.resources 是聚合预算,容器级 requests/limits 仍影响最低保障和重启行为。控制器维护每个工作负载的最小、最大、步长、冷却时间和 SLO 护栏,拒绝超出命名空间配额或节点容量的请求。
采集指标并计算目标
使用延迟、队列长度、CPU throttling、内存工作集和 OOM 事件。采用窗口聚合与滞后,避免单次尖峰造成抖动;目标值必须同时满足 Pod 级预算与容器请求总和约束。
通过 resize 子资源提交幂等更新
控制器只更新期望状态,并携带资源版本和 owner。示例请求:
spec:
resources:
requests:
cpu: "300m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"实际 API 调用使用 /resize 子资源并检查 resourceVersion;冲突时重新读取后再 reconcile,不能覆盖用户或其他控制器的更新。
跟踪条件与重试优先级
读取 PodResizePending、PodResizeInProgress 等条件和 observedGeneration。Infeasible 表示节点或约束无法满足,Deferred 表示暂时延后;控制器应保留原因、下次时间和尝试次数。重试按工作负载优先级、QoS 和等待时长排序,避免低优先级请求饿死。
处理容器重启策略
Pod 级调整可能触发容器级 resizePolicy。CPU 变化通常可不重启,内存变化可能要求重启;控制器必须逐容器检查策略、连接和状态。不可重启容器的请求进入保护队列,不能把 Pod 级成功误报成业务无中断。
观测、回滚与高可用
记录目标值、实际值、条件转移、重启次数、延迟 SLO、内存峰值和失败原因。控制器多副本通过 leader election 工作,队列按 Pod key 去重。发现错误预算、OOM 或 SLO 回归时,回滚到最后一个稳定目标,并暂停自动伸缩等待人工确认。
高质量示范回答
我会设计一个幂等 reconcile 控制器:读取延迟、队列、throttling、工作集和 OOM 指标,结合最小/最大预算、步长、冷却时间、节点容量和命名空间配额计算目标。通过 /resize 子资源提交带 resourceVersion 的小步更新,冲突则重新读取。状态记录 observedGeneration、Pending、InProgress、Infeasible、Deferred 原因和重试时间;重试按优先级和等待时长调度。逐容器检查 resizePolicy,内存变更可能重启时先验证连接与状态。控制器多副本 leader election,指标和审计可追踪;SLO 回归或 OOM 就恢复最后稳定预算并暂停自动化。
常见错误
- 直接修改 Pod spec 并假设 kubelet 会按预期应用,忽略
/resize子资源。 - 只看期望资源,不读取实际资源和状态条件。
- 无冷却和滞后,导致资源抖动和频繁重启。
- 把 Deferred 当作失败永久丢弃,或无限重试 Infeasible。
- 忽略容器级
resizePolicy和内存重启风险。 - 没有 resourceVersion、owner 和 RBAC,覆盖其他控制器的更新。
追问及应对
如何避免两个控制器互相覆盖?
使用明确 owner、resourceVersion、字段管理和单一写入责任;冲突时重新读取并合并意图,不做无条件覆盖。
Infeasible 与 Deferred 如何区分?
Infeasible 表示当前约束无法满足,应调整目标或等待容量;Deferred 表示暂时延迟,应按条件和优先级重试并保留原因。
什么时候暂停自动伸缩?
出现 OOM、SLO 连续回归、条件长期不变、重启超限或观测数据失真时暂停,并保留人工恢复入口。
如何证明控制器没有造成中断?
关联 resize 条件、容器 restartCount、连接错误、延迟和队列指标,按容器策略分层比较;不能只看 Pod phase 仍为 Running。