天天看点

揭示 Kubernetes CPU 限制(和节流)

作者:吉祥庄钢铁侠

最近,我一直在调查在对 Wordpress 网站进行例行安全扫描时出现的高 CPU 占用率问题,这导致了响应缓慢、错误增加和其他不良结果。这种情况通常仅限于单个 pod(由扫描仪随机路由),但用户仍然可以看到(并会激活 Pagerduty ),因此我们希望获得更好的监控。

初步调查

像其他 IT 人员一样,在调查自己不确定的事情时,我首先求助于谷歌。我查看了其他人是如何监控 Kubernetes 中 pod 的 CPU 使用情况的。这让我第一次发现,监控 CPU 的节流情况比监控 CPU 的使用情况要有用得多。

我已经知道 kubernetes-mixin 项目,它为监控 Kubernetes 集群的健康状况提供了合理的默认 Prometheus 警报规则,所以我先去那里看看他们用什么规则来监控 CPU。目前,唯一包含的 CPU 使用率警报是 "CPUThrottlingHigh"(CPUThrottling 高),它的计算公式是:number_of_cpu_cycles_pod_gets_throttled / number_of_cpu_cycles_total (CPUThrottlingHigh 高)。

但等等,"节流 "是什么意思?Throttled(至少在我看来)的意思类似于 "减速",但在这种情况下,"节流 "意味着完全停止--在下一个 CFS 周期(Kubernetes 中为每 100 毫秒,Linux 默认也是如此--稍后详述)之前,你不能再使用 CPU。

虽然抽象地说,这似乎很简单明了,但当你在具有多个 CPU 内核的生产服务器上实际操作时,就会变得更加扑朔迷离。

概念化

在本文中,我指的是一台有 128 个 CPU 内核的服务器,它运行的 pod 的 CPU 限制为 4.0。

如果您对毫核(millicore)的概念不熟悉,只需知道 1 毫核 = 1/1000 CPU 时间(1000 毫核 = 1 整核)。这是用于确定 Kubernetes 中 CPU 需求/限制的指标。我们的示例 pod 有 4.0 的限制,即 4000 毫核,或 4 个整核的工作能力。

但操作系统内核是如何执行这些操作的呢?如果你熟悉 Linux 容器的工作原理,你可能听说过 Cgroups。简单地说,Cgroups 是一种隔离和控制进程组的方法,这样它们就不会察觉到与它们运行在同一服务器上的其他进程。这就是为什么当你运行一个 Docker 容器时,它会认为它的 ENTRYPOINT+CMD 是 PID 1。

除其他外,Cgroups 还使用 Linux CFS(完全公平调度程序)为进程组(如 Kubernetes 中的 pod)设置和应用资源限制。这是通过设置配额和周期来实现的。配额是指在一定时间内可以使用的 CPU 时间。一旦配额用完,就会被 "节流",直到下一个时段可以重新开始使用 CPU。

回到我们对毫核的讨论,这意味着在操作系统中每 100 毫秒的 cfs_period 内,我们可以获得 400 毫秒的允许使用时间。之所以能在 100 毫秒的时间内获得 400 毫秒,是因为每个内核都能在 100 毫秒的时间内完成 100 毫秒的工作,即 100 毫秒 x 4 个内核 = 400 毫秒。这 400 毫秒的工作时间可以以任何方式细分--可以转化为 4 个 vCPU 在 100 毫秒的时间段内各做 100 毫秒的工作,也可以转化为 8 个 vCPU 在 100 毫秒的时间段内各做 50 毫秒的工作,等等。请记住,CPU 限制是基于时间而非实际 vCPU。

理解了这一点,节流混乱的原因就开始变得清晰起来。据我所知,节流的理论上限是 n * (100ms),其中 n 是 vCPU 的数量,上限是在 100 毫秒窗口内分配的 CPU 毫秒数(预先用 cpuLimit * 100ms 计算)。这意味着在我的 128 核机器上,节流的理论上限是每秒 124 秒,因为(128 核 * 100 毫秒 - 400 毫秒)* 10 = 124。

注意:CPU 的实际节流取决于运行的进程数量和操作系统调度程序分配的内核。

组装

现在,事情终于开始进入我的大脑了。至少......考虑到我对 Linux 调度程序本身的所有琐碎细节仍然一无所知,它们已经尽可能多地进入了我的大脑。

整个调查的起因是,当我在 Prometheus 中对 container_cpu_cfs_throttled_seconds_total 指标使用 rate() 函数时,每秒的节流率明显高于 1 秒(大约接近每秒 70 秒)。在 1 秒钟的窗口内,一个 pod 的节流时间怎么会超过 1 秒?我想知道。

综合所有这些信息,我现在知道,之所以会出现如此高的节流,是因为 httpd 在额外的 CPU 内核上产生了额外的进程,从而使节流量远高于资源限制。

结论

现在我可以说,我们有足够的监控功能,可以根据 CPU 的节流时间提醒我们 CPU 的高使用率。这就是我们现在的警报:

- alert: Wordpress_High_CPU_Throttling
      expr: rate(container_cpu_cfs_throttled_seconds_total{namespace=~"wordpress-.*"}[1m]) > 1
      for: 30m
            labels:
        severity: warning
              annotations:
        message: The {{ $labels.pod_name }} pod in {{ $labels.namespace }} is experiencing a high amount of CPU throttling as a result of its CPU limit.
                   

结论

  1. Kubernetes 使用 cfs_period_us 100ms(Linux 默认值)
  2. 以 k8s 为单位的每个 1.0 CPU 请求代表 cfs_period 中 100ms 的 CPU 时间
  3. 理论上,这相当于 1 个 CPU 100%的时间,但实际上并非如此,因为 pod 通常会在多个内核上运行多个进程
  4. 内核报告 pod 被节流的时间上限取决于 pod 使用的 CPU 内核数量。
  5. 您使用的 CPU 内核数与 CPU 限制没有直接关系。它与 pod 正在运行的进程数量关系更大。内核报告 pod 被节流的时间上限取决于 pod 使用的 CPU 内核数量。

继续阅读