1. 概述

在 Kubernetes 中,有效的资源管理对于确保工作负载获得公平的资源访问、防止资源饥饿或过度使用至关重要。CPU 请求和限制是实现这种平衡的重要工具。然而,CPU 限制的误用可能会适得其反,导致集群资源利用率低下

在本文中,我们将深入探讨 Kubernetes 中 CPU 资源的工作原理,了解 CPU 请求与限制的区别,以及为什么在某些场景下设置 CPU 限制可能并不合适。


2. Kubernetes 中的 CPU 与核心

CPU 是计算机的主要计算单元,负责执行指令。通常来说,一个核心(core)代表 CPU 内的一个独立处理单元,可以并行处理多个任务。现代 CPU 通常包含多个核心,从而实现并行计算,提高性能。

2.1. Kubernetes 中的 CPU 度量单位

在 Kubernetes 中,“core” 的含义略有不同。在 Kubernetes 中,core 是 CPU 资源的度量单位,1 core 等于 1000 millicores(毫核)。这种粒度允许我们更精细地定义 CPU 资源,从而更灵活地分配 CPU 时间。

需要注意的是,Kubernetes 并不是以实际计算能力来度量 CPU,而是以时间来度量。例如,当我们说某个工作负载需要至少 250 毫核时,意味着它在调度周期内应该获得单个 CPU 核心 25% 的执行时间。同理,如果一个工作负载使用了两个核心,则意味着它在任意时间窗口内占用了两个 CPU 核心的 100% 执行时间。

换句话说,Kubernetes 中的 CPU 核心度量是对集群处理时间分配的估算,而不是直接代表实际的计算能力。

2.2. CPU 请求与限制

Kubernetes 允许我们通过 requestslimits 来定义 Pod 的最小和最大 CPU 使用量。具体来说,我们可以在 Pod 的 YAML 定义中配置这些参数:

apiVersion: v1
kind: Pod
metadata:
  name: banking-service
spec:
  containers:
    - name: banking-service
      image: banking:1.0.0
      resources:
        requests:
          cpu: "250m"
        limits:
          cpu: "500m"

在上面的例子中,我们创建了一个名为 banking-service 的 Pod,它至少需要 250 毫核,最多可使用 500 毫核。

2.3. CPU 请求影响 Pod 调度

Kubernetes 中的调度器 kube-scheduler 负责决定将 Pod 调度到哪个节点。在做决策时,kube-scheduler 会考虑 Pod 所请求的 CPU 量以及各节点的可用 CPU 资源。只有当节点的可用 CPU 至少等于 Pod 的请求值时,调度器才会将该 Pod 调度到该节点上

举个例子:假设我们的集群中有两个节点,第一个节点剩余 500 毫核,第二个节点剩余 2 核(即 2000 毫核)。如果我们要调度一个请求 1500 毫核的 Pod,kube-scheduler 会选择第二个节点,因为第一个节点的可用资源不足。

2.4. CPU 是可压缩资源

CPU 是一种可压缩资源(compressible resource),这意味着即使 Pod 没有获得预期的 CPU 资源,它也不会被直接终止。这与内存等不可压缩资源不同

例如,一个 Pod 正常运行时使用 500 毫核,当流量突增导致 CPU 需求上升时,如果集群无法满足该需求,Pod 仍会继续运行,只是性能下降而已。

而内存不足时,系统会直接杀掉 Pod(OOMKilled)。因此,在调度时,kube-scheduler 会考虑内存限制,但不会考虑 CPU 限制。


3. Kubernetes CPU 请求与限制的实现机制

kube-scheduler 在节点上创建 Pod 时,底层容器运行时(如 containerd)会创建容器。在 Linux 系统中,**cgroup 控制容器的 CPU 访问权限,其中两个关键参数是 cpu.sharescpu.cfs_quota_us**。

3.1. 通过 cpu.shares 实现 CPU 请求

cpu.shares 对应 Pod 中定义的 CPU 请求。它控制 cgroup 在给定时间窗口内能获得的相对 CPU 时间

例如,两个 cgroup 的 cpu.shares 分别为 100 和 200,那么它们将分别获得 33.3% 和 66.7% 的 CPU 时间。

当容器运行时创建容器时,它会将 Pod 中定义的 CPU 请求值转换为对应节点上的 cpu.shares 值。

3.2. 通过 cpu.cfs_quota_us 实现 CPU 限制

cpu.cfs_quota_us 表示一个 cgroup 在一个调度周期内最多能使用的 CPU 时间(以微秒为单位)。默认调度周期为 cpu.cfs_period_us=1000000 微秒(即 1 秒)。

cfs 表示 Completely Fair Scheduler,这是 Linux 内核默认的调度算法(截至 6.6 版本)

容器运行时会将我们在 Pod 中定义的 CPU 限制值(以核为单位)转换为对应的 cpu.cfs_quota_us 值。


4. 设置 CPU 限制可能导致的资源浪费

在传统资源管理中,设置硬性限制似乎是一种保护集群稳定性的直观做法。但在 Kubernetes 中,CPU 资源的管理需要更加细致的思考。

首先,我们需要明确设置 CPU 限制的目的。如果是出于治理目的(如控制付费用户的资源配额),则必须设置 CPU 限制

但对大多数 Kubernetes 集群来说,目标是在保证稳定的同时最大化资源利用率。在这种情况下,最合理的做法是设置 CPU 请求,而不设置 CPU 限制

设置 CPU 请求可以保证 Pod 至少能获得一定的 CPU 资源,而在需要时可以自由使用空闲 CPU。这样既能保证公平,又能提高整体资源利用率。

举个例子:

假设我们有一个 4 核的集群,部署了两个 Pod,每个 Pod 请求 1 核,限制为 2 核。

  • 在正常情况下,每个 Pod 使用 1 核。
  • 当其中一个 Pod 突然需要更多 CPU 时,由于另一个 Pod 没有使用完自己的配额,它也无法利用额外的 CPU,导致资源浪费。

而如果我们不设置 CPU 限制:

  • 每个 Pod 依然保证至少有 1 核可用。
  • 当其中一个 Pod 需要更多资源时,它可以使用空闲 CPU,从而提升性能。
  • 同时,另一个 Pod 的基本资源依然得到保障,不会影响集群稳定性。

5. 总结

在本文中,我们了解了:

  • CPU 在 Kubernetes 中的度量单位是毫核(millicores)。
  • CPU 请求影响 Pod 的调度,而 CPU 限制不参与调度决策。
  • CPU 是可压缩资源,设置限制可能导致资源浪费。
  • 推荐做法是设置合理的 CPU 请求,而不设置 CPU 限制,以提升资源利用率。

因此,在大多数场景下,设置 CPU 请求即可,限制应谨慎使用


原始标题:CPU Requests and Limits in Kubernetes