apiserver 的过载保护有两套机制:旧的
--max-requests-inflight /
--max-mutating-requests-inflight
是全局并发上限;新的 API Priority and
Fairness(APF)在此之上提供按流量分类的公平排队。两者在
v1.30 同时存在,但 APF 是默认路径。
最常见的误判是:kubectl 超时返回 504,SRE 立即排查
etcd——但 etcd_request_duration_seconds 显示
etcd 响应 < 10ms。这次 504 的根因是 APF
排队超时:请求在 apiserver 内部等待调度,没有到达 storage
层。 本篇分列这两条路径及对应的证据包。
本篇在系列中的位置
篇目 核心内容 第 11 篇 · Authorization 与 Audit RBAC、SAR、403 分列 第 12 篇 · APF FlowSchema、PriorityLevel、504 分列、max-in-flight 共存 第 13 篇 · CRD 扩展边界 CRD/aggregation 停损线 系列目录 五轴、阅读路径
版本锚定:Kubernetes v1.30.3(tag
v1.30.3)。APF 实现见staging/src/k8s.io/apiserver/pkg/util/flowcontrol/;Fair queuing 算法见staging/src/k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/。APF 在 K8s v1.29 正式 GA(flowcontrol.apiserver.k8s.io/v1);v1.30 中该 API 组版本为 v1。无真实集群则不粘贴伪造 metrics 截图或 kubectl 输出。
一、max-in-flight:旧机制与局限
启动 flag:
| Flag | 默认值 | 控制范围 |
|---|---|---|
--max-requests-inflight |
400 | 非 mutating 请求(GET/LIST/WATCH)的并发上限 |
--max-mutating-requests-inflight |
200 | Mutating 请求(POST/PUT/PATCH/DELETE)的并发上限 |
超限时 apiserver 立即返回 429 Too Many Requests,不排队。
局限:全局计数,无法区分「controller resync LIST 风暴」与「leader election PATCH」——两者共享同一上限,高优先级请求(如节点心跳)在 LIST 风暴中被拒绝与低优先级请求同等对待。
v1.30 中这两个 flag 仍然有效,是 APF 排队之外的一道硬件止血阀。当 APF 并发耗尽时(所有队列都满),请求才被 max-in-flight 前的门槛直接拒绝。实际上,APF 配置合理的情况下,max-in-flight 应该不是第一道被触发的限制。
二、API Priority and Fairness:对象模型
APF 引入两类 cluster-scoped 对象,均在
flowcontrol.apiserver.k8s.io/v1 API 组(v1.30
GA):
FlowSchema
每个请求经过一组 FlowSchema 按
matchingPrecedence(数字越小优先级越高)顺序匹配,取第一个命中的
FlowSchema:
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: leader-election
spec:
matchingPrecedence: 100 # 越小越先匹配
priorityLevelConfiguration:
name: leader-election
rules:
- subjects:
- kind: Group
group:
name: system:masters
resourceRules:
- verbs: ["*"]
apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
namespaces: ["*"]distinguisherMethod(ByUser 或
ByNamespace)决定同一 PriorityLevel
下如何细分流(flow),用于公平排队中隔离不同用户/命名空间。
PriorityLevelConfiguration
FlowSchema 引用的 PriorityLevel 定义并发配额与队列参数:
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: PriorityLevelConfiguration
metadata:
name: workload-high
spec:
type: Limited
limited:
nominalConcurrencyShares: 30
limitResponse:
type: Queue
queuing:
queues: 64 # 队列数(shuffle sharding 用)
handSize: 6 # SFVR hand size
queueLengthLimit: 50 # 单队列最大等待请求数nominalConcurrencyShares 在多个
PriorityLevel
之间按比例分配并发槽位;type: Exempt 的
PriorityLevel(如 exempt,供
system:masters 使用)不受限制。
三、公平排队算法(SFVR / shuffle sharding)
APF 的公平排队核心见
staging/src/k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/,基于
Virtual Finish-time(VF)模型:
每个流(flow)维护一个虚拟时钟
virtualTime,新请求的 VF = max(当前流 VF,
全局最小 VF) + \(\frac{1}{C}\)(\(C\)
为并发上限)。调度器优先分发 VF
最小的请求,实现加权公平排队(WFQ)语义:在均衡负载下,不同流的请求被以其
nominalConcurrencyShares 比例的速率处理。
Shuffle Sharding(SFVR):每个请求用
flow hash 选取 handSize 个队列(从
queues
中随机选),放入其中虚拟时钟最小的那个。这保证:
- 某个恶意/失控的 flow 的请求分散在少数队列,不能占满所有队列。
- 不同 flow 可以在不同队列中并行推进,公平性来自 VF 排序而非严格轮询。
flowchart TD
req["Incoming Request"] --> fs["FlowSchema 匹配(matchingPrecedence)"]
fs --> pl["PriorityLevelConfiguration"]
pl --> exempt["Exempt:跳过排队,直接执行"]
pl --> limited["Limited:计算 flow hash → SFVR 选队列"]
limited --> qfull["队列满 → 429 TooManyRequests"]
limited --> enqueue["入队,等待并发槽位"]
enqueue --> timeout["等待超时(context deadline)→ 504"]
enqueue --> dispatch["并发槽位空闲 → dispatch"]
dispatch --> handler["Request handler → storage.Interface → etcd"]
四、429 / timeout / 504 分列
三种”超载”表现对应三条不同路径,证据包完全不同:
| 症状 | 根因路径 | 关键证据 |
|---|---|---|
| 429 立即返回 | APF 队列满 / max-in-flight 超限 | apiserver_flowcontrol_rejected_requests_total{reason="queue-full"}
或 {reason="concurrency-limit"} |
| 请求等待后成功 / P99 高 | APF 排队等待时间长 | apiserver_flowcontrol_request_wait_duration_seconds
P99 高 |
| 504 / context deadline | APF 等待时间 > client timeout,或 etcd 慢导致 handler 超时 | 需同时看
apiserver_flowcontrol_request_wait_duration_seconds
与 etcd_request_duration_seconds |
| 504 + etcd 慢 | etcd Storage 轴延迟 | etcd_request_duration_seconds{operation="put/range"}
高;APF wait 指标正常 |
| 503 | Webhook 不可用 | Admission 轴,见第 9 篇 |
分列口令:
- 先看
apiserver_flowcontrol_request_wait_duration_seconds(APF 轴)。 - 若 APF wait 低但
apiserver_request_duration_seconds高,继续看etcd_request_duration_seconds(Storage 轴)。 - 若 etcd 也低,看 Admission Webhook
轴(
apiserver_admission_webhook_latency_seconds)。 - 最后才考虑 GC/CPU 压力在 apiserver 进程内的排队。
apiserver_flowcontrol_current_inqueue_requests(gauge,当前各
PriorityLevel 的排队深度)是实时监控 APF
健康的核心指标;排队深度持续不为零说明该优先级并发不足。
五、默认 PriorityLevel 与 FlowSchema
apiserver 启动时自动创建一组内置 FlowSchema 和 PriorityLevel(不可删除,修改会在重启后被重置):
| PriorityLevel | nominalConcurrencyShares | 典型对应场景 |
|---|---|---|
exempt |
无限制 | system:masters;apiserver 自发请求 |
leader-election |
10 | coordination.k8s.io/leases 写操作 |
node-high |
40 | kubelet 节点心跳 |
system |
30 | system:nodes、system:serviceaccounts |
workload-high |
40 | 控制面控制器(deployment/replicaset controller) |
workload-low |
100 | 普通 workload 控制器 |
global-default |
20 | 未命中其他 FlowSchema 的请求 |
catch-all |
5 | 最后兜底 |
生产调参原则:不要修改
exempt 和 leader-election;若
workload-low 排队深,先检查是否有 LIST
风暴(见第 6
篇),而非直接调大
nominalConcurrencyShares。
六、APF 与 max-in-flight:运维复杂度争论
max-in-flight 的优势:单一旋钮,无需理解 FlowSchema 匹配逻辑;在不区分流量类型的场景(如开发集群)配置简单。
APF
的优势:防止低优先级流量饿死高优先级请求(如 LIST
风暴不影响 leader election);公平排队减少 P99
突刺;nominalConcurrencyShares
允许动态调整比例而不重启。
工程间隙:APF 的 SFVR
算法需要在生产集群观察真实流量分布才能合理配置
queues/handSize/queueLengthLimit。过小的
queues 导致 hash 碰撞加剧;过大的
queueLengthLimit
导致请求排队时间过长(等同于缓冲区膨胀,掩盖真实过载)。这与简单
max-in-flight 的调参完全不同,是APF 从 alpha 到 GA
耗时 4 个版本周期的主要原因之一。
开放问题(工程判断):APF 与
max-in-flight 两套机制在同一 apiserver
进程内共存,边界语义并非在所有场景下都清晰;K8s 官方 APF
文档建议「不要依赖 max-in-flight 作为主要限速手段」,但
--max-requests-inflight=0
可能导致非预期行为,生产中通常保留默认值作为最后兜底。APF
调参的 runbook 目前以集群维度调参为主,缺乏通用的 SLO-based
自动调整机制。
七、谱系
简单并发限制(max-in-flight,K8s v1.9 前后)
→ 无法区分流量类型,LIST 风暴饿死心跳
→ APF 设计(K8s Enhancement Proposal #1040,2019)
→ v1.18 alpha → v1.20 beta → v1.29 GA(flowcontrol.apiserver.k8s.io/v1)
→ Fair Queuing for Server Requests(SFVR,见 apiserver pkg 注释中的算法描述)
APF 的 SFVR 算法参考了 Linux 内核 FQ-CoDel(Fair Queuing with Controlled Delay)在网络过载控制中的思路:用 shuffle sharding 隔离 flow,用 virtual finish-time 保证公平性。FQ-CoDel 原始设计见 Nichols & Jacobson(2012),apiserver APF 的 shuffle sharding 是对此思路的工程裁剪,并不是完整实现。
本篇不写什么:FQ-CoDel / CoDel
完整算法(仅注明谱系);未测 APF 延迟数字;伪造
kubectl get prioritylevelconfigurations
输出;Kube scheduler 内部调度器与 APF 无关。
参考资料
规范 / 源码(A)
- Kubernetes v1.30.3(tag
v1.30.3):staging/src/k8s.io/apiserver/pkg/util/flowcontrol/(APF 主路径)staging/src/k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/(SFVR 实现)staging/src/k8s.io/api/flowcontrol/v1/types.go(FlowSchema / PriorityLevelConfiguration 类型)
- K8s · API Priority and Fairness(v1.30)
- KEP-1040: Priority and Fairness for API Server Requests
论文(A/B)
- Nichols & Jacobson, CoDel: Controlled Delay Active Queue Management, ACM Queue 2012(FQ-CoDel 基础,APF shuffle sharding 的思路来源)
站内对照
- 第 9 篇 · Webhook(503 分列)
- 第 6 篇 · List / Pagination(LIST 风暴)
- 第 15 篇 · 排障五轴
- etcd/15 · etcd 排障(etcd_request_duration_seconds 对照)
- PLAN.md §九篇 12
实验台账
- 无集群实测;无伪造 APF metrics 截图或 kubectl flowcontrol 输出。
上一篇:Authorization 与 Audit:RBAC、SAR 与 403 分列
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【kube-apiserver】控制面全景:缺口、五轴坐标系与 16 篇路线
相对 etcd/13、distributed/50、k8s-network 补齐 kube-apiserver 生产内核缺口;以 Storage/Watch/Admission/Auth/APF 五轴为坐标系定义 16 篇阅读路线;版本锚定 Kubernetes v1.30.3。
【kube-apiserver】排障五轴:Storage、Watch、Admission、Auth、APF
按 Storage/Watch/Admission/Auth/APF 五轴做症状否证;给出完整五轴命令表与症状→轴映射(504、410、401、403、webhook 超时、List 风暴、OOM);说明 apiserver_request_duration_seconds 等核心 metrics 语义;并提供决策树:何时穿透到 etcd/15,何时留在 apiserver 轴。
kube-apiserver / Kubernetes 控制面内核:storage、cacher、Admission 与 APF
补齐 etcd 系列 K8s 耦合篇之上的 kube-apiserver 生产内核:storage.Interface、watch cache、resourceVersion、Admission/Webhook、APF,并以排障与相对 etcd 的分层收束。
【kube-apiserver】进程与请求路径:generic apiserver、HandlerChain 与 REST 路由
拆解 kube-apiserver 进程模型与 generic apiserver 框架;钉 HandlerChain 各插槽顺序与失败落点;说明 GVR 路由机制与请求在到达 storage 前可能被拦截的位置。版本锚定 Kubernetes v1.30.3。