前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes APIServer 崩溃引出的流量控制使用

Kubernetes APIServer 崩溃引出的流量控制使用

作者头像
我是阳明
发布2023-08-21 15:26:54
8470
发布2023-08-21 15:26:54
举报
文章被收录于专栏:k8s技术圈

本文描述了我们最近遇到的一个真实案例:在一个集群中,Kubernetes APIServer 由于大量请求而瘫痪。今天,我们将讨论我们是如何解决这个问题并提供一些预防措施的。

问题

一天早上,我们接到了技术支持工程师的反馈,说一个客户的 Kubernetes 集群(包括生成环境)几乎无法正常工作了,要求必须帮助解决这个问题。当我们连接到故障集群后发下 APIServer 已经占用了所有内存,它们会崩溃、重启、再次崩溃、再次重启,一直这样循环下去,这就导致 Kubernetes APIServer 无法访问,完全无法正常工作了。

由于这是一个生产集群,为了能够快速解决问题,我们优先尝试通过向控制平面节点增加 CPU 和内存资源来临时修复问题,最开始我们添加资源后发现仍然不够,不过幸运的是,在继续添加一批资源之后,APIServer 稳定了下来。

问题的根源

虽然问题临时解决了,但我们仍然需要找到问题的根源,以便能够避免类似的问题再次发生。最初,控制平面节点有 8 个 CPU 和 16GB 的内存。在我们的干预之后,增加到了 16 个 CPU 和 64GB 的内存。

以下是问题发生时的内存消耗图表:

从上图可以看到内存消耗已经高达 50GB 了,后面经过分析我们发现是由于某些原因,Cilium pods 向 APIServer 发送了大量的 LIST 请求,由于集群规模较大且节点数量众多(超过 200 个),同时请求大大增加了内存的使用量。

然后我们与客户商定,在一个测试窗口内操作,重新启动 Cilium 代理,然后我们得到了下面的一些情况:

  • APIServer 的负载正在增加。
  • 它耗尽了节点上的所有内存。
  • 然后崩溃了。
  • 请求被重定向到另一个服务器。
  • 然后,同样的事情再次发生。

经过分析我们认为限制同时向 APIServer 发送 cilium-agent 请求的数量应该可以解决这个问题。在这种情况下,稍慢一点的 LIST 请求执行不会影响 Cilium 的性能。

解决方案

根据我们的分析,我们决定使用 Kubernetes 的流控管理功能来解决这个问题。我们使用了两个 Kubernetes 的流控管理功能:PriorityLevelConfigurationFlowSchema

我们创建了以下 FlowSchemaPriorityLevelConfiguration 资源清单:

代码语言:javascript
复制
---
apiVersion: flowcontrol.apiserver.k8s.io/v1beta1
kind: FlowSchema
metadata:
  name: cilium-pods
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 1000
  priorityLevelConfiguration:
    name: cilium-pods
  rules:
    - resourceRules:
        - apiGroups:
            - "cilium.io"
          clusterScope: true
          namespaces:
            - "*"
          resources:
            - "*"
          verbs:
            - "list"
      subjects:
        - group:
            name: system:serviceaccounts:d8-cni-cilium
          kind: Group
---
apiVersion: flowcontrol.apiserver.k8s.io/v1beta1
kind: PriorityLevelConfiguration
metadata:
  name: cilium-pods
spec:
  type: Limited
  limited:
    assuredConcurrencyShares: 5
    limitResponse:
      queuing:
        handSize: 4
        queueLengthLimit: 50
        queues: 16
      type: Queue

直接将上面的两个资源对象应用到集群中!创建后发现问题解决了,APIServer 的内存消耗也恢复到了正常水平,重新启动 cilium-agent 不会再次导致显著的内存消耗变化。因此,我们能够将节点资源削减为其原始资源。

管理 Kubernetes APIServer 的请求

在 Kubernetes 中,请求队列管理由 API 优先级和公平性 (APF) 处理。从 Kubernetes 1.20 版本开始,默认就启用了 APF,此外 APIServer 还提供两个参数:--max-requests-inflight(默认为 400)和 --max-mutating-requests-inflight(默认为 200),用于限制请求数量。如果启用了 APF,则这两个参数会相加,这就是 APIServer 的总并发限制的定义方式。

此外还有一些细节我们需要考虑:

  • Long-running 运行的 API 请求(例如,在 pod 中查看日志或执行命令)不受 APF 限制,WATCH 请求也不受限制。
  • 还有一个特殊的预定义优先级称为 exempt,该级别的请求会立即得到处理。

APF 确保 Cilium agent 请求不会限制用户 API 请求。APF 还允许您设置限制,以确保重要请求始终得到处理,而不受 K8s APIServer 负载的影响。

我们可以使用以下两个资源对象来配置 APF:

  • PriorityLevelConfiguration:定义可用的优先级级别之一。
  • FlowSchema:将每个传入请求映射到单个 PriorityLevelConfiguration

优先级配置

每个 PriorityLevelConfiguration 都有自己的并发限制(共享)。总并发限制按照它们的共享比例在现有的 PriorityLevelConfigurations 之间分配。

让我们通过以下示例计算该限制:

代码语言:javascript
复制
~# kubectl get prioritylevelconfigurations.flowcontrol.apiserver.k8s.io
NAME                 TYPE      ASSUREDCONCURRENCYSHARES   QUEUES   HANDSIZE   QUEUELENGTHLIMIT   AGE
catch-all            Limited   5                          <none>   <none>     <none>             193d
d8-serviceaccounts   Limited   5                          32       8          50                 53d
deckhouse-pod        Limited   10                         128      6          50                 90d
exempt               Exempt    <none>                     <none>   <none>     <none>             193d
global-default       Limited   20                         128      6          50                 193d
leader-election      Limited   10                         16       4          50                 193d
node-high            Limited   40                         64       6          50                 183d
system               Limited   30                         64       6          50                 193d
workload-high        Limited   40                         128      6          50                 193d
workload-low         Limited   100                        128      6          50                 193d
  1. 首先,累加所有 AssuredConcurrencyShares 的值(260)。
  2. 然后计算请求限制,例如,对于 workload-low 优先级级别计算为:(400+200)/260*100 = 230 个请求每秒。

假如我们改变其中一个值,看看会发生什么。例如,将 deckhouse-podAssuredConcurrencyShares 从 10 增加到 100。请求限制将降至(400+200)/350*100 = 171 个请求每秒。

通过增加 AssuredConcurrencyShares 的数量,我们增加了特定级别的查询限制,但降低了所有其他级别的限制

如果优先级级别中的请求数量超过允许的限制,请求将排队等待。你可以选择自定义队列参数,还可以配置 APF 以立即丢弃超过特定优先级级别限制的请求。

让我们看一下下面的示例:

代码语言:javascript
复制
---
apiVersion: flowcontrol.apiserver.k8s.io/v1beta1
kind: PriorityLevelConfiguration
metadata:
  name: cilium-pods
spec:
  type: Limited
  limited:
    assuredConcurrencyShares: 5
    limitResponse:
      queuing:
        handSize: 4
        queueLengthLimit: 50
        queues: 16
      type: Queue

在这里,优先级级别被配置为具有 AssuredConcurrencyShares = 5。如果没有其他自定义优先级级别,这将产生每秒 12 个请求。请求队列设置为 200 个请求(handSize * queueLengthLimit),并创建 16 个内部队列以更均匀地分发来自不同代理的请求。

关于 Kubernetes 流量控制中的优先级级别配置,还有一些重要的细节需要注意:

  • 拥有更多队列减少了流之间的碰撞次数,但增加了内存使用量。将其设置为 1 会禁用公平逻辑,但仍允许请求排队。
  • 增加 queueLengthLimit 可以处理高流量突发而不会忽略任何请求。然而,查询的处理速度较慢,并且需要更多的内存。
  • 通过更改 handSize,你可以调整流之间碰撞的可能性以及在高负载情况下单个流的总并发性。

这些参数是通过实验选择的:

  • 一方面,我们需要确保此优先级级别的请求不会处理得太慢。
  • 另一方面,我们需要确保 APIServer 不会被突然的流量激增而过载。

FlowSchema

接下来我们来看下 FlowSchema 资源,它的作用是将请求映射到适当的 PriorityLevel。其主要参数包括:

  • matchingPrecedence:定义 FlowSchema 的应用顺序,数字越低,优先级越高。
  • rules:定义请求过滤规则,格式与 Kubernetes RBAC 中的格式相同。
  • distinguisherMethod:指定一个参数(用户或命名空间),用于在将请求转发到优先级时将请求分离到流中,如果省略该参数,所有请求将分配给同一流(flow)。

示例:

代码语言:javascript
复制
---
apiVersion: flowcontrol.apiserver.k8s.io/v1beta1
kind: FlowSchema
metadata:
  name: cilium-pods
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 1000
  priorityLevelConfiguration:
    name: cilium-pods
  rules:
    - resourceRules:
        - apiGroups:
            - "cilium.io"
          clusterScope: true
          namespaces:
            - "*"
          resources:
            - "*"
          verbs:
            - "list"
      subjects:
        - group:
            name: system:serviceaccounts:d8-cni-cilium
          kind: Group

在上面的示例中,我们选择了所有针对 apiGroup: cilium.ioLIST 请求,包括集群范围的请求以及从所有命名空间发送到所有资源的请求。主体包括来自 d8-cni-cilium 服务账户的所有请求。

那么如何找出请求所在的 FlowSchemaPriorityLevelConfiguration 呢?

在响应时,APIServer 会提供特殊的 Header X-Kubernetes-PF-FlowSchema-UIDX-Kubernetes-PF-PriorityLevel-UID,你可以使用它们来查看请求的去向。

例如,让我们从 Cilium agent 的 ServiceAccount 向 API 发出一个请求:

代码语言:javascript
复制
TOKEN=$(kubectl -n d8-cni-cilium get secrets agent-token-45s7n -o json | jq -r .data.token | base64 -d)

curl https://127.0.0.1:6445/apis/cilium.io/v2/ciliumclusterwidenetworkpolicies?limit=500  -X GET --header "Authorization: Bearer $TOKEN" -k -I
HTTP/2 200
audit-id: 4f647505-8581-4a99-8e4c-f3f4322f79fe
cache-control: no-cache, private
content-type: application/json
x-kubernetes-pf-flowschema-uid: 7f0afa35-07c3-4601-b92c-dfe7e74780f8
x-kubernetes-pf-prioritylevel-uid: df8f409a-ebe7-4d54-9f21-1f2a6bee2e81
content-length: 173
date: Sun, 26 Mar 2023 17:45:02 GMT

kubectl get flowschemas -o custom-columns="uid:{metadata.uid},name:{metadata.name}" | grep 7f0afa35-07c3-4601-b92c-dfe7e74780f8
7f0afa35-07c3-4601-b92c-dfe7e74780f8   d8-serviceaccounts

kubectl get prioritylevelconfiguration -o custom-columns="uid:{metadata.uid},name:{metadata.name}" | grep df8f409a-ebe7-4d54-9f21-1f2a6bee2e81
df8f409a-ebe7-4d54-9f21-1f2a6bee2e81   d8-serviceaccounts

输出显示该请求属于 d8-serviceaccountsFlowSchemad8-serviceaccountsPriorityLevelConfiguration

需要关注的指标

Kubernetes APIServer 提供了几个需要关注的有用指标:

  • Apiserver_flowcontrol_rejected_requests_total:被拒绝的请求总数。
  • Apiserver_current_inqueue_requests:队列中当前请求的数量。
  • Apiserver_flowcontrol_request_execution_seconds:请求执行时间。

一些 debug 端点也可能有助于获取有用的信息:

代码语言:javascript
复制
kubectl get --raw /debug/api_priority_and_fairness/dump_priority_levels
PriorityLevelName,  ActiveQueues, IsIdle, IsQuiescing, WaitingRequests, ExecutingRequests
system,             0,            true,   false,       0,               0
workload-high,      0,            true,   false,       0,               0
catch-all,          0,            true,   false,       0,               0
exempt,             <none>,       <none>, <none>,      <none>,          <none>
d8-serviceaccounts, 0,            true,   false,       0,               0
deckhouse-pod,      0,            true,   false,       0,               0
node-high,          0,            true,   false,       0,               0
global-default,     0,            true,   false,       0,               0
leader-election,    0,            true,   false,       0,               0
workload-low,       0,            true,   false,       0,               0


kubectl get --raw /debug/api_priority_and_fairness/dump_queues
PriorityLevelName,  Index,  PendingRequests, ExecutingRequests, SeatsInUse, NextDispatchR,    InitialSeatsSum, MaxSeatsSum, TotalWorkSum
exempt,             <none>, <none>,          <none>,            <none>,     <none>,           <none>,          <none>,      <none>
d8-serviceaccounts, 0,      0,               0,                 0,          71194.55330547ss, 0,               0,           0.00000000ss
d8-serviceaccounts, 1,      0,               0,                 0,          71195.15951496ss, 0,               0,           0.00000000ss
...
global-default,     125,    0,               0,                 0,          0.00000000ss,     0,               0,           0.00000000ss
global-default,     126,    0,               0,                 0,          0.00000000ss,     0,               0,           0.00000000ss
global-default,     127,    0,               0,                 0,          0.00000000ss,     0,               0,           0.00000000ss

总结

我们最终通过设置请求队列管理解决了我们的问题,值得注意的是,这并不是我们在实践中遇到的唯一此类案例。在多次需要限制 API 请求后,我们将 APF 配置作为 Kubernetes 平台的重要组成部分。利用它可以帮助我们和我们的客户减少大型高负载 Kubernetes 集群中 API 拥塞问题的数量。

如果你在实践中也遇到了类似的问题,并且找到了其他解决方法,请在评论中分享你的经验。

原文链接:https://blog.palark.com/kubernetes-api-flow-control-management/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 k8s技术圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 问题的根源
  • 解决方案
  • 管理 Kubernetes APIServer 的请求
  • 优先级配置
  • FlowSchema
  • 需要关注的指标
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档