K8S CPU 请求和限制,是否有很好的选择?

2023-05-16

7e8ac05ade8784f4203c0bc7c4927402.gif

Limits 和 Requests 并不是 CPU 管理的灵丹妙药,在某些情况下,其他替代方案可能是更好的选择

在这篇博文中,您将了解到:

  • CPU requests 如何工作

  • CPU limits 如何工作

  • 按编程语言分类的现状

  • 限制不是最佳选择的情况

  • 您可以使用哪些替代方法来限制

Kubernetes CPU Requests

Requests 在 Kubernetes 中有两个目的:

调度可用性

首先,它们告诉 Kubernetes 需要多少 CPU 可用。然后它会过滤掉任何没有足够的 Requests 资源来完成调度的节点。

如果没有足够的 Requests CPU/内存,则 Pod 将处于未调度状态,直到有满足 Requests 的资源。

6df7f3f3291d7eb3a9586923f3631238.png

Guaranteed CPU 分配

一旦确定了在哪个节点上运行 Pod(在预选阶段幸存下来,然后得分最高的节点),它就会设置 Linux CPU shares 以大致与 mCPU 指标保持一致。CPU Shares 是 Linux 控制组 cgroups 功能[3]

通常,cpu_shares 可以是任意数字,Linux 使用每个 cgroup 的 shares 数与所有 shares 总数之间的比率来确定在 CPU 上应该调度哪个进程的优先级。例如,如果一个进程有 1,000 个 shares,所有 shares 的总和为 3,000,则它将获得总时间的三分之一。就 Kubernetes 而言:

  • 它应该是在每个节点上配置 Linux CPU shares 的唯一工具,并且它总是将这些 shares 与 mCPU 指标对齐。

  • 它只允许在机器上可用的实际逻辑 CPU 内核中请求最大的 mCPU(如果没有足够的未请求 CPU 可用,它将被调度器过滤掉)。

  • 因此,它将 Linux cgroups CPU shares 几乎变成了系统上容器最少 CPU 内核的“保证”,而在 Linux 上不一定这样使用。

由于设置 CPU request 是可选的,如果不设置会怎样?

  • 如果您通过 LimitRange 在命名空间上设置了一个 Limit,那么这个 Limit 将会应用。

  • 如果你没有设置一个 request,但设置了 limit(直接或通过命名空间 LimitRange),那么它也会设置一个 request 等于 limit。

  • 如果命名空间上没有 LimitRange,也没有 limit,那么如果不指定,就不会有 request。

如果不指定 CPU request:

  • 没有节点会在调度中被过滤掉,所以它可以在任何节点上调度。这意味着从 CPU 的角度来看,您可以过度配置集群。

  • 结果得到的 Pods/容器将是节点上优先级最低的,因为它们没有 CPU shares (而那些设置了 request 的 Pod 是有的)。

e8887f48ebd88d9f68f1ed96a50f129e.png

还要注意,如果您没有在 Pod 上设置 Request,则会将其作为 BestEffort 服务质量(QoS),并使其最有可能从节点中被逐出。

因此,设置一个足够高的 Request 来代表容器满足需求(可用性、性能/延迟等)所需的最小值是非常重要的。

36d48803acb9f45fb98abe73059916b5.png

此外,假设您的应用程序是无状态的,从可用性的角度来看,向外扩展(将 Pod 分布在更多的节点上)而不是向上扩展通常是有意义的。这也意味着在调度时,更小的 pod 更容易安装到您的节点上,从而更有效地利用我们的资源。

Kubernetes CPU 限制

除了请求,Kubernetes 也有 CPU 限制。限制是您的应用程序可以使用的最大资源量,但不能保证,因为它们取决于可用资源。

虽然您可以将请求视为确保容器至少具有该数量/比例的 CPU 时间,但限制却确保容器的 CPU 时间不能超过该数量。

虽然这可以保护系统上的其他工作负载免于与受限容器竞争,但它也会严重影响受限容器的性能。限制工作的方式也不直观,用户经常错误配置它们。

requests 使用 Linux cgroup CPU shares,而 limits 则使用 cgroup CPU quotas。

这些有两个主要组成部分:

  • Accounting Period:重置配额之前的时间量(以微秒为单位)。Kubernetes 默认将此设置为 100,000us(100 毫秒)。

  • 配额周期:cgroup(本例中为容器)在该周期内可以拥有的 CPU 时间量(以微秒为单位)。Kubernetes 设置了这个 CPU == 1000m CPU == 100,000us == 100ms。

840b1a0eac9b39aee038d8b13df2690c.png

如果这是一个单线程应用程序(一次只能在一个 CPU 核心上运行),那么这就很直观了。如果您将限制设置为 1 个 CPU,那么每 100 毫秒就会获得 100 毫秒的 CPU 时间,或者全部时间。问题是当我们有一个多线程应用程序可以同时在多个 cpu 上运行时。

如果您有两个线程,那么您可以在最短的 50ms 内消耗一个 CPU 周期(100ms)。10 个线程可以在 10 毫秒内消耗 1 个 CPU 周期,这样我们每 100 毫秒就会节流 90 毫秒,也就是 90%的时间!这通常会导致比没有节流的单个线程更差的性能。

另一个重要的考虑是,一些应用程序或语言/运行时将看到 Node 中的核心数量,并假设它可以使用所有的核心,而不管它的请求或限制。假设我们的 Kubernetes 节点/集群/环境在我们拥有的内核数量上不一致。在这种情况下,相同的 Limit 可能导致节点之间的不同行为,因为它们将运行不同数量的线程来对应不同数量的 core。

所以,你需要:

  • 设置 Limit 以容纳所有线程。

  • 降低线程数以便与 Limit 对齐。

除非语言/运行时知道查看我们的 cgroup 并自动适应你的 Limit。

编程语言的现状

node.js

Node 是单线程的(除非你使用 worker_threads),所以如果没有它们,你的代码就不能使用一个以上的 CPU 内核。这使得它成为 Kubernetes 上扩展到更多 pod 的良好候选,而不是扩展单个 pod。

  • 例外的是,Node.js 在默认情况下运行四个线程来执行各种操作(文件系统 IO、DNS、crypto 和 zlib),而 UV_THREADPOOL_SIZE 环境变量可以控制这一点。

  • 如果您确实使用 worker_threads,那么如果您想防止节流,那么您需要确保您运行的并发线程不会超过您的 CPU 限制。如果您通过 piscina 这样的线程池包使用它们,这将更容易做到,在这种情况下,您需要确保 maxThreads 被设置为与您的 CPU 限制相匹配,而不是与节点中的物理 CPU 相匹配(piscina 目前默认为物理 CPU 的 1.5 倍-并且不会从 cgroup/container limit 自动计算出限制)。

Python

像 Node.js 一样,解释性 Python 通常是单线程的,除了一些例外(使用多处理库,C/c++扩展等),不应该使用多个 CPU。这使得它成为一个很好的候选,就像 Node.js 一样,可以扩展到 Kubernetes 上的更多 pod,而不是扩展单个 pod。

注意,如果使用它,多处理库假定 Node 的物理内核数是默认情况下应该运行的线程数。目前似乎没有办法影响这种行为,除了在代码中设置它,而不是采用默认池大小。目前 GitHub 上有一个未解决的问题[4]

Java

Java 虚拟机 (JVM) 现在提供自动容器/cgroup 检测支持,这允许它确定在 Docker 容器中运行的 Java 进程可用的内存量和处理器数量。它使用此信息来分配系统资源。此支持仅适用于 Linux x64 平台。如果支持,则默认启用容器支持。如果不支持,您可以使用 -XX:ActiveProcessorCount=X 手动指定 CPU 限制的核心数[5]

NET/C

与 Java 一样,提供了自动容器/cgroup 检测支持[6]

Golang

为容器设置 GOMAXPROCS 环境变量[7]以匹配任何 CPU 限制。或者使用 automaxprocs 包[8] 根据 Uber 的容器限制自动设置。

真的需要限制吗?

也许您可以避免所有这些,只使用请求,因为它们讨论的是总 CPU 的比例,而不是像限制那样的 CPU 时间,因此在概念上更有意义。

限制的主要目的是确保特定的容器只能获得节点的特定份额。如果您的节点数量是相当固定的,可能是因为这是一个内部部署的数据中心,在接下来的几个月/几年里使用的裸金属节点数量是固定的,那么它们可能是维护整个多租户系统/环境的稳定性和公平性不得不接受的事。

然而,在云计算中,我们拥有的节点数量实际上是无限的,除了它们的成本。他们可以快速地提供和取消供应。这种弹性和灵活性是许多客户首先在云中运行这些工作负载的重要原因!

同样,需要多少节点直接取决于需要多少 pod 和它们的大小。需要的 pod 数量可以自动/动态地缩放,以响应在任何给定时间内要完成的工作量(请求的数量、队列中的项目数量等)。

我们可以做很多事情来节省成本,但是以一种损害其性能甚至可用性的方式限制工作负载应该是最后的手段。

Limit 的替代品

117ad8c52c4d5bf79a76ef8aebd3f1b1.png

让我们假设工作量增加(这会因工作负载而异,可能是请求/秒、队列中的工作量、响应的延迟等)。首先,Horizontal Pod Autoscaler (HPA) 会自动添加另一个 Pod,但没有足够的节点容量来调度它。Kubernetes Cluster Autoscaler 看到这个 Pending Pod,然后添加一个节点。

随后,工作日结束,由于要做的工作较少,HPA 缩减了几个 Pods。集群自动缩放器有足够的空闲容量来缩小一些节点,并且仍然适合所有运行的节点。因此,它排空并终止一些节点,以便为现在正在运行的较少的 Pods 调整环境的大小。

在这种情况下,我们不需要限制,只需要设置准确的请求(这样可以保证每个系统通过 CPU 共享运行所需的最小值)和集群自动定标器。这将确保不会对工作负载进行不必要的节流,并确保我们在任何给定时间都具有适当的容量。

结论

现在您更好地了解了 Kubernetes CPU 限制,是时候检查您的工作负载并适当地配置它们了。

这通常意味着确保运行的并发线程数符合限制(尽管某些语言/运行时,如 Java 和 C# 现在可以为您做到这一点)。

而且,有时,这意味着根本不使用它们,而是依靠 Requests 和 Horizontal Pod Autoscaler (HPA) 的组合来确保您始终有足够的容量以更动态(和更少的节流)方式运行。

作者:JASON UMIKER
出处:https://sysdig.com/blog/kubernetes-cpu-requests-limits-autoscaling/

参考资料

[1]

Kubernetes 限制和请求的基础知识: https://sysdig.com/blog/kubernetes-limits-requests/

[2]

内存不足终止和 CPU 节流: https://sysdig.com/blog/troubleshoot-kubernetes-oom/

[3]

CPU Shares 是 Linux 控制组 cgroups 功能: https://www.batey.info/cgroup-cpu-shares-for-kubernetes.html

[4]

目前 GitHub 上有一个未解决的问题: https://github.com/python/cpython/issues/77167

[5]

手动指定 CPU 限制的核心数: https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html#advanced-runtime-options-for-java

[6]

提供了自动容器/cgroup 检测支持: https://github.com/dotnet/designs/blob/main/accepted/2019/support-for-memory-limits.md.

[7]

为容器设置 GOMAXPROCS 环境变量: https://pkg.go.dev/runtime

[8]

automaxprocs 包: https://github.com/uber-go/automaxprocs


  

b6cc33fcf6a76603e834d1e1f0b00b8e.gif


  

10T 技术资源大放送!包括但不限于:Linux、虚拟化、容器、云计算、网络、Python、Go 等。在 开源Linux 公众号内回复 10T,即可免费获取!

Linux学习指南
有收获,点个在看
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

K8S CPU 请求和限制,是否有很好的选择? 的相关文章

  • 是否可以在支持 Intel IA-32e 模式的操作系统中运行 16 位代码?

    在 Intel 64 IA 32 架构手册第 3A 卷第 9 章处理器管理和初始化中 我发现了以下内容 兼容模式执行是基于代码段选择的 此模式允许旧应用程序与以 64 位模式运行的 64 位应用程序共存 在 IA 32e 模式下运行的操作系
  • 为什么 perf stat 将“stalled-cycles-backend”显示为<不支持>?

    Running perf stat ls显示这个 Performance counter stats for ls 1 388670 task clock 0 067 CPUs utilized 2 context switches 0 0
  • 制作一个编译 Tensorflow 二进制文件的 Dockerfile 以使用:SSE4.1、SSE4.2 和 AVX 指令

    那么 docker的目的之一就是轻松部署一个环境来测试软件 对吧 谁能告诉我如何编译 Tensorflow 二进制文件以在 docker 文件上使用 SSE4 1 SSE4 2 有人能指点我一个可以做到这一点的 docker 文件吗 如果有
  • 获取CPU或主板序列号?

    我正在尝试获取CPU serial or motherboard serial using C or Python出于许可目的 是否可以 我在用着Linux 在 Linux 下 您可以使用 lshw quiet xml 并解析其输出 您将在
  • CPU和GPU的区别

    CPU的单个处理单元和GPU的单个处理单元有什么区别 我在互联网上看到的大多数地方都涵盖了两者之间的高级差异 我想知道每条指令可以执行哪些指令 它们的速度有多快 以及这些处理单元如何集成到完整的架构中 这似乎是一个答案很长的问题 所以很多链
  • 多核汇编语言是什么样的?

    例如 曾几何时 要编写 x86 汇编程序 您可能会收到这样的指令 将值 5 加载到 EDX 寄存器 递增 EDX 寄存器等 对于具有 4 个核心 甚至更多 的现代 CPU 在机器代码级别看起来是否只是有 4 个独立的 CPU 即只有 4 个
  • 上下文切换线程等待

    我已经寻找这个问题的答案一天了 但找不到直接的答案 我正在阅读上下文切换等待队列之类的内容 确实很好地掌握了所有内容 在阅读一篇文章时 写到当发生车队情况时 将会有大量的上下文切换 那么让我直接说一下 假设一个线程处于等待互斥体解锁的队列中
  • Python CPU 计数在一台 Windows 服务器上工作但在另一台服务器上不起作用?

    我编写的代码可以在 Windows XP 和 Windows Server 2008 64 位上运行 但是 我刚刚启动了 Amazon Windows 64 位实例 代码失败 非常简单 看起来像这样 import multiprocessi
  • Linux下无root权限如何获取CPU序列号

    在没有root权限的Linux Ubuntu 下如何获取CPU序列号 我尝试了 cpuid 命令 它无需 root 权限即可工作 但似乎返回全零 我相信是因为需要在 BIOS 中更改某些内容 您能否建议我另一种从程序中检索 CPU 序列号的
  • Java限制资源使用

    有没有办法限制java使用的核心数量 同样 是否有可能限制该核心的使用量 您可以在 Linux 上使用任务集 您还可以降低进程的优先级 但除非 CPU 繁忙 否则进程将获得尽可能多的 CPU 我有一个将线程专用于核心的库 称为 Java T
  • 我如何访问 Intel CPU 计数器

    是否有任何小工具可以让我访问 Intel CPU 计数器收集的数据 例如 L1 L2 缓存未命中 分支预测失败 您知道现代 Core2 CPU 上有数百个此类数据 它必须在 Windows 上运行 同时能够在 Solaris FreeBSD
  • 如何在Python中模拟CPU和内存压力

    我想知道是否有人用 python 编写了模拟 cpu 和内存压力的代码 我看到一段加载 cpu 的代码 但如何强制它们在 90 的使用率下工作 一个节点主要有 4 种经常使用的资源 有效内存 中央处理器周期 储存空间 网络负载 上传和下载
  • Tensorflow 相同的代码,但从 CPU 设备到 GPU 设备得到不同的结果

    我正在尝试实现一个程序来测试 GPU 设备上的 Tensorflow 性能 数据测试是MNIST数据 使用多层感知器 神经网络 进行监督训练 我跟着这个简单的例子 http gist github com nishidy 8176548ec
  • 单核上的多线程有什么意义?

    我最近一直在研究 Linux 内核 并回顾了大学操作系统课程的时代 就像那时一样 我正在玩线程之类的东西 一直以来我一直假设线程是自动在多个核心上同时运行但我最近发现您实际上必须显式编写代码来处理多个核心 那么单核上的多线程有什么意义呢 我
  • CPU如何对指令重新排序

    我最近读到了有关 CPU 指令重新排序以提高效率的内容 但我无法理解CPU如何重新排序其指令 我的意思是编译时重新排序是可以想象的 因为编译器可以预见即将到来的代码 但是对于一个接一个地读取指令的CPU 它如何看到即将到来的指令并对它们重新
  • 使用 AMD FX 4100 四核获取 Linux ubuntu 12.10 上的 CPU 温度

    有很多类似的问题 但我还没有找到解决方案 如何在 Linux Ubuntu 12 10 上使用 C 或 C 获取 CPU 温度无需致电 sensors 我当然可以从文件中读取它 但是我找不到它在 12 10 中的存储位置 简单地读取文本文件
  • 阿迪和苏比之间到底是什么“关系”?

    我应该回答这个问题 经过一番研究后发现 add 和 sub 具有相同的操作码 仅在功能领域有所不同 这是答案还是其他什么 Update Nios II CPU 手册中提供了它 subi subtract immediate Operatio
  • python 进程占用 100% CPU

    我正在尝试运行 python 应用程序并根据指定的时间间隔执行操作 下面的代码持续消耗 100 的 CPU def action print print hello there interval 5 next run 0 while Tru
  • 通过 C 将线程固定到 cpuset 中的核心

    我有 cgroup cpuset set1 set1有2 5 8 我想将一个进程绑定到该 cpuset 然后将该进程中的一个线程固定到核心 4 cpuset 的名称 线程名称以及我应该将线程绑定到的核心位于 m 配置文件中 是否有任何 C
  • CPU是如何做减法的?

    我有一些基本的疑问 但每次我坐下来尝试面试问题时 这些问题和我的疑问就会出现 假设 A 5 B 2 假设A和B都是4字节 那么CPU是怎么做的呢 A B添加 我知道 A 的符号位 MSB 为 0 表示正值 B 的符号位为 1 表示负整数 现

随机推荐