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 允许我们通过 requests
和 limits
来定义 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.shares
和 cpu.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 请求即可,限制应谨慎使用。