土法炼钢 · 系统与基础设施

【kube-apiserver】APF 与 max-in-flight:公平排队、504 与 etcd lag 分列

文章导航

分类入口
kubernetesdistributed
标签入口
#kubernetes#apiserver#apf#flowcontrol#priority#fairqueuing#max-in-flight#v1.30.3

目录

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

每个请求经过一组 FlowSchemamatchingPrecedence(数字越小优先级越高)顺序匹配,取第一个命中的 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: ["*"]

distinguisherMethodByUserByNamespace)决定同一 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 中随机选),放入其中虚拟时钟最小的那个。这保证:

  1. 某个恶意/失控的 flow 的请求分散在少数队列,不能占满所有队列。
  2. 不同 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_secondsetcd_request_duration_seconds
504 + etcd 慢 etcd Storage 轴延迟 etcd_request_duration_seconds{operation="put/range"} 高;APF wait 指标正常
503 Webhook 不可用 Admission 轴,见第 9 篇

分列口令

  1. 先看 apiserver_flowcontrol_request_wait_duration_seconds(APF 轴)。
  2. 若 APF wait 低但 apiserver_request_duration_seconds 高,继续看 etcd_request_duration_seconds(Storage 轴)。
  3. 若 etcd 也低,看 Admission Webhook 轴(apiserver_admission_webhook_latency_seconds)。
  4. 最后才考虑 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 最后兜底

生产调参原则:不要修改 exemptleader-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)

论文(A/B)

站内对照

实验台账


上一篇Authorization 与 Audit:RBAC、SAR 与 403 分列

下一篇CRD / aggregation / 扩展边界

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-08-28 · kubernetes / distributed

【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 轴。


By .