API优先级与公平性


API Priority and Fairness (APF v1.29 stable)原则体现在,当 API 服务器过载的时候,处理请求与响应的能力。kube-apiserver 有一些选项如--max-requests-inflight--max-mutating-requests-inflight,可以限制将要接受的未处理请求,从而防止过量请求入站,潜在导致 API 服务奔溃。这些选项不足以保证在高流量期间,最重要的请求仍能被服务器接受。

APF 可提升 apiserver 过载情况下的并发限制。以更细粒度的方式对请求进行分类(优先级配置)和隔离(每个优先级分配自定义并发限制),引入空间有限的排队机制(公平排队分发请求),可在非常短暂的突发情况下,保证 apiserver 不会拒绝任何请求。这种情况能够保证所有请求都可以得到 apiserver 的响应。

APF 在设计上期望能与标准控制器(如 Deployment 等???)一起协同工作,这类控制器在处理失效时能够执行指数型回退(指回退时间)。

APF 稳定版 v1 默认被启用,可通过--enable-priority-and-fairness开启或禁用 APF。

请求通过 FlowSchema 按照其属性分类,并分配优先级。每个优先级分配自定义并发限制(隔离)。公平排队算法可以防止来自不同 Flow 的请求相互饿死,该算法将请求排队,防止因低平均负载因通信量突增而导致请求失败。

基本概念

优先级

如果未启用 APF,apiserver 中的整体并发量将收到 kube-apiserver 的参数--max-requests-inflight--max-mutating-requests-inflight的限制。启用 APF 后,将对这些参数定义的并发限制进行求和,然后将总和分配到一组可配置的优先级中。因为每个请求都会分配一个优先级,且每个优先级都有各自的并发限制,以此来达到即使异常的 Pod 向 apiserver 发送大量请求,也无法阻止诸如领导者选举或内置控制器的操作成功等高优先级的 http 请求。优先级的并发限制会被定期调整,允许利用率低的优先级将并发 seat 临时借给利用率高的优先级。

请求占用的 seat

有些请求占用一个 seat,有些请求占用多个 seat(可能附带其他请求)。对于后者 apiserver 将认为请求所返回的对象数量与所占用的 seat 成正比。即:如果一个请求返回一个 List 那么有可能这个请求会占用这个 List 长度的 seat。

watch 请求

APF 管理 watch 请求,要考虑除一个请求占用一个 seat 的情况。每当向 apiserver 发送通知创建/更新/删除一个对象时,正常都会以并发的方式发送所有相关的 watch 响应流,apiserver 估算要发送的通知数量,并调整写入请求占用的 seat 数以及额外工作继续占用的 seat 的时间。

排队

第一步:每个请求都被分配到某个 Flow(由对应的 FlowSchema 与 FlowDistinguisher)。FlowDistinguisher 可以是发出请求的用户、目标资源的名字空间或者为空等。apiserver 尝试为不同流中具有相同优先级的请求赋予近似相等的权重。第二步:apiserver 将请求分配到队列中,使用 shuffle-sharding 的技术,相对有效的利用队列隔离 low-intensity flows 与 high-intensity flows。

排队算法的细节可针对每个优先级进行调整,并允许管理员在内存占用、公平性、突发流量容忍度以及排队引发的额外延迟之间进行权衡。

豁免请求

某些特别重要的请求不受制于此特性施加的任何限制。这些豁免可防止不当的流控配置完全禁用 apiserver。

资源

PriorityLevelConfiguration

该对象定义可用的优先级,并按比例分配 kube-apiserver 定义的--max-request-inflight--max-mutating-requests-inflight总并发量限制。所以对于管理员来说,可以通过设定这 2 个参数的具体数值,然后集群中定义的所有PriorityLevelConfiguration都会按照比例相应缩减对应的并发限额。

一个优先级可以借出的并发数(seat)界限以及可以借用的并发数(seat)界限在PriorityLevelConfiguration表现该优先级的额定限制。这些界限值 * 额定限制 / 100.0 并取整,被解析为绝对 seat 数量。即一个优先级的动态调整并发限制范围在“器额定限制的下限 - 其可借出的 seat 数”和“其额定限制的上限 + 其可以借用的 seat 数”之间。在每次调整时,通过每个优先级推导得出动态限制,具体过程为回收最近出现需求的所有接触的 seat,然后在刚刚描述的界限内共同公平地响应有关这些优先级最近的 seat 要求。如果用户想要对给定资源分别进行处理,需要自己创建对应的 FlowSchema 对象,分别匹配对应的 mutating 和 non-mutating。

当入站请求数量大于分配的PriorityLevelConfiguration中允许的并发量(seat)时,type=Reject表示多余的请求将立即以 HTTP 429 的错误拒绝,type=Queue表示对允许并发量的请求进行排队处理(应用不同优先级的并发数量和公平排队的技术来平衡请求的处理)。

公平排队算法支持通过排队配置对优先级的微调,增强建议

  • queues 数值增大,能降低 flow 冲突概率,但是内存会增大,值为 1,则禁用公平排队逻辑
  • queueLengthLimit 数值增大,能提高并发,但是增加了等待时间和内存用量
  • handSize 允许调整过载情况下不同 flow 之间的冲突概率以及单个请求可用的整体并发性,较大使 2 个单独的 flow 发生碰撞的可能性变小(也就是一个操作可以饿死另一个,因为它占用了太久的 seat),更有可能的情况是少数 flow 可以调用 apiserver。较大的值还可能增加单个高并发 flow 的延迟时间。单个 flow 中可能排队的请求的最大数量为handSize*queueLengthLimit

FlowSchema

用于对每个入站请求进行分类,并与一个PriorityLevelConfiguration相匹配。每个入站请求都会对FlowSchema测试是否匹配,首先从matchingPrecedence数值最低的匹配开始,然后依次进行,直到首个匹配出现。

确定请求与某个FlowSchemarules的其中一条匹配的规则是:

  1. 要求该条规则的subjects字段至少存在一个与该请求相匹配
  2. 该条规则的resourceRulesnonResourceRules(取决于请求传入的是资源 URL 还是非资源 URL)字段至少存在一个与该请求匹配

对于subjects中的name字段和资源和非资源规则的verbs(如 GET,POST 等)、apiGroupsresourcesnamespacesnonResourceURLS字段,可以指定通配符*来匹配任意值。

FlowSchemadistinguisherMethod.type字段决定了如何把与该模式匹配的请求分发到不同的 flow 中,如:

  1. ByUser 一个用户将无法饿死其他容量的用户
  2. ByNamespace 一个命名空间中的对象资源请求将无法饿死其他命名空间中的对象资源请求
  3. 如果省略distinguisherMethod,这种情况将被视为与此FlowSchema相匹配的请求是单个 flow 的一部分。

默认值

kube-apiserver 会维护 2 种类型的 APF 配置对象:Mandatory 和 Suggested

健康检查豁免示例

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3
kind: FlowSchema
metadata:
  name: health-for-strangers
spec:
  matchingPrecedence: 1000 # 最低匹配数值
  priorityLevelConfiguration:
    name: exempt # 优先级用于完全不受流控限制的请求
  rules:
    - nonResourceRules:
        - nonResourceURLs: # 针对URL
            - "/healthz"
            - "/livez"
            - "/readyz"
          verbs: # 对所有的HTTP(GET,POST,PUT...)的请求
            - "*"
      subjects:
        - kind: Group
          group:
            name: "system:unauthenticated"

稳定性提升

根据提供的指标配置 APF 监控