API 优先级和公平性
Kubernetes v1.29 [稳定]
控制 Kubernetes API 服务器在过载情况下的行为是集群管理员的一项关键任务。 kube-apiserver 提供了一些可用的控制机制(例如 --max-requests-inflight
和 --max-mutating-requests-inflight
命令行标志)来限制可接受的未完成工作量,防止大量入站请求过载并可能导致 API 服务器崩溃,但这些标志不足以确保在高流量期间最重要的请求能够通过。
API 优先级和公平性功能 (APF) 是一种替代方案,它改进了上述最大飞行限制。APF 以更细粒度的方式对请求进行分类和隔离。它还引入了有限的排队机制,以便在非常短暂的突发情况下不会拒绝任何请求。使用公平排队技术从队列中调度请求,例如,行为不端的 控制器 不必饿死其他控制器(即使在相同的优先级级别)。
此功能旨在与标准控制器良好配合使用,这些控制器使用 informer 并通过指数退避对 API 请求失败做出反应,以及以这种方式工作的其他客户端。
注意
某些分类为“长时间运行”的请求(例如远程命令执行或日志跟踪)不受 API 优先级和公平性过滤器的约束。在未启用 API 优先级和公平性功能的情况下,--max-requests-inflight
标志也是如此。API 优先级和公平性*确实*适用于**watch**请求。当禁用 API 优先级和公平性时,**watch**请求不受 --max-requests-inflight
限制的约束。启用/禁用 API 优先级和公平性
API 优先级和公平性功能由命令行标志控制,默认情况下处于启用状态。有关可用 kube-apiserver 命令行选项以及如何启用和禁用它们的常规说明,请参阅选项。APF 的命令行选项名称为“--enable-priority-and-fairness”。此功能还涉及一个 API 组,其中:(a) 一个稳定的 v1
版本,在 1.29 中引入,默认启用 (b) 一个 v1beta3
版本,默认启用,在 v1.29 中弃用。您可以通过将以下命令行标志添加到 kube-apiserver
调用中来禁用 API 组测试版 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-apisever
时最简单的方法)是在代码中构建禁用优先级和公平性的服务器 B。
概念
API 优先级和公平性功能涉及几个不同的功能。使用*FlowSchemas*根据请求的属性对传入请求进行分类,并将其分配给优先级级别。优先级级别通过维护单独的并发限制来增加一定程度的隔离,以便分配给不同优先级级别的请求不会相互饿死。在优先级级别内,公平排队算法可防止来自不同*流*的请求相互饿死,并允许对请求进行排队,以防止突发流量在平均负载可接受地低时导致请求失败。
优先级级别
如果未启用 APF,则 API 服务器中的总体并发性受 kube-apiserver
标志 --max-requests-inflight
和 --max-mutating-requests-inflight
的限制。启用 APF 后,由这些标志定义的并发限制将被求和,然后将总和在可配置的*优先级级别*集中进行划分。每个传入请求都分配给一个优先级级别,并且每个优先级级别只会调度与其特定限制允许的并发请求一样多的请求。
例如,默认配置包括针对领导者选举请求、来自内置控制器的请求和来自 Pod 的请求的单独优先级级别。这意味着,一个行为不端的 Pod 向 API 服务器发送大量请求,不会妨碍领导者选举或内置控制器的操作成功。
优先级级别的并发限制会定期调整,允许未充分利用的优先级级别临时将并发性借给高度利用的级别。这些限制基于名义限制以及优先级级别可以借出和借入多少并发性的界限,所有这些都来自下面提到的配置对象。
请求占用的席位
上述并发管理描述是基线情况。请求的持续时间不同,但在与优先级级别的并发限制进行比较时,在任何给定时刻都被视为相等。在基线情况下,每个请求占用一个并发单元。受火车或飞机上的每位乘客占用固定座位供应量的方式的启发,“座位”一词用于表示一个并发单元。
但有些请求占用的座位不止一个。其中一些是服务器估计将返回大量对象的**list**请求。已经发现这些请求给服务器带来了极大的负担。因此,服务器会估计将返回的对象数量,并认为请求占用的座位数量与该估计数量成正比。
watch 请求的执行时间调整
API 优先级和公平性管理**watch**请求,但这涉及到对基线行为的更多调整。第一个问题是**watch**请求被认为占用其座位多长时间。根据请求参数,对**watch**请求的响应可能会也可能不会以所有相关预先存在的对象的**create**通知开始。API 优先级和公平性认为,一旦初始通知突发(如果有)结束,**watch**请求就会完成其座位。
当服务器收到对象创建/更新/删除的通知时,正常的通知会以并发突发的形式发送到所有相关的**watch**响应流。为了考虑这项工作,API 优先级和公平性认为每个写请求在实际写入完成后都会花费一些额外的时间来占用座位。服务器会估计要发送的通知数量,并调整写请求的座位数量和座位占用时间,以包含这项额外的工作。
排队
即使在同一个优先级内,也可能存在大量不同的流量来源。在过载情况下,防止一个请求流饿死其他请求流非常重要(特别是,在单个错误客户端使用请求淹没 kube-apiserver 的相对常见情况下,理想情况下,该错误客户端不应对其他客户端产生任何可衡量的影响)。这是通过使用公平队列算法来处理分配了相同优先级的请求来处理的。每个请求都被分配到一个_流_,由匹配 FlowSchema 的名称加上一个_流区分符_(请求用户、目标资源的命名空间或无)标识,系统会尝试为相同优先级的不同流中的请求赋予大致相等的权重。为了能够对不同的实例进行不同的处理,具有多个实例的控制器应该使用不同的用户名进行身份验证。
在将请求分类到流中之后,API 优先级和公平性功能可能会将请求分配到队列中。此分配使用一种称为 随机分片 的技术,该技术可以相对有效地利用队列来隔离低强度流和高强度流。
队列算法的细节对于每个优先级都是可调的,并且允许管理员在内存使用、公平性(当总流量超过容量时,独立流都将取得进展的属性)、对突发流量的容忍度以及队列引起的额外延迟之间进行权衡。
免检请求
某些请求被认为足够重要,以至于不受此功能施加的任何限制。这些豁免可防止配置错误的流量控制配置完全禁用 API 服务器。
资源
流量控制 API 涉及两种资源。PriorityLevelConfigurations 定义了可用的优先级、每个优先级可以处理的可用并发预算份额,并允许微调队列行为。FlowSchemas 用于对单个入站请求进行分类,将每个请求匹配到单个 PriorityLevelConfiguration。
PriorityLevelConfiguration
PriorityLevelConfiguration 表示单个优先级。每个 PriorityLevelConfiguration 对未完成请求的数量以及排队请求的数量都有独立的限制。
PriorityLevelConfiguration 的标称并发限制不是以绝对的席位数指定的,而是以“标称并发份额”指定的。API 服务器的总并发限制按比例分配给现有的 PriorityLevelConfigurations,以席位形式给出每个级别的标称限制。这允许集群管理员通过使用不同的 --max-requests-inflight
(或 --max-mutating-requests-inflight
)值重新启动 kube-apiserver
来扩展或缩减服务器的总流量,并且所有 PriorityLevelConfigurations 将看到它们的最大允许并发按相同比例增加(或减少)。
注意
在v1beta3
之前的版本中,相关的 PriorityLevelConfiguration 字段名为“assured concurrency shares”而不是“nominal concurrency shares”。此外,在 Kubernetes 1.25 及更早版本中,没有定期调整:标称/保证限制始终在没有调整的情况下应用。优先级可以借出多少并发以及可以借用多少并发的界限在 PriorityLevelConfiguration 中表示为该级别标称限制的百分比。这些值通过乘以标称限制 / 100.0 并四舍五入来解析为绝对席位数。优先级的动态调整并发限制被限制在其标称限制减去其可借出席位的下限 (a) 和其标称限制加上其可以借用的席位的上限 (b) 之间。在每次调整时,动态限制是通过每个优先级收回最近出现需求的任何借出席位,然后在上述范围内共同公平地响应优先级上的最近席位需求来得出的。
注意
启用优先级和公平性功能后,服务器的总并发限制设置为--max-requests-inflight
和 --max-mutating-requests-inflight
的总和。不再区分突变请求和非突变请求;如果您想为给定资源分别处理它们,请分别创建匹配突变和非突变动词的 FlowSchemas。当分配给单个 PriorityLevelConfiguration 的入站请求量超过其允许的并发级别时,其规范的 type
字段决定了对额外请求的处理方式。Reject
类型意味着超出流量将立即被拒绝,并显示 HTTP 429(请求过多)错误。Queue
类型意味着超出阈值的请求将排队,并使用随机分片和公平排队技术来平衡请求流之间的进度。
队列配置允许为优先级调整公平排队算法。算法的详细信息可以在增强建议中阅读,但简而言之
增加
queues
可以降低不同流之间发生冲突的几率,但代价是增加内存使用量。此处的值为 1 实际上禁用了公平排队逻辑,但仍然允许请求排队。增加
queueLengthLimit
允许在不丢弃任何请求的情况下维持更大的流量突发,但代价是增加延迟和内存使用量。更改
handSize
允许您调整不同流之间发生冲突的概率以及在过载情况下单个流可用的总体并发量。注意
较大的handSize
会降低两个单独的流发生冲突的可能性(因此也降低了一个流饿死另一个流的可能性),但更有可能使少数流主导 apiserver。较大的handSize
还可能增加单个高流量流可能导致的延迟量。单个流可能的最大排队请求数为handSize * queueLengthLimit
。
下表显示了一些有趣的随机分片配置集合,显示了对于每个配置,对于说明性的大象数量集合,给定老鼠(低强度流)被大象(高强度流)压扁的概率。请参阅 https://play.golang.org/p/Gi0PLgVHiUg,它计算了此表。
HandSize | 队列 | 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 匹配一些入站请求并将它们分配到优先级。每个入站请求都会根据 FlowSchemas 进行测试,从 matchingPrecedence
数值最低的开始,然后向上进行。第一个匹配的获胜。
注意
对于给定请求,只有第一个匹配的 FlowSchema 才重要。如果多个 FlowSchemas 匹配单个入站请求,则将根据matchingPrecedence
最高的那个进行分配。如果具有相同 matchingPrecedence
的多个 FlowSchemas 匹配同一个请求,则名称字典顺序较小的那个将获胜,但最好不要依赖于此,而是要确保没有两个 FlowSchemas 具有相同的 matchingPrecedence
。如果 FlowSchema 的至少一个 rules
匹配,则该 FlowSchema 匹配给定请求。如果其至少一个 subjects
_和_至少一个 resourceRules
或 nonResourceRules
(取决于传入请求是针对资源 URL 还是非资源 URL)匹配请求,则规则匹配。
对于主题中的 name
字段,以及资源和非资源规则的 verbs
、apiGroups
、resources
、namespaces
和 nonResourceURLs
字段,可以指定通配符 *
来匹配给定字段的所有值,从而有效地将其从考虑中移除。
FlowSchema 的 distinguisherMethod.type
确定如何将匹配该模式的请求分离到流中。它可以是 ByUser
,其中一个请求用户将无法饿死其他用户的容量;ByNamespace
,其中对一个命名空间中的资源的请求将无法饿死对其他命名空间中的资源的请求的容量;或者为空(或者可以完全省略 distinguisherMethod
),其中与此 FlowSchema 匹配的所有请求都将被视为单个流的一部分。给定 FlowSchema 的正确选择取决于资源和您的特定环境。
默认值
每个 kube-apiserver 都维护两种 APF 配置对象:强制性的和建议的。
强制配置对象
四个强制配置对象反映了固定的内置护栏行为。这是服务器在这些对象存在之前的行为,当这些对象存在时,它们的规范反映了这种行为。四个强制对象如下。
强制
exempt
优先级用于根本不受流量控制的请求:它们将始终立即被调度。强制exempt
FlowSchema 将来自system:masters
组的所有请求分类到此优先级。如果适用,您可以定义其他 FlowSchemas 将其他请求定向到此优先级。强制
catch-all
优先级与强制catch-all
FlowSchema 结合使用,以确保每个请求都获得某种分类。通常,您不应该依赖此 catch-all 配置,而应该根据需要创建自己的 catch-all FlowSchema 和 PriorityLevelConfiguration(或使用默认安装的建议global-default
优先级)。因为它预计不会正常使用,所以强制catch-all
优先级的并发份额非常小,并且不会对请求进行排队。
建议的配置对象
建议的 FlowSchemas 和 PriorityLevelConfigurations 构成了合理的默认配置。如果需要,您可以修改这些对象和/或创建其他配置对象。如果您的集群可能会遇到沉重的负载,那么您应该考虑哪种配置最有效。
建议的配置将请求分组到六个优先级
node-high
优先级用于来自节点的健康更新。system
优先级用于来自system:nodes
组(即 Kubelets)的非健康请求,这些请求必须能够联系 API 服务器才能在它们上调度工作负载。leader-election
优先级级别适用于来自内置控制器的领导者选举请求(特别是来自system:kube-controller-manager
或system:kube-scheduler
用户以及kube-system
命名空间中的服务帐户的endpoints
、configmaps
或leases
请求)。将这些请求与其他流量隔离非常重要,因为领导者选举失败会导致其控制器失败并重新启动,这反过来会导致更昂贵的流量,因为新的控制器会同步其通知器。workload-high
优先级级别适用于来自内置控制器的其他请求。workload-low
优先级级别适用于来自任何其他服务帐户的请求,通常包括来自 Pod 中运行的控制器的所有请求。global-default
优先级级别处理所有其他流量,例如由非特权用户运行的交互式kubectl
命令。
建议的 FlowSchema 用于将请求引导到上述优先级级别,此处未枚举。
强制和建议配置对象的维护
每个 kube-apiserver
都使用初始和定期行为独立维护强制和建议的配置对象。因此,在服务器版本混合的情况下,只要不同的服务器对这些对象的内容有不同的看法,就可能会出现抖动。
每个 kube-apiserver
都会对强制和建议的配置对象进行初始维护,之后会定期(每分钟一次)对这些对象进行维护。
对于强制配置对象,维护包括确保对象存在,如果存在,则具有正确的规范。服务器拒绝创建或更新与服务器的防护行为不一致的规范。
建议配置对象的维护旨在允许覆盖其规范。另一方面,删除操作不受尊重:维护将恢复对象。如果您不希望使用建议的配置对象,则需要保留它,但将其规范设置为影响最小。建议对象的维护还旨在支持在推出新版本的 kube-apiserver
时自动迁移,尽管在服务器群体混合的情况下可能会出现抖动。
建议配置对象的维护包括在对象不存在时使用服务器建议的规范创建它。另一方面,如果对象已经存在,则维护行为取决于 kube-apiservers
还是用户控制该对象。在前一种情况下,服务器确保对象的规范是服务器建议的规范;在后一种情况下,规范保持不变。
谁控制对象的问题首先通过查找键为 apf.kubernetes.io/autoupdate-spec
的注释来回答。如果存在这样的注释并且其值为 true
,则 kube-apiservers 控制该对象。如果存在这样的注释并且其值为 false
,则用户控制该对象。如果这两种情况都不成立,则会参考对象的 metadata.generation
。如果为 1,则 kube-apiservers 控制该对象。否则用户控制该对象。这些规则是在 1.22 版本中引入的,它们对 metadata.generation
的考虑是为了从早期更简单的行为中迁移。希望控制建议配置对象的用户应将其 apf.kubernetes.io/autoupdate-spec
注释设置为 false
。
强制或建议配置对象的维护还包括确保它具有一个 apf.kubernetes.io/autoupdate-spec
注释,该注释准确反映了 kube-apiservers 是否控制该对象。
维护还包括删除既不是强制性的也不是建议的,但注释为 apf.kubernetes.io/autoupdate-spec=true
的对象。
健康检查并发豁免
建议的配置没有对来自本地 kubelet 的 kube-apiserver 上的健康检查请求进行特殊处理,这些请求倾向于使用安全端口但不提供凭据。使用建议的配置,这些请求将被分配给 global-default
FlowSchema 和相应的 global-default
优先级级别,其他流量可能会挤占它们。
如果您添加以下额外的 FlowSchema,这将使这些请求免于速率限制。
注意
进行此更改还允许任何恶意方以他们喜欢的任何数量发送与此 FlowSchema 匹配的健康检查请求。如果您有 Web 流量过滤器或类似的外部安全机制来保护您的集群的 API 服务器免受一般互联网流量的攻击,则可以配置规则以阻止来自集群外部的任何健康检查请求。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"
可观察性
指标
注意
在 v1.20 之前的 Kubernetes 版本中,标签flow_schema
和 priority_level
分别被不一致地命名为 flowSchema
和 priorityLevel
。如果您运行的是 Kubernetes v1.19 及更早版本,则应参考您版本的文档。启用 API 优先级和公平性功能后,kube-apiserver 会导出其他指标。监控这些指标可以帮助您确定您的配置是否不适当地限制了重要流量,或者找到可能损害系统健康的异常工作负载。
成熟度级别:测试版
apiserver_flowcontrol_rejected_requests_total
是一个计数器向量(自服务器启动以来累计),用于统计被拒绝的请求,并按标签flow_schema
(指示与请求匹配的 FlowSchema)、priority_level
(指示请求被分配到的优先级级别)和reason
进行细分。reason
标签将是以下值之一queue-full
,表示队列中已经存在太多请求。concurrency-limit
,表示 PriorityLevelConfiguration 配置为拒绝而不是排队超出限制的请求。time-out
,表示请求在其排队时间限制到期时仍在队列中。cancelled
,表示请求未被清除锁定并已从队列中弹出。
apiserver_flowcontrol_dispatched_requests_total
是一个计数器向量(自服务器启动以来累计),用于统计已开始执行的请求,并按flow_schema
和priority_level
进行细分。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
进行细分。apiserver_flowcontrol_request_wait_duration_seconds
是一个直方图向量,用于统计请求在队列中花费的时间,并按标签flow_schema
、priority_level
和execute
进行细分。execute
标签指示请求是否已开始执行。注意
由于每个 FlowSchema 始终将请求分配给单个 PriorityLevelConfiguration,因此您可以将一个优先级级别的所有 FlowSchema 的直方图相加,以获得分配给该优先级级别的请求的有效直方图。apiserver_flowcontrol_nominal_limit_seats
是一个仪表向量,用于保存每个优先级级别的标称并发限制,该限制根据 API 服务器的总并发限制和优先级级别的配置标称并发份额计算得出。
成熟度级别:Alpha
apiserver_current_inqueue_requests
是一个仪表向量,用于统计最近排队请求数量的高水位线,并按名为request_kind
的标签进行分组,该标签的值为mutating
或readOnly
。这些高水位线描述了最近完成的 1 秒窗口中看到的最大数量。这些指标是对旧的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
进行细分,由入队的请求进行采样。每个排队的请求都会为其直方图贡献一个样本,报告请求添加后队列的长度。请注意,这会产生与无偏调查不同的统计数据。注意
此处直方图中的异常值意味着单个流(即,由一个用户或针对一个命名空间的请求,具体取决于配置)很可能正在淹没 API 服务器,并且正在受到限制。相比之下,如果一个优先级级别的直方图显示该优先级级别的所有队列都比其他优先级级别的队列长,则可能需要增加该 PriorityLevelConfiguration 的并发份额。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
分组。
使用 API 优先级和公平性的良好实践
当给定的优先级级别超过其允许的并发性时,请求可能会遇到延迟增加或被丢弃,并出现 HTTP 429(请求过多)错误。为了防止 APF 的这些副作用,您可以修改工作负载或调整 APF 设置,以确保有足够的席位来满足您的请求。
要检测请求是否因 APF 而被拒绝,请检查以下指标
- apiserver_flowcontrol_rejected_requests_total:每个 FlowSchema 和 PriorityLevelConfiguration 被拒绝的请求总数。
- apiserver_flowcontrol_current_inqueue_requests:每个 FlowSchema 和 PriorityLevelConfiguration 当前排队的请求数量。
- apiserver_flowcontrol_request_wait_duration_seconds:在队列中等待的请求增加的延迟。
- apiserver_flowcontrol_priority_level_seat_utilization:每个 PriorityLevelConfiguration 的席位利用率。
工作负载修改
为了防止请求因 APF 而排队、增加延迟或被丢弃,您可以通过以下方式优化请求
- 降低请求的执行速率。在固定时间段内减少请求数量将导致在给定时间需要更少的席位。
- 避免同时发出大量昂贵的请求。可以优化请求以使用更少的席位或降低延迟,以便这些请求在更短的时间内占用这些席位。列表请求可能会占用多个席位,具体取决于请求期间获取的对象数量。限制列表请求中检索的对象数量(例如,使用分页)将在更短的时间内使用更少的总席位。此外,将列表请求替换为监视请求将需要更低的总并发份额,因为监视请求在其初始通知爆发期间仅占用 1 个席位。如果在 1.27 及更高版本中使用流式列表,则监视请求将占用与列表请求相同数量的席位,用于其初始通知爆发,因为必须流式传输集合的整个状态。请注意,在这两种情况下,监视请求在此初始阶段之后都不会占用任何席位。
请记住,APF 导致的请求排队或拒绝可能是由请求数量增加或现有请求的延迟增加引起的。例如,如果通常需要 1 秒才能执行的请求开始需要 60 秒,则 APF 可能会开始拒绝请求,因为由于延迟增加,请求占用席位的时间比正常情况更长。如果 APF 在工作负载没有显着变化的情况下开始拒绝来自多个优先级级别的请求,则可能是控制平面性能存在潜在问题,而不是工作负载或 APF 设置问题。
优先级和公平性设置
您还可以修改默认的 FlowSchema 和 PriorityLevelConfiguration 对象,或创建这些类型的新对象,以更好地适应您的工作负载。
可以修改 APF 设置以
- 为高优先级请求提供更多席位。
- 隔离非必要或昂贵的请求,如果这些请求与其他流共享并发级别,则会导致并发级别不足。
为高优先级请求提供更多席位
- 如果可能,可以通过增加
max-requests-inflight
和max-mutating-requests-inflight
标志的值来增加特定kube-apiserver
的所有优先级级别可用的席位总数。或者,假设请求的负载均衡足够,则水平扩展kube-apiserver
实例的数量将增加整个集群中每个优先级级别的总并发性。 - 您可以创建一个新的 FlowSchema,它引用具有更大并发级别的 PriorityLevelConfiguration。这个新的 PriorityLevelConfiguration 可以是现有级别,也可以是具有自己的一组名义并发份额的新级别。例如,可以引入一个新的 FlowSchema,将请求的 PriorityLevelConfiguration 从 global-default 更改为 workload-low,以增加用户可用的席位数量。创建新的 PriorityLevelConfiguration 将减少指定给现有级别的席位数量。请记住,编辑默认的 FlowSchema 或 PriorityLevelConfiguration 需要将
apf.kubernetes.io/autoupdate-spec
注释设置为 false。 - 您还可以增加为高优先级请求提供服务的 PriorityLevelConfiguration 的 NominalConcurrencyShares。或者,对于 1.26 及更高版本,您可以增加竞争优先级级别的 LendablePercent,以便给定的优先级级别拥有更大的可借用席位池。
隔离非必要请求,防止其耗尽其他流的资源
对于请求隔离,您可以创建一个 FlowSchema,其主题与发出这些请求的用户匹配,或者创建一个与请求内容匹配的 FlowSchema(对应于 resourceRules)。接下来,您可以将此 FlowSchema 映射到具有较低席位份额的 PriorityLevelConfiguration。
例如,假设默认命名空间中 Pod 发出的列表事件请求每个使用 10 个席位,并执行 1 分钟。为了防止这些昂贵的请求影响使用现有 service-accounts FlowSchema 的其他 Pod 的请求,您可以应用以下 FlowSchema 来将这些列表调用与其他请求隔离开来。
用于隔离列表事件请求的示例 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
- 此 FlowSchema 捕获默认命名空间中默认服务帐户发出的所有列表事件调用。匹配优先级 8000 低于现有 service-accounts FlowSchema 使用的值 9000,因此这些列表事件调用将匹配 list-events-default-service-account 而不是 service-accounts。
- catch-all PriorityLevelConfiguration 用于隔离这些请求。catch-all 优先级级别的并发份额非常小,并且不排队请求。
下一步
- 您可以访问流量控制参考文档以了解更多有关故障排除的信息。
- 有关 API 优先级和公平性设计细节的背景信息,请参阅增强提案。
- 您可以通过SIG API Machinery或该功能的Slack 频道提出建议和功能请求。