API 优先级和公平性

功能状态: Kubernetes v1.29 [稳定]

控制 Kubernetes API 服务器在超负荷情况下的行为是集群管理员的一项关键任务。<a class="glossary-tooltip" title="控制平面组件,提供 Kubernetes API 服务。" data-toggle="tooltip" data-placement="top" href="/docs/concepts/overview/components/#kube-apiserver" target="_blank" aria-label="kube-apiserver">kube-apiserver</a> 提供了一些可用的控制机制(例如 --max-requests-inflight--max-mutating-requests-inflight 命令行标志),用于限制将要接受的未完成工作量,防止大量传入请求使 API 服务器超载并可能导致其崩溃,但这些标志不足以确保在高流量期间最重要的请求能顺利完成。

API 优先级和公平功能 (APF) 是一种改进上述最大未完成请求限制的替代方案。APF 以更细粒度的方式对请求进行分类和隔离。它还引入了一定的队列限制,这样在流量短暂激增的情况下,就不会拒绝任何请求。请求从队列中以公平队列方式调度,例如,一个行为不佳的<a class="glossary-tooltip" title="一个控制循环,通过 apiserver 监视集群的共享状态,并进行更改尝试将当前状态移至预期状态。" data-toggle="tooltip" data-placement="top" href="/docs/concepts/architecture/controller/" target="_blank" aria-label="controller">控制器</a> 不应耗尽其他控制器(即使在同一优先级级别)。

此功能旨在与标准控制器配合良好,这些控制器使用信息器并对 API 请求失败采取指数级回退,以及其他以这种方式工作的客户端。

启用/禁用 API 优先级和公平性

API 优先级和公平性功能由命令行标志控制,默认情况下启用。有关可用 kube-apiserver 命令行选项以及如何启用和禁用它们的常规说明,请参见<a href="/docs/reference/command-line-tools-reference/kube-apiserver/#options">选项</a>。APF 命令行选项的名称为“--enable-priority-and-fairness”。此功能还涉及一个<a class="glossary-tooltip" title="Kubernetes API 中的一组相关路径。" data-toggle="tooltip" data-placement="top" href="/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning" target="_blank" aria-label="API Group">API 组</a>,其中包含:(a)一个在 1.29 中引入的稳定 v1 版本,默认情况下启用;(b)一个 v1beta3 版本,默认情况下启用,在 v1.29 中已弃用。您可以通过在 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-apisever 时最简单的方法)是在代码中禁用服务器 B 上的优先级和公平性。

概念

API 优先级和公平性功能涉及几个不同的功能。传入请求使用FlowSchemas 根据请求的属性进行分类,并分配到优先级级别。优先级级别通过维护独立的并发限制来增加一定程度的隔离,以便分配到不同优先级级别的请求不会相互耗尽。在同一优先级级别内,公平队列算法防止来自不同的请求相互耗尽,并允许对请求进行排队,以防止在平均负载可接受的情况下,突发流量导致请求失败。

优先级级别

在没有启用 APF 的情况下,API 服务器中的总体并发性由 kube-apiserver 标志 --max-requests-inflight--max-mutating-requests-inflight 限制。在启用 APF 的情况下,由这些标志定义的并发限制会进行求和,然后将总和分配给一组可配置的优先级级别。每个传入请求都会分配到一个优先级级别,每个优先级级别只调度其特定限制允许的并发请求数量。

例如,默认配置包括针对领导者选举请求、内置控制器请求和 Pod 请求的单独优先级级别。这意味着一个行为不佳的 Pod 即使向 API 服务器发送大量请求,也不会阻止领导者选举或内置控制器执行操作。

优先级级别的并发限制会定期调整,允许未充分利用的优先级级别暂时将并发性借给利用率高的级别。这些限制基于名义限制和优先级级别可以借用和借出的并发性上限,所有这些都源自下面提到的配置对象。

请求占用的席位

以上关于并发管理的描述是基本情况。请求具有不同的持续时间,但在与优先级级别的并发限制进行比较时,在任何给定时刻都被平等地计算。在基本情况中,每个请求占用一个并发单位。单词“席位”指的是一个并发单位,其灵感来自火车或飞机上每位乘客都占用一个固定供应的座位的方式。

但是有些请求会占用多个席位。其中一些是服务器估计将返回大量对象的列表请求。这些请求被发现会给服务器带来异常沉重的负担。出于这个原因,服务器会估计将返回的对象数量,并认为请求占用的席位数量与该估计数量成正比。

针对监视请求的执行时间调整

API 优先级和公平性管理**监视**请求,但这涉及对基本行为的额外调整。第一个调整与**监视**请求被认为占用席位的时长有关。根据请求参数,对**监视**请求的响应可能包含或不包含所有相关现有对象的创建通知。API 优先级和公平性认为,当这些初始通知(如果有)完成时,**监视**请求就完成了其席位。

每当服务器收到有关对象创建/更新/删除的通知时,正常的通知都会以并发方式发送到所有相关**监视**响应流。为了解释这项工作,API 优先级和公平性认为,每个写入请求在实际写入完成后会花费一些额外的时长占用席位。服务器会估计要发送的通知数量,并调整写入请求的席位数量和席位占用时间,以包含这项额外工作。

排队

即使在同一优先级级别内,也可能存在大量不同的流量来源。在超负荷情况下,防止一个请求流耗尽其他请求流(尤其是在单个错误客户端向 kube-apiserver 发送大量请求的常见情况下,该错误客户端理想情况下不会对其他客户端造成任何可衡量的影响)非常重要。这是通过使用公平队列算法来处理分配到相同优先级级别的请求来实现的。每个请求都会分配到一个,该由匹配的 FlowSchema 的名称加上一个流区分符(要么是请求用户,要么是目标资源的命名空间,要么是空)来标识,系统会尝试对同一优先级级别中不同流中的请求赋予大致相同的权重。为了实现对不同实例的不同处理,具有多个实例的控制器应使用不同的用户名进行身份验证。

将请求分类到流之后,API 优先级和公平性功能可能会将请求分配到一个队列。这种分配使用了一种称为<a class="glossary-tooltip" title="一种将请求分配到队列的技术,它比模队列数量的散列提供更好的隔离。" data-toggle="tooltip" data-placement="top" href="/docs/reference/glossary/?all=true#term-shuffle-sharding" target="_blank" aria-label="shuffle sharding">随机分片</a>的技术,它利用队列来隔离低强度流,使其不受高强度流的影响,从而实现相对高效的队列使用。

排队算法的细节对于每个优先级级别都是可调的,允许管理员在内存使用、公平性(独立流在总流量超过容量时都会取得进展的属性)、对突发流量的容忍度以及排队带来的额外延迟之间进行权衡。

豁免请求

某些请求被认为足够重要,不受此功能施加的任何限制约束。这些豁免防止配置不当的流量控制配置完全禁用 API 服务器。

资源

流量控制 API 包含两种类型的资源。 PriorityLevelConfigurations 定义了可用的优先级级别,每个级别可以处理的可用并发预算的份额,并允许微调排队行为。 FlowSchemas 用于对单个入站请求进行分类,将每个请求匹配到单个 PriorityLevelConfiguration。

PriorityLevelConfiguration

PriorityLevelConfiguration 表示单个优先级级别。每个 PriorityLevelConfiguration 对未完成请求的数量都有一个独立的限制,以及对排队请求数量的限制。

PriorityLevelConfiguration 的标称并发限制不是以绝对的座位数来指定,而是以“标称并发份额”来指定。API 服务器的总并发限制按比例分配给现有的 PriorityLevelConfigurations,以这些份额为基础,以便每个级别以座位数获得其标称限制。这允许集群管理员通过使用不同的 --max-requests-inflight(或 --max-mutating-requests-inflight)值重新启动 kube-apiserver 来增加或减少服务器的总流量,并且所有 PriorityLevelConfigurations 都将看到其最大允许并发增加(或减少)相同的比例。

优先级级别可以借用和借出多少并发的界限在 PriorityLevelConfiguration 中以该级别标称限制的百分比表示。这些通过乘以标称限制 / 100.0 并四舍五入转换为绝对座位数。优先级级别的动态调整并发限制被限制在 (a) 其标称限制减去其可借出座位数的下限和 (b) 其标称限制加上其可借用座位数的上限之间。在每次调整时,动态限制由每个优先级级别收回最近出现需求的任何借出座位,然后在上述界限内共同公平地响应最近的优先级级别上的座位需求而得出。

当分配给单个 PriorityLevelConfiguration 的入站请求量超过其允许的并发级别时,其规范的 type 字段决定了对额外请求将如何处理。Reject 类型意味着多余的流量将立即被拒绝,并出现 HTTP 429(请求过多)错误。Queue 类型意味着超过阈值的请求将被排队,并使用洗牌分片和公平排队技术来平衡请求流之间的进度。

排队配置允许调整优先级级别的公平排队算法。算法的详细信息可以在 增强建议 中找到,但简而言之

  • 增加 queues 会降低不同流之间发生碰撞的速率,但会以增加内存使用量为代价。这里的值为 1 实际上会禁用公平排队逻辑,但仍然允许请求排队。

  • 增加 queueLengthLimit 允许更大的流量突发在不丢弃任何请求的情况下维持,但会以增加延迟和内存使用量为代价。

  • 更改 handSize 允许您调整不同流之间发生碰撞的概率以及在过载情况下单个流可用的总并发量。

以下是显示一系列有趣的洗牌分片配置的表格,显示了对于一组说明性的象的数量,每个配置中一只给定的小鼠(低强度流)被大象(高强度流)压扁的概率。见 https://play.golang.org/p/Gi0PLgVHiUg ,它计算了此表。

示例洗牌分片配置
HandSizeQueues1 头大象4 头大象16 头大象
12324.428838398950118e-090.114313488300991440.9935089607656024
10321.550093439632541e-080.06264798402235450.9753101519027554
10646.601827268370426e-120.000455713209903707760.49999929150089345
9643.6310049976037345e-110.000455012123041122730.4282314876454858
8642.25929199850899e-100.00048866970530404460.35935114681123076
81286.994461389026097e-133.4055790161620863e-060.02746173137155063
71281.0579122850901972e-116.960839379258192e-060.02406157386340147
72567.597695465552631e-146.728547142019406e-080.0006709661542533682
62562.7134626662687968e-122.9516464018476436e-070.0008895654642000348
65124.116062922897309e-144.982983350480894e-092.26025764343413e-05
610246.337324016514285e-168.09060164312957e-114.517408062903668e-07

FlowSchema

FlowSchema 匹配某些入站请求并将它们分配到优先级级别。每个入站请求都针对 FlowSchemas 进行测试,从数字上最小的 matchingPrecedence 开始,并向上工作。第一个匹配项获胜。

如果 FlowSchema 的至少一个 rules 匹配,则 FlowSchema 匹配给定请求。如果规则的至少一个 subjects 以及至少一个 resourceRulesnonResourceRules(取决于传入请求是针对资源还是非资源 URL)匹配请求,则该规则匹配。

对于 subjects 中的 name 字段以及 resource 和 non-resource 规则的 verbsapiGroupsresourcesnamespacesnonResourceURLs 字段,可以使用通配符 * 来匹配给定字段的所有值,从而有效地将其从考虑范围中移除。

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-managersystem:kube-scheduler 用户以及 kube-system 命名空间中的服务帐户的 endpointsconfigmapsleases 的请求)。这些很重要,需要与其他流量隔离,因为领导者选举中的故障会导致它们的控制器失败并重新启动,这反过来又会导致更昂贵的流量,因为新的控制器同步它们的信息收集器。

  • workload-high 优先级级别用于来自内置控制器的其他请求。

  • workload-low 优先级级别用于来自任何其他服务帐户的请求,这通常包括来自在 Pod 中运行的控制器的所有请求。

  • global-default 优先级级别处理所有其他流量,例如非特权用户运行的交互式 kubectl 命令。

建议的 FlowSchemas 用于将请求引导到上述优先级级别,这里没有列出。

强制性和建议性配置对象的维护

每个 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 的对象。

健康检查并发豁免

建议的配置对来自其本地 kubelets 的 kube-apiservers 上的健康检查请求没有特殊处理,这些请求往往使用安全端口但没有提供凭据。使用建议的配置,这些请求将被分配给 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"

可观察性

指标

启用 API 优先级和公平性功能后,kube-apiserver 会导出其他指标。监控这些指标可以帮助您确定您的配置是否不适当地限制了重要流量,或找到可能损害系统健康的运行状况不佳的工作负载。

成熟度级别 BETA

  • apiserver_flowcontrol_rejected_requests_total 是一个计数器向量(自服务器启动以来的累积),用于统计被拒绝的请求,并通过标签 flow_schema(指示与请求匹配的标签)、priority_level(指示请求被分配到的标签)和 reason 进行细分。reason 标签将是以下值之一:

    • queue-full,指示已排队过多的请求。
    • concurrency-limit,指示 PriorityLevelConfiguration 配置为拒绝而不是排队过多的请求。
    • time-out,指示请求在排队时间限制到期时仍在排队中。
    • cancelled,指示请求没有被锁定,并且已被从队列中弹出。
  • apiserver_flowcontrol_dispatched_requests_total 是一个计数器向量(自服务器启动以来的累积),用于统计已开始执行的请求,并通过 flow_schemapriority_level 进行细分。

  • apiserver_flowcontrol_current_inqueue_requests 是一个量规向量,用于统计正在排队(未执行)的请求数量,并通过 priority_levelflow_schema 进行细分。

  • apiserver_flowcontrol_current_executing_requests 是一个量规向量,用于统计正在执行(未在队列中等待)的请求数量,并通过 priority_levelflow_schema 进行细分。

  • apiserver_flowcontrol_current_executing_seats 是一个量规向量,用于统计已占用席位的数量,并通过 priority_levelflow_schema 进行细分。

  • apiserver_flowcontrol_request_wait_duration_seconds 是一个直方图向量,用于统计请求在队列中等待的时间,并通过标签 flow_schemapriority_levelexecute 进行细分。execute 标签指示请求是否已开始执行。

  • apiserver_flowcontrol_nominal_limit_seats 是一个量规向量,用于统计每个优先级级别的名义并发限制,该限制根据 API 服务器的总并发限制和优先级级别的配置的名义并发份额计算得出。

成熟度级别 ALPHA

  • apiserver_current_inqueue_requests 是一个量规向量,用于统计排队请求数量的最近高水位,并通过名为 request_kind 的标签进行分组,其值为 mutatingreadOnly。这些高水位描述了在最近完成的 1 秒窗口中看到的最多的数量。这些补充了较旧的 apiserver_current_inflight_requests 量规向量,该向量保存了最后一个窗口的活动服务请求数量的高水位。

  • apiserver_current_inqueue_seats 是一个量规向量,用于统计排队请求中每个请求将占用的最多席位的总和,并通过名为 flow_schemapriority_level 的标签进行分组。

  • apiserver_flowcontrol_read_vs_write_current_requests 是一个直方图向量,用于统计在每个纳秒结束时观察到的请求数量,并通过标签 phase(取值为 waitingexecuting)和 request_kind(取值为 mutatingreadOnly)进行细分。每个观察到的值都是一个比率,介于 0 和 1 之间,表示请求数量除以相应的请求数量限制(对于等待状态为队列容量限制,对于执行状态为并发限制)。

  • apiserver_flowcontrol_request_concurrency_in_use 是一个量规向量,用于统计已占用席位的数量,并通过 priority_levelflow_schema 进行细分。

  • apiserver_flowcontrol_priority_level_request_utilization 是一个直方图向量,用于统计在每个纳秒结束时观察到的请求数量,并通过标签 phase(取值为 waitingexecuting)和 priority_level 进行细分。每个观察到的值都是一个比率,介于 0 和 1 之间,表示请求数量除以相应的请求数量限制(对于等待状态为队列容量限制,对于执行状态为并发限制)。

  • apiserver_flowcontrol_priority_level_seat_utilization 是一个直方图向量,用于统计在每个纳秒结束时观察到的优先级级别并发限制的利用率,并通过 priority_level 进行细分。这种利用率是 (已占用席位数量) / (并发限制) 的分数。此指标考虑除 WATCHes 之外的所有请求的所有执行阶段(包括正常阶段和写入结束时的额外延迟以涵盖相应的通知工作);对于 WATCHes,它只考虑传递现有对象通知的初始阶段。向量中的每个直方图还用 phase: executing 进行标记(等待阶段没有席位限制)。

  • apiserver_flowcontrol_request_queue_length_after_enqueue 是一个直方图向量,用于统计排队请求采样的队列长度,并通过 priority_levelflow_schema 进行细分。每个排队的请求都会对其直方图贡献一个样本,报告请求添加后队列的长度。请注意,这会产生与无偏调查不同的统计数据。

  • apiserver_flowcontrol_request_concurrency_limitapiserver_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_schemapriority_level 进行细分。

  • apiserver_flowcontrol_watch_count_samples 是一个直方图向量,用于统计与给定写入相关的活动 WATCH 请求的数量,并通过 flow_schemapriority_level 进行细分。

  • apiserver_flowcontrol_work_estimated_seats 是一个直方图向量,用于统计与请求关联的估计席位数量(执行的初始阶段和最终阶段的最大值),并通过 flow_schemapriority_level 进行细分。

  • apiserver_flowcontrol_request_dispatch_no_accommodation_total 是一个计数器向量,用于统计原则上可能导致请求被调度但未被调度的事件数量,因为没有可用的并发,并通过 flow_schemapriority_level 进行细分。

  • apiserver_flowcontrol_epoch_advance_total 是一个计数器向量,用于统计尝试将优先级级别的进度计向后跳跃以避免数字溢出的次数,并通过 priority_levelsuccess 进行分组。

使用 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 个座位。如果在 1.27 及更高版本中使用流式列表,监视请求将占用与列表请求相同的座位数量,用于其初始通知爆发,因为必须流式传输集合的整个状态。请注意,在这两种情况下,监视请求在此初始阶段之后都不会占用任何座位。

请记住,APF 造成的排队或被拒绝的请求可能是由请求数量增加或现有请求延迟增加引起的。例如,如果通常需要 1 秒才能执行的请求开始需要 60 秒,则 APF 可能开始拒绝请求,因为由于延迟增加,请求占用的座位时间比平时更长。如果 APF 在多个优先级级别开始拒绝请求,而工作负载没有明显变化,则可能是控制平面性能存在潜在问题,而不是工作负载或 APF 设置。

优先级和公平性设置

您还可以修改默认的 FlowSchema 和 PriorityLevelConfiguration 对象,或创建这些类型的新对象,以更好地适应您的工作负载。

APF 设置可以修改为

  • 为高优先级请求提供更多座位。
  • 隔离非必要的或昂贵的请求,这些请求如果与其他流共享并发级别,将会导致并发级别匮乏。

为高优先级请求提供更多座位

  1. 如果可能,可以通过增加 `max-requests-inflight` 和 `max-mutating-requests-inflight` 标志的值来增加特定 `kube-apiserver` 所有优先级级别中的可用座位数。或者,水平扩展 `kube-apiserver` 实例数量将增加集群中每个优先级级别的总并发数,前提是请求有足够的负载均衡。
  2. 您可以创建一个新的 FlowSchema,它引用具有更大并发级别的 PriorityLevelConfiguration。此新的 PriorityLevelConfiguration 可以是现有级别,也可以是具有自己的一组名义并发份额的新级别。例如,可以引入一个新的 FlowSchema,将您的请求的 PriorityLevelConfiguration 从 global-default 更改为 workload-low,以增加可用于您的用户的座位数量。创建一个新的 PriorityLevelConfiguration 将减少为现有级别指定的座位数量。请记住,编辑默认的 FlowSchema 或 PriorityLevelConfiguration 需要将 `apf.kubernetes.io/autoupdate-spec` 注释设置为 false。
  3. 您还可以增加为高优先级请求提供服务的 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。
  • 通用 PriorityLevelConfiguration 用于隔离这些请求。通用优先级级别具有非常小的并发份额,并且不排队请求。

下一步

上次修改于 2024 年 7 月 2 日下午 1:35 PST:修复了错别字 (856c384387)