kube-apiserver 如何针对某个 ServiceAccount 创建限流规则
结论
kube-apiserver 的限流由 APF(API Priority and Fairness,API 优先级与公平性) 实现,通过两个集群级对象配合完成:
- PriorityLevelConfiguration(简称 PLC):定义限流策略——并发份额、是否排队、队列参数,是真正“限流”的地方;
- FlowSchema(简称 FS):定义“哪些请求进哪个 PLC”,通过
subjects字段可以精确匹配某个 ServiceAccount(kind: ServiceAccount+serviceAccount.name/serviceAccount.namespace)。
针对某个 ServiceAccount 限流,只需两步:创建一个 Limited 型 PLC(或复用现有 PLC)→ 创建一个 matchingPrecedence 足够小、subjects 命中该 SA 的 FlowSchema 绑定过去。配置热加载,kubectl apply 后立即生效,无需重启 kube-apiserver。
关键前提:默认的 service-accounts FlowSchema 的 matchingPrecedence 是 9000(命中后进 workload-low),自定义 FlowSchema 的 matchingPrecedence 必须小于 9000(例如 8000),才能在该 SA 请求被默认规则截走之前抢先命中。
分析版本
- 软件:Kubernetes kube-apiserver
- 版本:v1.37(2026-08 发布);APF 特性自 v1.29 起稳定(Stable),
flowcontrol.apiserver.k8s.io/v1API 自 v1.29 起稳定 - 源码路径:
staging/src/k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap/default.go(默认对象定义) - 官方文档:https://kubernetes.io/docs/concepts/cluster-administration/flow-control/
详细分析
1. 前置知识:SA 请求在 kube-apiserver 里的身份
Pod 内进程使用 ServiceAccount Token 访问 kube-apiserver 时,认证后得到的身份信息是固定格式:
| 身份字段 | 取值示例 |
|---|---|
| 用户名(user) | system:serviceaccount:default:my-app |
| 所属组(groups) | system:serviceaccounts |
| 所属组(按命名空间) | system:serviceaccounts:default |
| 所属组 | system:authenticated |
因此,在 FlowSchema 里匹配某个 SA 有三种写法:
kind: ServiceAccount+serviceAccount.name/serviceAccount.namespace(推荐,字段可读性最好,name 支持*匹配该命名空间下所有 SA);kind: User+user.name: system:serviceaccount:default:my-app(直接匹配完整用户名);kind: Group+group.name: system:serviceaccounts:default(匹配该命名空间下所有 SA,适合按命名空间整体限流)。
2. APF 分类与限流流程

请求进入 kube-apiserver 后的处理要点:
- FlowSchema 匹配:所有请求按
matchingPrecedence从小到大依次与 FlowSchema 比较,第一个命中即生效(值越小优先级越高)。 - 匹配语义:一个 FlowSchema 内有
rules列表,任意一条 rule 命中即命中;一条 rule 需同时满足subjects(任一命中)AND(resourceRules任一命中 ORnonResourceRules任一命中)。verbs、apiGroups、resources、namespaces、nonResourceURLs以及 subject 的name字段都支持*通配。 - 绑定 PLC:FlowSchema 通过
priorityLevelConfiguration.name绑定到一个 PLC;distinguisherMethod决定 flow 的划分方式(ByUser/ByNamespace/ 空表示单流)。 - PLC 限流:PLC 分为
Exempt(不限流,直接放行)和Limited(限流)。Limited 型按nominalConcurrencyShares在总并发中分得席位;超出并发时按limitResponse处理——Queue排队等待,Reject直接返回 HTTP 429。 - 总并发:默认
--max-requests-inflight=400+--max-mutating-requests-inflight=200= 600 席,各 PLC 按 shares 比例分配,空闲席位可借出(lendablePercent)。
3. v1.37 默认配置:某个 SA 的请求默认去哪
不做任何自定义时,普通命名空间下的 SA 请求会命中默认的 service-accounts FlowSchema,进入 workload-low PLC。v1.37 源码里的默认对象如下:
默认 FlowSchema(按 matchingPrecedence 升序):
| FlowSchema | matchingPrecedence | 绑定的 PLC | 命中条件 |
|---|---|---|---|
| exempt | 1 | exempt | system:masters 组 |
| probes | 2 | exempt | /healthz、/readyz、/livez |
| system-leader-election | 100 | leader-election | 内置控制器的 leader election(leases) |
| system-node-high | 400 | node-high | system:nodes 组的节点状态上报 |
| system-nodes | 500 | system | system:nodes 组的其他请求 |
| kube-controller-manager | 800 | workload-high | kube-controller-manager 用户 |
| kube-scheduler | 800 | workload-high | kube-scheduler 用户 |
| kube-system-service-accounts | 900 | workload-high | kube-system 命名空间下的所有 SA |
| service-accounts | 9000 | workload-low | 所有 SA(system:serviceaccounts 组) |
| global-default | 9900 | global-default | 其余所有已认证/未认证请求 |
| catch-all | 10000 | catch-all | 兜底(Reject,份额极小) |
默认 PriorityLevelConfiguration:
| PLC | nominalConcurrencyShares | lendablePercent | limitResponse | queues / handSize / queueLengthLimit |
|---|---|---|---|---|
| system | 30 | 33% | Queue | 64 / 6 / 50 |
| node-high | 40 | 25% | Queue | 64 / 6 / 50 |
| leader-election | 10 | 0% | Queue | 16 / 4 / 50 |
| workload-high | 40 | 50% | Queue | 128 / 6 / 50 |
| workload-low | 100 | 90% | Queue | 128 / 6 / 50 |
| global-default | 20 | 50% | Queue | 128 / 6 / 50 |
| catch-all | 5 | 0% | Reject | — |
| exempt | — | — | Exempt | — |
可以看到,workload-low 的份额最大(100)、借出比例最高(90%),对普通 SA 比较宽松。如果某个 SA(比如一个写得不好的监控/同步组件)会用大量 LIST 请求打满 apiserver,它会和其他所有普通 SA 共享 workload-low 的 100 份额,互相影响。自定义限流的目的就是把它单独隔离到一个低份额的 PLC,避免拖累别人。
4. 实操:为某个 SA 创建限流规则

假设要限流的目标是 default 命名空间下名为 my-app 的 ServiceAccount。完整 YAML 如下:
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: PriorityLevelConfiguration
metadata:
name: my-app-limited
spec:
type: Limited
limited:
# 并发份额:总并发 600 席中占 5/(所有 Limited PLC shares 之和)
nominalConcurrencyShares: 5
# 空闲席位不借给其他 PLC,隔离更彻底
lendablePercent: 0
limitResponse:
# Queue:超出并发时排队;Reject:超出直接 429
type: Queue
queuing:
queues: 8 # 队列数,越多越公平、内存越大
handSize: 3 # shuffle sharding 每组队列数
queueLengthLimit: 20 # 单队列最大长度
---
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: my-app-fs
spec:
# 必须小于 9000(默认 service-accounts 的 precedence),才能抢先命中
matchingPrecedence: 8000
# 绑定到上面创建的 PLC
priorityLevelConfiguration:
name: my-app-limited
# flow 划分:ByNamespace 表示同一命名空间的请求视为同一条流
distinguisherMethod:
type: ByNamespace
rules:
- subjects:
# 精确匹配 default/my-app 这个 ServiceAccount
- kind: ServiceAccount
serviceAccount:
name: my-app
namespace: default
# 匹配所有资源请求(可按需收窄)
resourceRules:
- verbs: ["*"]
apiGroups: ["*"]
resources: ["*"]
namespaces: ["*"]
clusterScope: true
# 匹配所有非资源请求(如 /api、/apis、/version)
nonResourceRules:
- verbs: ["*"]
nonResourceURLs: ["*"]
应用并确认:
kubectl apply -f my-app-limit.yaml
# prioritylevelconfiguration.flowcontrol.apiserver.k8s.io/my-app-limited created
# flowschema.flowcontrol.apiserver.k8s.io/my-app-fs created
kubectl get flowschemas my-app-fs
kubectl get prioritylevelconfiguration my-app-limited
字段要点说明:
matchingPrecedence: 8000:小于默认service-accounts的 9000,因此该 SA 的请求会先命中my-app-fs,而不是走默认的 workload-low。nominalConcurrencyShares: 5:份额越小,并发上限越低。实际并发席位 = 600 × 5 /(所有 Limited PLC shares 之和)。默认所有 Limited PLC shares 之和 = 30+40+10+40+100+20+5+5 = 250,因此 5 份额约等于 600×5/250 = 12 席(注意:新增 PLC 会让分母变大,其他 PLC 的绝对席位会同比略降)。lendablePercent: 0:该 PLC 的空闲席位不借给别人,别人的空闲席位也不会被它借走(借出是双向的,lendablePercent 控制本 PLC 可借出的比例)。设为 0 隔离最彻底。limitResponse.type: Queue:突发流量先排队而不是直接拒绝,适合对延迟不敏感但不能丢请求的场景;如果希望硬限流直接拒绝,改成Reject。distinguisherMethod.type: ByNamespace:同一命名空间的请求视为同一条 flow,公平排队。如果这个 SA 会被多个命名空间的 Pod 共用(不太常见),可以用ByUser按用户划分。resourceRules里clusterScope: true+namespaces: ["*"]:同时覆盖集群级请求(如 list nodes)和命名空间级请求。如果只写namespaces: ["*"]不写clusterScope: true,集群级请求不会命中。
5. 变体场景
场景 A:只限制该 SA 的 LIST / WATCH 请求(写操作不受限)
把 resourceRules 收窄:
resourceRules:
- verbs: ["get", "list", "watch"]
apiGroups: ["*"]
resources: ["*"]
namespaces: ["*"]
clusterScope: true
这样 create/update/patch/delete 等写请求不会命中这个 FlowSchema,继续走默认的 workload-low。
场景 B:限制某个命名空间下的所有 SA
用 Group 匹配,而不是逐个 SA 写:
subjects:
- kind: Group
group:
name: system:serviceaccounts:my-namespace
场景 C:限制 kube-system 命名空间下的某个 SA
默认 kube-system-service-accounts 的 matchingPrecedence 是 900,因此自定义 FlowSchema 的 precedence 必须小于 900(例如 800)才能抢先命中。注意:kube-system 里跑的大多是关键组件,限流前务必评估影响。
场景 D:不新建 PLC,直接把请求打到现有 PLC
FlowSchema 的 priorityLevelConfiguration.name 可以指向任何已存在的 PLC,包括默认的 workload-low、global-default,甚至强制打到 catch-all(Reject 型,相当于直接拒绝该 SA 的所有请求)。但更推荐新建独立 PLC,这样份额和队列参数可以单独调,不影响默认配置。
6. 验证与监控
查看对象是否生效:
kubectl get flowschemas
kubectl get prioritylevelconfiguration
查看实时限流状态(每个 PLC 的并发、队列、拒绝情况):
kubectl get --raw /debug/api_priority_and_fairness/dump_priority_levels
kubectl get --raw /debug/api_priority_and_fairness/dump_flowschemas
kubectl get --raw /debug/api_priority_and_fairness/dump_queues
dump_priority_levels 输出 CSV,包含每个 PLC 的当前执行请求数、排队请求数、已拒绝数等,可直接确认 my-app-limited 是否在承接流量。
关键 Prometheus 指标(kube-apiserver /metrics):
| 指标 | 含义 |
|---|---|
apiserver_flowcontrol_rejected_requests_total{flow_schema, priority_level, reason} |
被拒绝的请求总数,reason 为 queue-full / concurrency-limit / time-out / cancelled |
apiserver_flowcontrol_current_inqueue_requests{priority_level, flow_schema} |
当前排队中的请求数 |
apiserver_flowcontrol_current_executing_requests{priority_level, flow_schema} |
当前正在执行的请求数 |
apiserver_flowcontrol_current_executing_seats{priority_level, flow_schema} |
当前占用的席位数(大 LIST 会占多席) |
apiserver_flowcontrol_nominal_limit_seats{priority_level} |
该 PLC 的名义并发席位上限 |
apiserver_flowcontrol_request_wait_duration_seconds{flow_schema, priority_level} |
请求排队等待时长直方图 |
验证自定义规则是否命中:
# 用挂了该 SA 的 Pod 发起请求,观察 my-app-fs 的拒绝计数是否增长
kubectl get --raw /metrics | grep apiserver_flowcontrol_rejected_requests_total | grep my-app-fs
7. 注意事项
-
默认对象会被自动维护:kube-apiserver 每分钟 reconcile 一次默认(mandatory + suggested)对象。删除默认对象会被自动重建;修改默认对象的 spec 会被拉回,除非加上注解
apf.kubernetes.io/autoupdate-spec: "false"。自己新建的对象不会被自动覆盖,放心修改。 -
matchingPrecedence 冲突:如果两个 FlowSchema 的 matchingPrecedence 相同且都命中同一个请求,按名称字典序小的生效。应避免依赖这个行为,保持每个 precedence 唯一。
-
watch 请求计入 APF:watch 请求占 1 席,初始突发(推送已有对象)结束后释放席位。但
exec、log(流式)、portforward等长连接请求不受 APF 限流。 -
大 LIST 请求占多席:apiserver 会估算 LIST 返回的对象数,请求占用的席位数与估算对象数成正比。因此一个
list pods --all-namespaces可能占几十席,限流效果比预期更明显。 -
被拒返回 HTTP 429:
Too Many Requests。客户端(尤其是 controller)应做指数退避重试,不要立即重试加剧压力。 -
APF 是并发模型,不是每秒 QPS 限流:它限制的是“同时在处理的请求数”,不是“每秒请求数”。如果需要精确的每秒速率限流(令牌桶),应在网关层(如 ingress / API 网关)做,APF 做不到。
-
调整总并发需要重启:修改
--max-requests-inflight/--max-mutating-requests-inflight需要重启 kube-apiserver 才生效;重启后各 PLC 的绝对席位按 shares 比例同比变化,不需要改 PLC 配置。 -
多实例独立限流:每个 kube-apiserver 实例独立执行 APF 限流(共享 etcd 中的 FlowSchema/PLC 对象,但并发计数是实例级的)。如果有 3 个 apiserver 实例且负载均衡均匀,实际总并发约为单实例的 3 倍。
8. 参考资料
- 官方文档:API Priority and Fairness
- API 参考:FlowSchema v1、PriorityLevelConfiguration v1
- v1.37 源码:
staging/src/k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap/default.go - KEP-1040:API Priority and Fairness
浙公网安备 33010602011771号