API 优先级和公平性

特性状态: Kubernetes v1.29 [稳定]

在过载情况下控制 Kubernetes API 服务器的行为是集群管理员的一项关键任务。kube-apiserver 具有一些可用的控件(即 --max-requests-inflight--max-mutating-requests-inflight 命令行标志)来限制将接受的未完成工作量,从而防止大量入站请求过载并可能导致 API 服务器崩溃,但这些标志不足以确保最重要的请求在高流量期间通过。

API 优先级和公平性(APF)功能是上述最大并发限制的替代方案。APF 以更细粒度的方式对请求进行分类和隔离。它还引入了有限的排队量,因此在非常短暂的突发情况下不会拒绝任何请求。请求使用公平排队技术从队列中分派,例如,行为不当的控制器不必使其他控制器(即使在同一优先级级别)处于饥饿状态。

此功能旨在与使用 informers 并以指数退避响应 API 请求失败的标准控制器以及以这种方式工作的其他客户端良好配合。

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

API 优先级和公平性功能由命令行标志控制,并且默认情况下处于启用状态。有关可用的 kube-apiserver 命令行选项以及如何启用和禁用它们的常规说明,请参阅选项。APF 的命令行选项的名称是“--enable-priority-and-fairness”。此功能还涉及一个API 组,其中:(a)一个稳定的 v1 版本,在 1.29 中引入,并且默认启用(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 优先级和公平性功能涉及几个不同的功能。使用 FlowSchema 根据请求的属性对传入请求进行分类,并将其分配给优先级级别。优先级级别通过维护单独的并发限制来增加隔离度,从而使分配给不同优先级级别的请求不会相互饥饿。在优先级级别内,公平排队算法可以防止来自不同的请求相互饥饿,并允许请求排队,以防止突发流量在平均负载可接受时导致请求失败。

优先级级别

在未启用 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 将看到其允许的最大并发量以相同的比例增加(或减少)。

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

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

排队配置允许为优先级调整公平排队算法。算法的详细信息可以在增强提案中阅读,但简而言之

  • 增加 queues 可以减少不同流之间的冲突率,但会增加内存使用量。此处的值为 1 实际上会禁用公平排队逻辑,但仍允许请求排队。

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

  • 更改 handSize 允许您调整不同流之间发生冲突的可能性以及过载情况下单个流可用的整体并发性。

下面是一个表格,其中显示了一组有趣的洗牌分片配置,其中显示了对于一组说明性的象群数量,给定的鼠标(低强度流)被象群(高强度流)挤压的可能性。请参阅https://play.golang.org/p/Gi0PLgVHiUg,它计算此表。

示例洗牌分片配置
HandSize队列1 个象群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 字段,以及资源和非资源规则的 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 的请求)。将这些请求与其他流量隔离很重要,因为领导者选举失败会导致它们的控制器失败并重新启动,这反过来会导致新的控制器同步其 informer 时产生更多的开销流量。

  • 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 上的健康检查请求进行特殊处理 —— 这些 kubelets 倾向于使用安全端口但不提供凭据。使用建议的配置,这些请求会被分配到 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。这些最高水位标记描述了最近完成的一秒窗口中看到的最多数量。这些补充了较旧的 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 分解。此利用率是 (已占用席位数量) / (并发限制) 的分数。此指标考虑了所有执行阶段(包括正常阶段和写入结束时的额外延迟,以覆盖相应的通知工作),但 WATCH 除外;对于这些,它仅考虑传递预先存在对象的通知的初始阶段。向量中的每个直方图也标有 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 个席位,具体取决于请求期间提取的对象数量。限制列表请求中检索的对象数量(例如,使用分页)将在较短的时间内使用更少的总席位。此外,用 watch 请求替换列表请求将需要较低的总并发份额,因为 watch 请求仅在其初始通知爆发期间占用 1 个席位。如果在 1.27 及更高版本中使用流式列表,则 watch 请求在其初始通知爆发期间将占用与列表请求相同数量的席位,因为必须流式传输集合的整个状态。请注意,在这两种情况下,watch 请求在此初始阶段之后都不会持有任何席位。

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

优先级和公平性设置

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

可以修改 APF 设置以

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

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

  1. 如果可能,可以通过增加 max-requests-inflightmax-mutating-requests-inflight 标志的值来增加特定 kube-apiserver 的所有优先级级别可用的席位数量。或者,水平扩展 kube-apiserver 实例的数量将增加整个集群中每个优先级级别的总并发量,前提是请求的负载均衡足够。
  2. 您可以创建一个新的 FlowSchema,该 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 的主题与发出这些请求的用户匹配,或者创建一个 FlowSchema,该 FlowSchema 与请求的内容匹配(对应于 resourceRules)。接下来,您可以将此 FlowSchema 映射到具有低份额席位的 PriorityLevelConfiguration。

例如,假设默认命名空间中运行的 Pod 发出的列表事件请求每个使用 10 个席位并执行 1 分钟。为了防止这些昂贵的请求影响其他 Pod 使用现有 service-accounts FlowSchema 发出的请求,您可以应用以下 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(太平洋标准时间):修复拼写错误 (856c384387)