API 优先级和公平性
Kubernetes v1.29 [稳定]
在过载情况下控制 Kubernetes API 服务器的行为是集群管理员的一项关键任务。kube-apiserver 具有一些可用的控件(即 --max-requests-inflight
和 --max-mutating-requests-inflight
命令行标志)来限制将接受的未完成工作量,从而防止大量入站请求过载并可能导致 API 服务器崩溃,但这些标志不足以确保最重要的请求在高流量期间通过。
API 优先级和公平性(APF)功能是上述最大并发限制的替代方案。APF 以更细粒度的方式对请求进行分类和隔离。它还引入了有限的排队量,因此在非常短暂的突发情况下不会拒绝任何请求。请求使用公平排队技术从队列中分派,例如,行为不当的控制器不必使其他控制器(即使在同一优先级级别)处于饥饿状态。
此功能旨在与使用 informers 并以指数退避响应 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 组 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 将看到其允许的最大并发量以相同的比例增加(或减少)。
注意
在v1beta3
之前的版本中,相关的 PriorityLevelConfiguration 字段名为“保证并发份额”而不是“标称并发份额”。此外,在 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
最高的 FlowSchema 进行分配。如果多个 matchingPrecedence
相等的 FlowSchemas 匹配同一请求,则具有字典顺序较小的 name
的 FlowSchema 将获胜,但最好不要依赖这一点,而是确保没有两个 FlowSchemas 具有相同的 matchingPrecedence
。如果 FlowSchema 的至少一个 rules
匹配给定的请求,则 FlowSchema 匹配该请求。如果至少一个 subjects
且至少一个 resourceRules
或 nonResourceRules
(取决于传入的请求是针对资源还是非资源 URL)匹配该请求,则规则匹配。
对于 subjects 中的 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
的请求)。将这些请求与其他流量隔离很重要,因为领导者选举失败会导致它们的控制器失败并重新启动,这反过来会导致新的控制器同步其 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,这将使这些请求免于速率限制。
注意
进行此更改还会允许任何恶意方发送与此 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 会导出其他指标。监视这些指标可以帮助你确定你的配置是否不适当地限制了重要的流量,或者查找可能损害系统健康的不良行为的工作负载。
成熟度级别 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_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
。这些最高水位标记描述了最近完成的一秒窗口中看到的最多数量。这些补充了较旧的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 除外;对于这些,它仅考虑传递预先存在对象的通知的初始阶段。向量中的每个直方图也标有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 个席位,具体取决于请求期间提取的对象数量。限制列表请求中检索的对象数量(例如,使用分页)将在较短的时间内使用更少的总席位。此外,用 watch 请求替换列表请求将需要较低的总并发份额,因为 watch 请求仅在其初始通知爆发期间占用 1 个席位。如果在 1.27 及更高版本中使用流式列表,则 watch 请求在其初始通知爆发期间将占用与列表请求相同数量的席位,因为必须流式传输集合的整个状态。请注意,在这两种情况下,watch 请求在此初始阶段之后都不会持有任何席位。
请记住,来自 APF 的排队或拒绝的请求可能是由于请求数量增加或现有请求的延迟增加引起的。例如,如果通常需要 1 秒执行的请求开始需要 60 秒执行,则 APF 可能会开始拒绝请求,因为请求由于延迟增加而占用席位的时间比正常时间长。如果 APF 开始跨多个优先级级别拒绝请求而工作负载没有显着变化,则可能是控制平面性能存在潜在问题,而不是工作负载或 APF 设置。
优先级和公平性设置
您还可以修改默认的 FlowSchema 和 PriorityLevelConfiguration 对象,或者创建这些类型的新对象,以便更好地适应您的工作负载。
可以修改 APF 设置以
- 为高优先级请求提供更多席位。
- 隔离非必要或昂贵的请求,这些请求如果与其他流共享则会使并发级别匮乏。
为高优先级请求提供更多席位
- 如果可能,可以通过增加
max-requests-inflight
和max-mutating-requests-inflight
标志的值来增加特定kube-apiserver
的所有优先级级别可用的席位数量。或者,水平扩展kube-apiserver
实例的数量将增加整个集群中每个优先级级别的总并发量,前提是请求的负载均衡足够。 - 您可以创建一个新的 FlowSchema,该 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 的主题与发出这些请求的用户匹配,或者创建一个 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 用于隔离这些请求。捕获所有请求的优先级级别具有非常小的并发份额,并且不排队请求。
下一步
- 您可以访问流控制参考文档以了解有关故障排除的更多信息。
- 有关 API 优先级和公平性的设计细节的背景信息,请参阅增强提案。
- 您可以通过SIG API Machinery或该功能的Slack 频道提出建议和功能请求。