本文发布已超过一年。较早的文章可能包含过时的内容。请核实页面信息自发布以来是否已不再准确。

内存资源的质量服务(Quality-of-Service)

Kubernetes v1.22 版本于 2021 年 8 月发布,引入了一个新的 Alpha 特性,改进了 Linux 节点实现内存资源请求和限制的方式。

在之前的版本中,Kubernetes 不支持内存质量保证。例如,如果您按如下方式设置容器资源:

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  containers:
  - name: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "64Mi"
        cpu: "500m"

spec.containers[].resources.requests (例如 cpu, memory) 是为调度设计的。当你创建一个 Pod 时,Kubernetes 调度器会为其选择一个节点运行。每个节点对于每种资源类型都有最大容量:它可以为 Pods 提供的 CPU 和内存量。调度器确保,对于每种资源类型,已调度容器的资源请求总和小于节点的容量。

spec.containers[].resources.limits 在 kubelet 启动容器时传递给容器运行时。CPU 被认为是“可压缩”资源。如果你的应用开始达到 CPU 限制,Kubernetes 会开始限制你的容器,可能导致应用性能下降。然而,它不会被终止。这就是“可压缩”的含义。

在 cgroup v1 中以及此特性之前,容器运行时从未考虑且实际上忽略了 spec.containers[].resources.requests["memory"]。这与 CPU 不同,对于 CPU,容器运行时会同时考虑请求和限制。此外,在 cgroup v1 中内存实际上无法压缩。由于无法限制内存使用,如果容器超出其内存限制,它将被内核以 OOM(内存不足)杀死终止。

幸运的是,cgroup v2 带来了新的设计和实现,可以在内存上实现全面保护。新特性依赖于 cgroup v2,目前大多数 Linux 操作系统版本已经提供了该功能。通过此实验性特性,针对 Pods 和容器的服务质量(QoS)不仅涵盖 CPU 时间,也扩展到了内存。

工作原理是什么?

内存 QoS 使用 cgroup v2 的内存控制器来保障 Kubernetes 中的内存资源。Pod 中容器的内存请求和限制用于设置内存控制器提供的特定接口 memory.minmemory.high。当 memory.min 设置为内存请求时,内存资源被保留,并且永远不会被内核回收;这就是内存 QoS 如何确保 Kubernetes Pods 可用内存的方式。如果在容器中设置了内存限制,这意味着系统需要限制容器的内存使用量,内存 QoS 使用 memory.high 来限制接近其内存限制的工作负载,确保系统不会因瞬时内存分配而过载。

下表详细介绍了这两个参数的具体功能以及它们与 Kubernetes 容器资源的对应关系。

文件描述
memory.minmemory.min 指定 cgroup 必须始终保留的最小内存量,即永远不会被系统回收的内存。如果 cgroup 的内存使用达到此下限且无法增加,则会调用系统 OOM killer。

我们将其映射到容器的内存请求
memory.highmemory.high 是内存使用限制阈值。这是控制 cgroup 内存使用的主要机制。如果 cgroup 的内存使用超出此处指定的高位边界,则 cgroup 的进程将被限制,并承受沉重的回收压力。默认值为 max,表示没有限制。

我们使用一个公式计算 memory.high,该公式取决于容器的内存限制或节点可分配内存(如果容器的内存限制为空)以及一个限制因子。有关该公式的更多详细信息,请参阅 KEP。

提出容器内存请求后,kubelet 会在容器创建期间通过 CRI 中的 Unified 字段将 memory.min 传递给后端 CRI 运行时(可能是 containerd、cri-o)。容器级别 cgroup 中的 memory.min 将设置为


i:Pod 中的第 i 个容器

由于 memory.min 接口要求祖先 cgroup 目录都已设置,因此需要正确设置 Pod 和节点 cgroup 目录。

Pod 级别 cgroup 中的 memory.min

i:Pod 中的第 i 个容器

节点级别 cgroup 中的 memory.min

i:节点中的第 i 个 Pod,j:Pod 中的第 j 个容器

Kubelet 将使用 runc libcontainer 库直接管理 Pod 级别和节点级别 cgroups 的 cgroup 层次结构,而容器 cgroup 限制由容器运行时管理。

对于内存限制,除了限制内存使用的原始方式外,内存 QoS 还增加了限制内存分配的附加特性。引入了一个限制因子作为乘数(默认为 0.8)。如果内存限制乘以该因子的结果大于内存请求,kubelet 将把 memory.high 设置为该值,并通过 CRI 使用 Unified。如果容器未指定内存限制,kubelet 将转而使用节点可分配内存。容器级别 cgroup 中的 memory.high 设置为


i:Pod 中的第 i 个容器

这有助于在 Pod 内存使用量增加时提高稳定性,确保在接近内存限制时限制内存使用。

如何使用它?

以下是在 Linux 节点上启用内存 QoS 的先决条件,其中一些与 Kubernetes 对 cgroup v2 的支持有关。

  1. Kubernetes v1.22 及更高版本
  2. runc v1.0.0-rc93 及更高版本;containerd 1.4 及更高版本;cri-o 1.20 及更高版本
  3. Linux 内核最低版本:4.15,推荐版本:5.2+
  4. 启用 cgroup v2 的 Linux 镜像或手动启用 cgroup v2 unified_cgroup_hierarchy

runc 和 crun 等 OCI 运行时已支持 cgroups v2 Unified,并且 Kubernetes CRI 也已进行了相应的更改以支持传递 Unified。然而,还需要 CRI 运行时支持。Alpha 阶段的内存 QoS 特性旨在支持 containerd 和 cri-o。相关的 PR Feature: containerd-cri support LinuxContainerResources.Unified #5627 已合并,并将随 containerd 1.6 版本发布。CRI-O 的 implement kube alpha features for 1.22 #5207 仍在进行中 (WIP)。

满足这些先决条件后,你可以启用内存 QoS 特性门(参见通过配置文件设置 kubelet 参数)。

如何了解更多?

你可以通过以下方式了解更多详情

如何参与?

你可以通过多种方式联系 SIG Node

你也可以直接联系我