4. APF 关键参数详解:queues、handSize、queueLengthLimit、nominalConcurrencyShares、lendablePercent
APF(API Priority and Fairness)的流量整形效果完全由五个核心参数决定。理解每个参数的含义、默认值以及调优方向,是进行容量校准的前提。本章将逐一拆解这些参数,并给出生产环境下的推荐配置思路。
4.1 参数全景图
下表列出了 APF 配置中每个 FlowSchema 和 PriorityLevelConfiguration 所涉及的关键字段及其作用域:
| 参数名称 | 所属对象 | 默认值 | 核心作用 |
|---|---|---|---|
| queues | PriorityLevelConfiguration | 64 | 为该优先级分配的队列数量,决定并发隔离粒度 |
| handSize | PriorityLevelConfiguration | 8 | 每次从队列中取出的请求数,影响公平轮转的步长 |
| queueLengthLimit | PriorityLevelConfiguration | 50 | 单个队列的最大排队深度,超过后请求被拒绝 |
| nominalConcurrencyShares | PriorityLevelConfiguration | (取决于优先级) | 该优先级在总并发配额中的权重比例 |
| lendablePercent | PriorityLevelConfiguration | 50 | 空闲并发配额可借给其他优先级的百分比 |
调优起点: 在修改任何参数前,务必通过
kubectl get prioritylevelconfigurations 和 kubectl get flowschemas 查看当前集群的默认配置。Kubernetes v1.29+ 的默认配置已经过优化,但大型集群仍需根据实际流量模式调整。
4.2 queues —— 并发隔离的粒度
queues 参数定义了某个优先级下有多少个独立的请求队列。APF 使用 Shuffle Sharding 算法将不同的 FlowSchema 请求分散到这些队列中,从而实现流量的细粒度隔离。
- 工作原理: 每个请求根据其 FlowSchema 的标签选择器被哈希到某个队列。队列之间通过公平轮转(Round Robin)竞争执行机会。
- 调优影响:
- 队列数过多(如 >128): 增加调度开销,且每个队列的请求密度降低,可能导致吞吐量下降。
- 队列数过少(如 <16): 隔离性变差,一个高流量 FlowSchema 可能占据整个优先级的大部分队列,导致其他 FlowSchema 被饿死。
- 推荐实践: 对于关键系统优先级(如
workload-high),建议保持 64 或 128;对于低优先级(如catch-all),可适当降低至 32 以减少资源占用。
4.3 handSize —— 公平轮转的步长
handSize 控制 APF 调度器在每次轮转中从每个队列取出的请求数量。它与 queues 共同决定了调度的公平性。
- 数学关系: 调度器在每个调度周期内,会遍历所有队列,并从每个队列中取出
handSize个请求进行处理。因此,一个调度周期内最多处理的请求数为queues × handSize。 - 调优影响:
- handSize 过大: 单个队列的突发流量会快速消耗调度周期,导致其他队列等待时间变长。
- handSize 过小: 调度器频繁切换队列,增加上下文切换开销,降低整体吞吐。
- 推荐实践: 保持默认值 8 通常是最佳选择。如果发现某个优先级下的请求延迟抖动较大,可以尝试将
handSize降低到 4 以提升公平性。
关键公式: 调度周期容量 = queues × handSize。当集群 API Server 的并发处理能力(即
--max-requests-inflight 和 --max-mutating-requests-inflight)固定时,调整这两个参数可以改变请求的排队和分发模式。
4.4 queueLengthLimit —— 排队深度的安全阀
queueLengthLimit 定义了单个队列中最多允许排队的请求数量。当队列已满时,新的请求会被直接拒绝(HTTP 429 Too Many Requests)。
- 默认值 50 的含义: 假设
queues=64,则该优先级下最多可同时排队 64 × 50 = 3200 个请求。超过此阈值后,请求开始被丢弃。 - 调优影响:
- 值过大(如 >200): 队列深度增加,请求在队列中等待的时间变长,导致端到端延迟飙升,且内存占用增加。
- 值过小(如 <10): 队列容易满,导致大量请求被直接拒绝,客户端需要频繁重试,可能引发重试风暴。
- 推荐实践: 对于延迟敏感的关键业务(如
workload-high),建议将queueLengthLimit降低到 20-30,以快速失败而非排队等待;对于批处理或后台任务,可以保持 50 或适当增加到 100。
4.5 nominalConcurrencyShares —— 权重分配的核心
nominalConcurrencyShares 是 APF 中最关键的权重参数。它决定了当集群处于并发压力下时,各个优先级之间如何分配 API Server 的处理能力。
- 计算方式: 每个优先级的并发配额 = (该优先级的 shares / 所有优先级 shares 之和) × 总并发配额。总并发配额由
--max-requests-inflight和--max-mutating-requests-inflight决定。 - 默认值分布: 在 Kubernetes 默认配置中,
workload-high通常为 2000,workload-low为 1000,catch-all为 5。这意味着workload-high获得了绝大部分的并发处理能力。 - 调优影响:
- shares 过高: 该优先级获得过多并发资源,可能导致其他优先级(如系统监控、kube-proxy)被饿死。
- shares 过低: 该优先级下的请求大量排队或拒绝,影响核心业务。
- 推荐实践: 基于历史监控数据(API Server 的请求延迟、拒绝率)进行比例调整。例如,如果
workload-high的请求延迟正常但catch-all的拒绝率过高,可以适当降低workload-high的 shares 或提高catch-all的 shares。
注意:
nominalConcurrencyShares 仅在集群达到并发瓶颈时才生效。在低负载时,所有优先级都可以使用空闲的并发配额(受 lendablePercent 控制)。因此,不要仅根据低负载时的表现来调整 shares。
4.6 lendablePercent —— 空闲配额的借贷机制
lendablePercent 控制一个优先级在空闲时,可以将多少比例的并发配额借给其他优先级使用。这是 APF 实现资源利用率最大化的关键机制。
- 取值范围: 0 到 100。默认值为 50。
- 工作原理:
- 当优先级 A 的实际并发使用量低于其 nominal 配额时,空闲部分(按
lendablePercent比例)可以被其他优先级借用。 - 当优先级 A 的负载上升,需要回收借出的配额时,借用方会立即被抢占(请求可能被重新排队或拒绝)。
- 当优先级 A 的实际并发使用量低于其 nominal 配额时,空闲部分(按
- 调优影响:
- lendablePercent = 100: 完全共享空闲配额,资源利用率最高,但高优先级在突发时可能无法立即获得全部配额(因为部分配额被低优先级占用)。
- lendablePercent = 0: 严格隔离,每个优先级只能使用自己的配额,资源利用率低,但隔离性最强。
- 推荐实践: 对于关键系统优先级(如
workload-high),建议设置lendablePercent = 0或 10,确保其始终拥有完整的并发能力;对于低优先级(如catch-all),可以设置lendablePercent = 100,以充分利用空闲资源。
4.7 参数协同关系图
下图展示了五个参数在 APF 调度周期中的协同工作流程:
4.8 参数调优决策矩阵
在实际调优中,不同场景下的参数调整方向如下表所示:
| 问题现象 | 优先调整参数 | 调整方向 | 预期效果 |
|---|---|---|---|
| 高优先级请求延迟高 | nominalConcurrencyShares | 增加该优先级的 shares | 获得更多并发配额,减少排队 |
| 低优先级请求被大量拒绝 | queueLengthLimit | 适当增加(如 50 → 100) | 提高排队容量,减少 429 |
| 请求延迟抖动明显 | handSize | 降低(如 8 → 4) | 更细粒度的公平轮转,减少突发 |
| 资源利用率低,空闲时浪费 | lendablePercent | 增加(如 50 → 80) | 空闲配额可被其他优先级借用 |
| 关键业务被非关键业务影响 | lendablePercent | 降低关键优先级的借贷比例(如 50 → 0) | 严格隔离,关键业务不受影响 |
| 整体吞吐量不足 | queues | 适当减少(如 64 → 32) | 减少调度开销,提升吞吐 |
实战建议: 每次只调整一个参数,并观察至少 15 分钟的监控数据(重点关注
apiserver_flowcontrol_request_wait_duration_seconds 和 apiserver_flowcontrol_request_rejected_total 指标)。避免同时修改多个参数,否则无法定位根因。