Kubernetes v1.29 [稳定]在超载情况下控制 Kubernetes API 服务器的行为是集群管理员的关键任务。kube-apiserver 提供了一些可用的控制手段(即命令行参数 --max-requests-inflight 和 --max-mutating-requests-inflight),以限制待处理请求的数量,从而防止大量入站请求使 API 服务器过载甚至崩溃。然而,这些标志不足以确保在流量高峰期最重要的请求能够通过。
API 优先级与公平性(API Priority and Fairness,APF)特性是上述最大并发请求限制的一种改进替代方案。APF 以更细粒度的方式对请求进行分类和隔离。它还引入了有限的排队机制,以便在极短的突发流量情况下不会拒绝任何请求。请求使用公平排队技术从队列中分发,例如,行为不端的控制器无需导致其他控制器(即使在相同优先级级别下)资源匮乏。
此特性旨在与标准控制器配合使用,这些控制器使用 informer 并通过指数回退(exponential back-off)来应对 API 请求的失败,同时也适用于以相同方式运作的其他客户端。
--max-requests-inflight 标志的情况也是如此。API 优先级与公平性确实适用于 watch 请求。当禁用 API 优先级与公平性时,watch 请求不受 --max-requests-inflight 限制。API 优先级与公平性特性通过命令行标志控制,默认启用。有关可用 kube-apiserver 命令行选项及其启用和禁用方法的通用说明,请参阅选项。APF 的命令行选项名称为 "--enable-priority-and-fairness"。该特性还涉及一个API 组,包含:(a) 在 1.29 中引入并默认启用的稳定 v1 版本;(b) 默认启用但在 v1.29 中被弃用的 v1beta3 版本。您可以通过在 kube-apiserver 启动命令中添加以下命令行标志来禁用 API 组 beta 版本 v1beta3:
kube-apiserver \
--runtime-config=flowcontrol.apiserver.k8s.io/v1beta3=false \
# …and other flags as usual
命令行标志 --enable-priority-and-fairness=false 将禁用 API 优先级与公平性特性。
在递归服务器场景中必须谨慎使用 API 优先级与公平性。在这些场景中,某个服务器 A 在服务请求时,会向某个服务器 B 发出附属请求。甚至服务器 B 可能还会向服务器 A 发起进一步的附属调用。在无论递归多深的情况下,如果对原始请求和某些附属请求都应用优先级与公平性控制,则存在优先级倒置和/或死锁的风险。
递归的一个例子是当 kube-apiserver 向服务器 B 发出准入 Webhook 调用,而在服务该调用时,服务器 B 又向 kube-apiserver 发出进一步的附属请求。递归的另一个例子是当 APIService 对象指示 kube-apiserver 将关于特定 API 组的请求委托给自定义外部服务器 B 时(这被称为“聚合”)。
当已知原始请求属于特定优先级级别,而附属受控请求被归类为更高优先级级别时,这是一种可能的解决方案。当原始请求可能属于任何优先级级别时,附属受控请求必须豁免于优先级与公平性限制。一种方法是使用下文讨论的配置分类和处理的对象。另一种方法是使用上述技术完全禁用服务器 B 上的优先级与公平性。第三种方法(当服务器 B 不是 kube-apiserver 时最简单)是在代码构建时禁用服务器 B 的优先级与公平性。
API 优先级与公平性特性涉及几个不同的功能。入站请求使用 FlowSchemas 根据请求属性进行分类,并分配给优先级级别。优先级级别通过维护独立的并发限制来增加隔离度,从而使分配到不同优先级级别的请求不会互相匮乏资源。在同一个优先级级别内,公平排队算法可防止来自不同 流(flow) 的请求互相匮乏,并允许将请求排队,以防止当平均负载足够低时因流量突发导致请求失败。
未启用 APF 时,API 服务器中的总并发量受 kube-apiserver 标志 --max-requests-inflight 和 --max-mutating-requests-inflight 限制。启用 APF 后,这些标志定义的并发限制将相加,然后将总和分配给一组可配置的 优先级级别(priority levels)。每个入站请求都被分配到一个单一的优先级级别,并且每个优先级级别将仅分发其特定限制所允许数量的并发请求。
例如,默认配置包括用于领导选举请求、来自内置控制器的请求以及来自 Pod 的请求的独立优先级级别。这意味着行为不端的 Pod 如果用请求轰炸 API 服务器,将无法阻止领导选举或内置控制器的操作成功。
优先级级别的并发限制会定期调整,允许利用率不足的优先级级别暂时将其并发额度借出给高利用率级别。这些限制基于名义限制以及优先级级别可能借出和借入多少并发量的界限,所有这些都源自下文提到的配置对象。
上述并发管理描述是基础情况。请求具有不同的持续时间,但在与优先级级别的并发限制进行比较时,它们在任何给定时刻的计数方式相同。在基础情况中,每个请求占用一个并发单元。术语“席位(seat)”用于表示一个并发单元,灵感来自于火车或飞机上的每位乘客占用固定供应席位中的一个的方式。
但有些请求会占用多个席位。其中一些是服务器估计会返回大量对象的 list 请求。事实证明,这些请求会给服务器带来极其沉重的负担。因此,服务器会估计将要返回的对象数量,并认为该请求占用的席位数量与该估计数量成比例。
API 优先级与公平性管理 watch 请求,但这涉及比基础行为更多的偏离。第一个偏离涉及 watch 请求占用其席位的时间长度。根据请求参数的不同,watch 请求的响应可能会以针对所有相关预先存在对象的 create 通知开始,也可能不会。API 优先级与公平性认为,一旦初始通知爆发(如果有)结束,watch 请求即完成了其席位占用。
每当服务器收到对象创建/更新/删除的通知时,正常的通知会以并发爆发的形式发送到所有相关的 watch 响应流。为了说明这项工作,API 优先级与公平性认为每个写请求在实际写入完成后还需要花费一些额外时间占用席位。服务器估计要发送的通知数量,并调整写请求的席位数量和席位占用时间以包含这项额外工作。
即使在同一个优先级级别内,也可能存在大量不同的流量来源。在超载情况下,防止一个请求流导致其他请求流匮乏是非常有价值的(特别是,在单个有缺陷的客户端使用请求轰炸 kube-apiserver 的常见情况下,理想情况下该有缺陷的客户端对其他客户端几乎没有可测量的影响)。这是通过使用公平排队算法来处理分配到相同优先级级别的请求来解决的。每个请求被分配到一个 流(flow),由匹配的 FlowSchema 的名称加上一个 流区分符(flow distinguisher) 标识——该区分符要么是请求用户,要么是目标资源的命名空间,或者为空——系统会尝试为相同优先级级别的不同流中的请求赋予大致相等的权重。为了实现对不同实例的区分处理,具有多个实例的控制器应当使用不同的用户名进行身份验证。
在将请求分类到流中后,API 优先级与公平性特性可能会将请求分配给一个队列。这种分配使用了一种称为洗牌分片(shuffle sharding)的技术,它能相对高效地利用队列,从而将低强度流与高强度流隔离开来。
排队算法的细节针对每个优先级级别都是可调的,允许管理员在内存使用、公平性(即当总流量超过容量时,独立流都能取得进展的特性)、对突发流量的容忍度以及由排队带来的额外延迟之间进行权衡。
有些请求被认为足够重要,以至于它们不受此特性施加的任何限制。这些豁免可以防止配置不当的流控配置完全禁用 API 服务器。
流控 API 涉及两种资源。PriorityLevelConfigurations 定义了可用的优先级级别、每个级别可处理的可用并发预算份额,并允许对排队行为进行微调。FlowSchemas 用于对个别入站请求进行分类,将每个请求匹配到一个单一的 PriorityLevelConfiguration。
PriorityLevelConfiguration 表示一个单一的优先级级别。每个 PriorityLevelConfiguration 对待处理请求的数量有独立的限制,并对排队请求的数量有限制。
PriorityLevelConfiguration 的名义并发限制不是以绝对席位数量指定的,而是以“名义并发份额(nominal concurrency shares)”指定的。API 服务器的总并发限制会根据这些份额按比例分配给现有的 PriorityLevelConfigurations,从而赋予每个级别以席位计的名义限制。这允许集群管理员通过使用不同的 --max-requests-inflight(或 --max-mutating-requests-inflight)值重启 kube-apiserver 来放大或缩小服务器的总流量,所有 PriorityLevelConfigurations 的最大允许并发量将按相同的比例增加(或减少)。
v1beta3 之前的版本中,相关的 PriorityLevelConfiguration 字段被命名为“确保并发份额(assured concurrency shares)”,而不是“名义并发份额”。此外,在 Kubernetes 1.25 及更早版本中,没有定期调整:名义/确保限制始终在没有调整的情况下应用。优先级级别可能借出和借入多少并发量的界限在 PriorityLevelConfiguration 中表示为该级别名义限制的百分比。这些百分比通过乘以名义限制 / 100.0 并四舍五入解析为绝对席位数量。优先级级别的动态调整并发限制被约束在 (a) 其名义限制减去其可借出席位的下界,以及 (b) 其名义限制加上其可借入席位的上界之间。在每次调整时,动态限制是通过每个优先级级别收回近期出现需求的任何已借出席位,然后在上述范围内共同公平地响应各优先级级别的近期席位需求而导出的。
--max-requests-inflight 和 --max-mutating-requests-inflight 之和。不再区分修改(mutating)和非修改(non-mutating)请求;如果您想为特定资源分别对待它们,请创建分别匹配修改和非修改动词的独立 FlowSchemas。当分配给单个 PriorityLevelConfiguration 的入站请求量超过其允许的并发级别时,其规范的 type 字段决定了多余请求的去向。类型为 Reject 意味着多余流量将立即被 HTTP 429(Too Many Requests)错误拒绝。类型为 Queue 意味着超过阈值的请求将被排队,并使用洗牌分片和公平排队技术来平衡请求流之间的进展。
排队配置允许对优先级级别的公平排队算法进行调优。有关算法的详细信息,可以在增强建议中阅读,但简而言之:
增加 queues 会降低不同流之间冲突的概率,但代价是增加内存使用。此处值为 1 有效地禁用了公平排队逻辑,但仍然允许请求被排队。
增加 queueLengthLimit 允许在不丢弃任何请求的情况下承受更大的流量突发,代价是增加延迟和内存使用。
更改 handSize 允许您调整不同流之间冲突的概率以及在超载情况下单个流可用的总体并发量。
handSize 会降低两个独立流发生冲突(从而使其中一个可能导致另一个匮乏)的可能性,但会增加少量流可能主导 apiserver 的可能性。较大的 handSize 也可能增加单个高流量流可能导致的延迟。单个流可能排队的最大请求数量为 handSize * queueLengthLimit。下表显示了一个有趣的洗牌分片配置集合,展示了对于一系列的大象(高强度流)数量,给定老鼠(低强度流)被大象挤压的概率。请参阅 https://play.golang.org/p/Gi0PLgVHiUg,该链接计算了此表格。
| HandSize | 队列 (Queues) | 1 头大象 | 4 头大象 | 16 头大象 |
|---|---|---|---|---|
| 12 | 32 | 4.428838398950118e-09 | 0.11431348830099144 | 0.9935089607656024 |
| 10 | 32 | 1.550093439632541e-08 | 0.0626479840223545 | 0.9753101519027554 |
| 10 | 64 | 6.601827268370426e-12 | 0.00045571320990370776 | 0.49999929150089345 |
| 9 | 64 | 3.6310049976037345e-11 | 0.00045501212304112273 | 0.4282314876454858 |
| 8 | 64 | 2.25929199850899e-10 | 0.0004886697053040446 | 0.35935114681123076 |
| 8 | 128 | 6.994461389026097e-13 | 3.4055790161620863e-06 | 0.02746173137155063 |
| 7 | 128 | 1.0579122850901972e-11 | 6.960839379258192e-06 | 0.02406157386340147 |
| 7 | 256 | 7.597695465552631e-14 | 6.728547142019406e-08 | 0.0006709661542533682 |
| 6 | 256 | 2.7134626662687968e-12 | 2.9516464018476436e-07 | 0.0008895654642000348 |
| 6 | 512 | 4.116062922897309e-14 | 4.982983350480894e-09 | 2.26025764343413e-05 |
| 6 | 1024 | 6.337324016514285e-16 | 8.09060164312957e-11 | 4.517408062903668e-07 |
FlowSchema 匹配某些入站请求并将它们分配给优先级级别。每个入站请求都会根据 FlowSchema 进行测试,从 matchingPrecedence 数字最小的开始,并向上处理。第一个匹配项获胜。
matchingPrecedence 最高的那个进行分配。如果多个 matchingPrecedence 相等的 FlowSchema 匹配同一个请求,名称在词典顺序上较小的那个将获胜,但最好不要依赖这一点,而是确保没有两个 FlowSchema 具有相同的 matchingPrecedence。如果至少有一个 rules 匹配,则 FlowSchema 匹配给定请求。如果至少有一个 subjects 并且 至少有一个 resourceRules 或 nonResourceRules(取决于入站请求是针对资源还是非资源 URL)匹配请求,则规则匹配。
对于 subjects 中的 name 字段,以及 resource rules 和 non-resource rules 中的 verbs、apiGroups、resources、namespaces 和 nonResourceURLs 字段,可以指定通配符 * 以匹配给定字段的所有值,从而有效地将其从考量中移除。
FlowSchema 的 distinguisherMethod.type 确定匹配该模式的请求将如何被分离到不同的流中。它可以是 ByUser(一个请求用户将无法匮乏其他用户的容量);ByNamespace(针对一个命名空间中资源的请求将无法匮乏针对其他命名空间中资源的容量);或者为空(或者可以完全省略 distinguisherMethod),在这种情况下,所有匹配该 FlowSchema 的请求将被视为单个流的一部分。对于给定 FlowSchema 的正确选择取决于资源和您的特定环境。
每个 kube-apiserver 维护两种 APF 配置对象:强制性的和建议的。
这四个强制性配置对象反映了固定的内置护栏行为。这是服务器在这些对象存在之前就有的行为,当这些对象存在时,它们的规范反映了这种行为。这四个强制性对象如下。
强制性的 exempt 优先级级别用于那些完全不受流控限制的请求:它们将始终被立即分发。强制性的 exempt FlowSchema 将来自 system:masters 组的所有请求归类到此优先级级别。如果合适,您可以定义其他 FlowSchema,将其他请求引导至此优先级级别。
强制性的 catch-all 优先级级别与强制性的 catch-all FlowSchema 结合使用,以确保每个请求都能得到某种形式的分类。通常您不应依赖此 catch-all 配置,而应创建自己的 catch-all FlowSchema 和 PriorityLevelConfiguration(或者使用默认安装的建议 global-default 优先级级别)。由于它不被预期正常使用,强制性的 catch-all 优先级级别具有非常小的并发份额且不对请求进行排队。
建议的 FlowSchemas 和 PriorityLevelConfigurations 构成了合理的默认配置。如果您愿意,可以修改这些配置和/或创建额外的配置对象。如果您的集群可能经历重负载,那么您应该考虑什么样的配置最有效。
建议的配置将请求分为六个优先级级别:
node-high 优先级级别用于来自节点的健康更新。
system 优先级级别用于来自 system:nodes 组(即 Kubelet)的非健康请求,为了让工作负载能够调度在这些节点上,它们必须能够联系 API 服务器。
leader-election 优先级级别用于来自内置控制器的领导选举请求(特别是来自 kube-system 命名空间中的 system:kube-controller-manager 或 system:kube-scheduler 用户和服务账户的针对 endpoints、configmaps 或 leases 的请求)。这些请求需要与其他流量隔离开来,因为领导选举失败会导致控制器失败和重启,进而导致昂贵的流量,因为新控制器需要同步其 informer。
workload-high 优先级级别用于来自内置控制器的其他请求。
workload-low 优先级级别用于来自任何其他服务账户的请求,通常包括来自在 Pod 中运行的控制器的所有请求。
global-default 优先级级别处理所有其他流量,例如由非特权用户运行的交互式 kubectl 命令。
建议的 FlowSchemas 用于将请求引导至上述优先级级别,此处不再一一枚举。
每个 kube-apiserver 独立维护强制性和建议的配置对象,使用初始和定期行为。因此,在版本混合的服务器情况下,只要不同服务器对这些对象正确内容的理解不同,就可能会出现抖动(thrashing)。
每个 kube-apiserver 对强制性和建议的配置对象进行初始维护传递,之后对这些对象进行定期维护(每分钟一次)。
对于强制性配置对象,维护包括确保对象存在,如果存在,则具有正确的规范。服务器拒绝允许创建或更新不符合服务器护栏行为的规范。
建议配置对象的维护旨在允许覆盖其规范。另一方面,删除操作是不被尊重的:维护将恢复该对象。如果您不想要某个建议配置对象,则需要保留它但将其规范设置为具有最小后果。建议对象的维护也旨在支持在发布新版本的 kube-apiserver 时进行自动迁移,尽管在服务器混合期间可能会有抖动。
建议配置对象的维护包括:如果对象不存在,则创建它——使用服务器的建议规范。反之,如果对象已经存在,维护行为取决于是由 kube-apiservers 还是用户控制该对象。在前一种情况下,服务器确保对象的规范是服务器建议的;在后一种情况下,规范保持不变。
关于谁控制该对象的问题,首先查找带有键 apf.kubernetes.io/autoupdate-spec 的注解。如果存在此类注解且其值为 true,则 kube-apiserver 控制该对象。如果存在此类注解且其值为 false,则用户控制该对象。如果这两个条件都不满足,则查看对象的 metadata.generation。如果为 1,则 kube-apiserver 控制该对象。否则,用户控制该对象。这些规则是在 1.22 版本中引入的,其对 metadata.generation 的考虑是为了从早期更简单的行为进行迁移。希望控制建议配置对象的用户应将其 apf.kubernetes.io/autoupdate-spec 注解设置为 false。
强制性或建议配置对象的维护还包括确保它具有准确反映 kube-apiserver 是否控制该对象的 apf.kubernetes.io/autoupdate-spec 注解。
维护还包括删除那些既不是强制性也不是建议性但被标注为 apf.kubernetes.io/autoupdate-spec=true 的对象。
建议的配置没有对来自本地 Kubelet 的 kube-apiserver 健康检查请求给予特殊处理——这些请求往往使用安全端口但不提供凭据。使用建议配置时,这些请求会被分配到 global-default FlowSchema 和对应的 global-default 优先级级别,在此处其他流量可能会挤占它们。
如果您添加以下额外的 FlowSchema,这会将这些请求豁免于速率限制。
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: health-for-strangers
spec:
matchingPrecedence: 1000
priorityLevelConfiguration:
name: exempt
rules:
- nonResourceRules:
- nonResourceURLs:
- "/healthz"
- "/livez"
- "/readyz"
verbs:
- "*"
subjects:
- kind: Group
group:
name: "system:unauthenticated"
flow_schema 和 priority_level 的命名不一致,分别为 flowSchema 和 priorityLevel。如果您运行的是 v1.19 及更早版本的 Kubernetes,请参考您版本的文档。当您启用 API 优先级与公平性特性时,kube-apiserver 会导出额外的指标。监视这些指标可以帮助您确定您的配置是否不适当地限制了重要流量,或者发现可能损害系统健康的表现不佳的工作负载。
apiserver_flowcontrol_rejected_requests_total 是一个被拒绝请求的计数器向量(自服务器启动以来累计),按标签 flow_schema(指示与请求匹配的那个)、priority_level(指示请求被分配到的那个)和 reason 进行细分。reason 标签将是以下值之一:
queue-full,表示已排队太多请求。concurrency-limit,表示 PriorityLevelConfiguration 被配置为拒绝而不是排队多余请求。time-out,表示当排队时间限制到期时,请求仍在队列中。cancelled,表示该请求没有清除锁(purge locked),并已从队列中弹出。apiserver_flowcontrol_dispatched_requests_total 是已开始执行的请求的计数器向量(自服务器启动以来累计),按 flow_schema 和 priority_level 进行细分。
apiserver_flowcontrol_current_inqueue_requests 是一个保持瞬时排队(未执行)请求数量的仪表向量(gauge vector),按 priority_level 和 flow_schema 进行细分。
apiserver_flowcontrol_current_executing_requests 是一个保持瞬时执行(未在队列中等待)请求数量的仪表向量,按 priority_level 和 flow_schema 进行细分。
apiserver_flowcontrol_current_executing_seats 是一个保持瞬时占用席位数量的仪表向量,按 priority_level 和 flow_schema 进行细分。
apiserver_flowcontrol_request_wait_duration_seconds 是一个请求排队时间的直方图向量,按 flow_schema、priority_level 和 execute 标签进行细分。execute 标签指示请求是否已开始执行。
apiserver_flowcontrol_nominal_limit_seats 是一个保持每个优先级级别名义并发限制的仪表向量,该限制根据 API 服务器的总并发限制和优先级级别配置的名义并发份额计算得出。
apiserver_current_inqueue_requests 是一个排队请求数量近期高水位标记的仪表向量,按名为 request_kind 的标签分组,其值为 mutating 或 readOnly。这些高水位标记描述了在最近完成的一秒窗口中观察到的最大数量。它们补充了旧的 apiserver_current_inflight_requests 仪表向量,该向量保持了上一窗口活跃服务请求数量的高水位标记。
apiserver_current_inqueue_seats 是排队请求中各自将占用的最大席位数量的总和的仪表向量,按名为 flow_schema 和 priority_level 的标签分组。
apiserver_flowcontrol_read_vs_write_current_requests 是每纳秒结束时观察到的请求数量的直方图向量,按标签 phase(取值为 waiting 和 executing)和 request_kind(取值为 mutating 和 readOnly)进行细分。每个观察值是一个介于 0 和 1 之间的比率,即请求数量除以相应的请求数量限制(等待的队列容量限制和执行的并发限制)。
apiserver_flowcontrol_request_concurrency_in_use 是一个保持瞬时占用席位数量的仪表向量,按 priority_level 和 flow_schema 进行细分。
apiserver_flowcontrol_priority_level_request_utilization 是每纳秒结束时观察到的请求数量的直方图向量,按标签 phase(取值为 waiting 和 executing)和 priority_level 进行细分。每个观察值是一个介于 0 和 1 之间的比率,即请求数量除以相应的请求数量限制(等待的队列容量限制和执行的并发限制)。
apiserver_flowcontrol_priority_level_seat_utilization 是每纳秒结束时观察到的优先级级别并发限制利用率的直方图向量,按 priority_level 进行细分。此利用率是比率 (占用席位数) / (并发限制)。此指标考虑了所有请求(除 WATCH 外)执行的所有阶段(正常阶段和写入末尾涵盖相应通知工作的额外延迟);对于 WATCH,它仅考虑传递预先存在对象通知的初始阶段。向量中的每个直方图还标有 phase: executing(等待阶段没有席位限制)。
apiserver_flowcontrol_request_queue_length_after_enqueue 是队列长度的直方图向量,按 priority_level 和 flow_schema 进行细分,由入队的请求采样。每个被排队的请求都会向其直方图贡献一个样本,报告请求添加后队列的长度。请注意,这产生的结果与无偏调查的统计数据不同。
apiserver_flowcontrol_request_concurrency_limit 与 apiserver_flowcontrol_nominal_limit_seats 相同。在引入优先级级别之间的并发借入之前,此值始终等于 apiserver_flowcontrol_current_limit_seats(该值作为一个独立的指标不存在)。
apiserver_flowcontrol_lower_limit_seats 是一个保持每个优先级级别动态并发限制下界的仪表向量。
apiserver_flowcontrol_upper_limit_seats 是一个保持每个优先级级别动态并发限制上界的仪表向量。
apiserver_flowcontrol_demand_seats 是一个直方图向量,在每纳秒结束时统计每个优先级级别的 (席位需求) / (名义并发限制) 比率的观察值。优先级级别的席位需求是排队请求和处于初始执行阶段请求的(请求初始和最终执行阶段所占席位数的最大值)的总和。
apiserver_flowcontrol_demand_seats_high_watermark 是一个仪表向量,保持每个优先级级别在上次并发借入调整期间观察到的最大席位需求。
apiserver_flowcontrol_demand_seats_average 是一个仪表向量,保持每个优先级级别在上次并发借入调整期间观察到的时间加权平均席位需求。
apiserver_flowcontrol_demand_seats_stdev 是一个仪表向量,保持每个优先级级别在上次并发借入调整期间观察到的时间加权总体标准差席位需求。
apiserver_flowcontrol_demand_seats_smoothed 是一个仪表向量,保持在上次并发调整时确定的每个优先级级别的平滑包络席位需求。
apiserver_flowcontrol_target_seats 是一个仪表向量,保持每个优先级级别进入借入分配问题时的并发目标。
apiserver_flowcontrol_seat_fair_frac 是一个保持上次借入调整中确定的公平分配分数的仪表。
apiserver_flowcontrol_current_limit_seats 是一个仪表向量,保持在上次调整中得出的每个优先级级别的动态并发限制。
apiserver_flowcontrol_request_execution_seconds 是请求实际执行时间的直方图向量,按 flow_schema 和 priority_level 进行细分。
apiserver_flowcontrol_watch_count_samples 是给定写操作相关的活跃 WATCH 请求数量的直方图向量,按 flow_schema 和 priority_level 进行细分。
apiserver_flowcontrol_work_estimated_seats 是与请求关联的估计席位数量(执行初始阶段和最终阶段的最大值)的直方图向量,按 flow_schema 和 priority_level 进行细分。
apiserver_flowcontrol_request_dispatch_no_accommodation_total 是由于缺乏可用并发性而导致本应分发但未分发的事件数量的计数器向量,按 flow_schema 和 priority_level 进行细分。
apiserver_flowcontrol_epoch_advance_total 是为避免数值溢出而尝试将优先级级别进度计数器向后跳转次数的计数器向量,按 priority_level 和 success 进行分组。
当某个优先级级别超过其允许的并发量时,请求可能会经历增加的延迟或被 HTTP 429(Too Many Requests)错误丢弃。为了防止 APF 的这些副作用,您可以修改您的工作负载或调整您的 APF 设置,以确保有足够的席位来处理您的请求。
要检测请求是否因 APF 被拒绝,请检查以下指标:
为了防止请求因 APF 排队增加延迟或被丢弃,您可以通过以下方式优化您的请求:
请记住,来自 APF 的排队或拒绝请求可能是由请求数量增加或现有请求延迟增加引起的。例如,如果通常需要 1 秒执行的请求开始需要 60 秒,则 APF 可能会开始拒绝请求,因为由于延迟增加,请求比平时占用席位的时间更长。如果 APF 在没有显著工作负载变化的情况下开始拒绝跨多个优先级级别的请求,则可能是控制平面性能出现了根本性问题,而不是工作负载或 APF 设置的问题。
您还可以修改默认的 FlowSchema 和 PriorityLevelConfiguration 对象,或创建这些类型的新对象,以更好地适应您的工作负载。
APF 设置可以修改为:
max-requests-inflight 和 max-mutating-requests-inflight 标志的值来增加特定 kube-apiserver 在所有优先级级别上的可用席位数量。或者,水平扩展 kube-apiserver 实例的数量将增加整个集群中每个优先级级别的总并发量,前提是有足够的请求负载均衡。apf.kubernetes.io/autoupdate-spec 注解设置为 false。对于请求隔离,您可以创建一个其 subject 匹配发出这些请求的用户的 FlowSchema,或者创建一个匹配请求内容(对应于 resourceRules)的 FlowSchema。接下来,您可以将此 FlowSchema 映射到具有较小席位份额的 PriorityLevelConfiguration。
例如,假设在默认命名空间中运行的 Pod 发出的 List 事件请求每个使用 10 个席位,并执行 1 分钟。为了防止这些昂贵的请求影响使用现有 service-accounts FlowSchema 的其他 Pod 的请求,您可以应用以下 FlowSchema 来将这些 List 调用与其他请求隔离开来。
用于隔离 List 事件请求的示例 FlowSchema 对象
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: list-events-default-service-account
spec:
distinguisherMethod:
type: ByUser
matchingPrecedence: 8000
priorityLevelConfiguration:
name: catch-all
rules:
- resourceRules:
- apiGroups:
- '*'
namespaces:
- default
resources:
- events
verbs:
- list
subjects:
- kind: ServiceAccount
serviceAccount:
name: default
namespace: default