Kubernetes v1.36:借助 Memory QoS 实现分层内存保护
我谨代表 SIG Node 宣布 Kubernetes v1.36 中 Memory QoS 特性(Alpha)的更新。
Memory QoS 使用 cgroup v2 的内存控制器,为内核提供更好的指引来处理容器内存。
它最早在 v1.22 中引入,并在 v1.27 中更新。
在 Kubernetes v1.36 中,我们引入了以下内容:可选启用的内存预留、基于 QoS 类的分层保护、
可观测性指标,以及针对 memory.high 的内核版本告警。
v1.36 的新变化
使用 memoryReservationPolicy 选择性启用内存预留
v1.36 将节流与预留分离开来。启用该特性门控后,会开启 memory.high 节流
(kubelet 基于 memoryThrottlingFactor 设置 memory.high,默认值为 0.9),
但内存预留现在由一个独立的 kubelet 配置字段控制:
None(默认):不写入memory.min或memory.low。通过memory.high进行的节流仍然有效。TieredReservation:kubelet 基于 Pod 的 QoS 类 写入分层内存保护:
Guaranteed Pod 通过 memory.min 获得硬保护。例如,一个请求 512 MiB 内存的
Guaranteed Pod 会得到:
$ cat /sys/fs/cgroup/kubepods.slice/kubepods-pod6a4f2e3b_1c9d_4a5e_8f7b_2d3e4f5a6b7c.slice/memory.min
536870912
内核在任何情况下都不会回收这部分内存。如果无法兑现这一保证, 它会对其他进程触发 OOM killer 以释放内存页。
Burstable Pod 通过 memory.low 获得软保护。对于同样请求 512 MiB 内存的
Burstable Pod:
$ cat /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8b3c7d2e_4f5a_6b7c_9d1e_3f4a5b6c7d8e.slice/memory.low
536870912
在正常压力下,内核会避免回收这部分内存;但如果另一种选择是系统范围的 OOM, 内核仍可能回收其中一部分。
BestEffort Pod 既不会获得 memory.min,也不会获得 memory.low。
它们的内存仍然是完全可回收的。
与 v1.27 行为的对比
在更早的版本中,启用 MemoryQoS 特性门控后,会立即为每个带有内存请求的容器设置 memory.min。
memory.min 是一种硬预留,无论内存压力如何,内核都不会回收它。
假设一个节点有 8 GiB RAM,而 Burstable Pod 的请求总量达到 7 GiB。
在更早版本中,这 7 GiB 会作为 memory.min 被锁定,
从而给内核、系统守护进程或 BestEffort 工作负载留下极少余量,
并增加 OOM kill 的风险。
在 v1.36 的分层预留机制下,这些 Burstable 请求会映射到 memory.low,而不是 memory.min。
在正常压力下,内核仍会保护这部分内存;但在极端压力下,
它可以回收其中一部分以避免系统范围的 OOM。
只有 Guaranteed Pod 才会使用 memory.min,这使得硬预留保持在更低水平。
借助 v1.36 中的 memoryReservationPolicy,
你可以先启用节流,观察工作负载行为,
然后在节点拥有足够余量时再选择启用预留。
可观测性指标
kubelet 的 /metrics 端点会暴露两个 Alpha 级稳定性指标:
| 指标 | 说明 |
|---|---|
kubelet_memory_qos_node_memory_min_bytes | Guaranteed Pod 的 memory.min 总量 |
kubelet_memory_qos_node_memory_low_bytes | Burstable Pod 的 memory.low 总量 |
这些指标对于容量规划非常有用。
如果 kubelet_memory_qos_node_memory_min_bytes
正在逐渐逼近节点的物理内存,
你就知道硬预留已经变得紧张了。
$ curl -sk https://localhost:10250/metrics | grep memory_qos
# HELP kubelet_memory_qos_node_memory_min_bytes [ALPHA] Total memory.min in bytes for Guaranteed pods
kubelet_memory_qos_node_memory_min_bytes 5.36870912e+08
# HELP kubelet_memory_qos_node_memory_low_bytes [ALPHA] Total memory.low in bytes for Burstable pods
kubelet_memory_qos_node_memory_low_bytes 2.147483648e+09
内核版本检查
在版本低于 5.9 的内核上,memory.high 节流可能触发
内核活锁
问题。这个缺陷已在内核 5.9 中修复。
在 v1.36 中,当启用该特性门控时,kubelet 会在启动时检查内核版本,
如果内核版本低于 5.9 就记录一条告警日志。
该特性仍然可以继续工作,这只是信息提示,并不是硬阻断。
Kubernetes 如何将 Memory QoS 映射到 cgroup v2
Memory QoS 使用四个 cgroup v2 内存控制器接口:
memory.max:硬内存限制,与之前版本一致memory.min:硬内存保护,在TieredReservation下仅对 Guaranteed Pod 设置memory.low:软内存保护,在TieredReservation下对 Burstable Pod 设置memory.high:内存节流阈值,与之前版本一致
下表展示了在配置 memoryReservationPolicy: TieredReservation 时,
Kubernetes 容器资源如何映射到 cgroup v2 接口。
对于默认值 memoryReservationPolicy: None,不会设置 memory.min 或 memory.low。
| QoS 类 | memory.min | memory.low | memory.high | memory.max |
|---|---|---|---|---|
| Guaranteed | 设置为 requests.memory(硬保护) | 不设置 | 不设置 (requests == limits,因此节流没有意义) | 设置为 limits.memory |
| Burstable | 不设置 | 设置为 requests.memory(软保护) | 基于节流因子公式计算 | 设置为 limits.memory(如果已指定) |
| BestEffort | 不设置 | 不设置 | 基于节点可分配内存计算 | 不设置 |
cgroup 层级
cgroup v2 要求父 cgroup 的内存保护值至少要与其所有子 cgroup 之和一样大。
kubelet 通过以下方式维持这一点:
将 kubepods 根 cgroup 上的 memory.min
设置为所有 Guaranteed 和 Burstable Pod 内存请求之和,
并将 Burstable QoS cgroup 上的 memory.low
设置为所有 Burstable Pod 内存请求之和。
这样,内核就能够正确执行容器级和 Pod 级的保护值。
kubelet 直接使用 runc 的 libcontainer 库管理 Pod 级别和 QoS 类级别的 cgroup, 而容器级别的 cgroup 则由容器运行时(containerd 或 CRI-O)管理。
如何使用?
前提条件
- Kubernetes v1.36 或更高版本
- 使用 cgroup v2 的 Linux。推荐使用 5.9 或更高版本的内核;
更早版本的内核也能工作,但可能会遇到活锁问题。
你可以运行
mount | grep cgroup2来验证 cgroup v2 是否已启用。 - 支持 cgroup v2 的容器运行时(containerd 1.6+、CRI-O 1.22+)
配置
要启用带分层保护的 Memory QoS:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
MemoryQoS: true
memoryReservationPolicy: TieredReservation # 可选值:None(默认)、TieredReservation
memoryThrottlingFactor: 0.9 # 可选,默认值为 0.9
如果你只想启用 memory.high 节流,而不启用内存保护,
可以省略 memoryReservationPolicy,或者将其设置为 None:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
MemoryQoS: true
memoryReservationPolicy: None # 默认值
如何进一步了解?
参与其中
此特性由 SIG Node 推动。
如果你有兴趣参与贡献或提供反馈,
可以通过 Slack(#sig-node)、
邮件列表
或定期举行的
SIG Node 会议
找到我们。
请将缺陷报告提交到 kubernetes/kubernetes,
并将增强提案提交到 kubernetes/enhancements。