本文发布已超过一年。较早的文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再准确。
Kubernetes 内存管理器进入 beta 阶段
这篇博文解释了 Kubernetes 1.22 的 Beta 特性—— 内存管理器 (Memory manager) 的一些内部细节。在 Kubernetes 中,内存管理器是 kubelet 的一个子组件。内存管理器为 Guaranteed
QoS 类 中的 Pod 提供有保障的内存(和 HugePages)分配。
这篇博文涵盖以下内容:
为什么你需要它?
一些 Kubernetes 工作负载运行在具有非一致性内存访问 (NUMA) 的节点上。如果你的集群中有 NUMA 节点,那么你会知道当计算资源需要访问不同 NUMA 区域的内存时,可能会产生额外的延迟。
为了获得最佳性能和最低延迟,容器的 CPU、外围设备和内存都应该对齐到相同的 NUMA 区域。在 Kubernetes v1.22 之前,kubelet 已经提供了一系列管理器来对齐 CPU 和 PCI 设备,但你没有办法对齐内存。Linux 内核可以尽力尝试为运行在相同 NUMA 节点的任务分配内存,但无法保证这种放置。
它是如何工作的?
内存管理器主要做两件事:
- 向 Topology Manager 提供拓扑提示 (topology hint)
- 为容器分配内存并更新状态
Memory Manager 在 Kubelet 下的总体执行顺序如下:
在准入阶段 (Admission phase):
- 首次处理新的 Pod 时,kubelet 调用 TopologyManager 的
Admit()
方法。 - Topology Manager 调用包括 Memory Manager 在内的所有提示提供者 (hint provider) 的
GetTopologyHints()
方法。 - Memory Manager 为 Pod 内的每个容器计算所有可能的 NUMA 节点组合,并将提示返回给 Topology Manager。
- Topology Manager 调用包括 Memory Manager 在内的所有提示提供者 的
Allocate()
方法。 - Memory Manager 根据 Topology Manager 选择的提示在状态 (state) 中分配内存。
在 Pod 创建阶段 (Pod creation):
- kubelet 调用
PreCreateContainer()
。 - 对于每个容器,Memory Manager 查找它为容器分配内存的 NUMA 节点,然后将该信息返回给 kubelet。
- kubelet 通过 CRI 使用包含来自 Memory Manager 信息在内的容器规约 (container specification) 创建容器。
配置说明
默认情况下,Memory Manager 使用 None
策略运行,这意味着它会闲置不执行任何操作。要使用 Memory Manager,你需要为 kubelet 设置两个命令行选项:
--memory-manager-policy=Static
--reserved-memory="<numaNodeID>:<resourceName>=<quantity>"
--memory-manager-policy
的值很直接:Static
。决定 --reserved-memory
要指定什么需要更多思考。要正确配置它,你应该遵循两个主要规则:
- 为
memory
资源保留的内存量必须大于零。 - 为该资源类型保留的内存量必须等于 NodeAllocatable(
kube-reserved + system-reserved + eviction-hard
)。你可以在为系统守护进程预留计算资源中阅读有关内存预留的更多信息。
当前局限性
1.22 版本的发布和晋升至 Beta 带来了一些增强和修复,但 Memory Manager 仍有一些局限性。
单一 NUMA 节点分配 vs 跨 NUMA 节点分配
一个 NUMA 节点不能同时存在单一节点分配和跨节点分配。当容器内存被固定到两个或更多 NUMA 节点时,我们无法知道容器将从哪个 NUMA 节点消耗内存。
container1
在 NUMA 节点 0 上启动,请求 5Gi 内存,但目前仅消耗 3Gi 内存。container2
的内存请求是 10Gi,并且没有单一 NUMA 节点可以满足它。container2
从 NUMA 节点 0 消耗 3.5Gi 内存,但是一旦container1
需要更多内存,它将无法获得,并且内核将终止其中一个容器并报 OOM 错误。
为了防止此类问题,在机器拥有两个没有单一 NUMA 节点分配的 NUMA 节点之前,Memory Manager 将拒绝 container2
的准入。
仅适用于 Guaranteed Pod
Memory Manager 不能保证 Burstable Pod 的内存分配,即使 Burstable Pod 指定了相等的内存限制和请求。
假设你有两个 Burstable Pod:pod1
包含内存请求和限制相等的容器,而 pod2
仅包含设置了内存请求的容器。你希望为 pod1
保证内存分配。对于 Linux 内核而言,这两个 Pod 中的进程具有相同的 OOM Score,一旦内核发现内存不足,它可能会终止属于 pod1
的进程。
内存碎片化
Pod 和容器的启动和停止顺序可能导致 NUMA 节点上的内存碎片化。Memory Manager 的 Alpha 实现没有任何机制来平衡 Pod 并将内存重新去碎片化。
Memory Manager 的未来工作
我们不想止步于 Memory Manager 的当前状态,并正在寻求改进,包括以下方面。
使 Memory Manager 分配算法更智能
当前算法在计算分配时忽略了 NUMA 节点之间的距离。如果无法实现同节点放置,我们仍然可以通过更改 Memory Manager 以优先选择最近的 NUMA 节点进行跨节点分配,从而提供比当前实现更好的性能。
减少准入错误数量
默认的 Kubernetes 调度器不感知节点的 NUMA 拓扑,这可能是 Pod 启动期间出现许多准入错误的原因。我们希望添加一个 KEP (Kubernetes Enhancement Proposal) 来涵盖这方面的改进。关注 kube-scheduler 中的拓扑感知调度器插件 以了解这个想法的进展。
结论
随着 Memory Manager 在 1.22 中晋升至 Beta,我们鼓励大家试用它,并期待你的任何反馈。虽然仍存在一些局限性,但我们计划了一系列增强功能来解决这些问题,并期待在即将发布的版本中为你提供许多新特性。如果你有其他增强功能的想法或对某些特性有需求,请告诉我们。团队始终欢迎建议,以增强和改进 Memory Manager。希望这篇博文对你有所帮助!如果你有任何问题或意见,请告诉我们。
你可以通过以下方式联系我们:
- Kubernetes Slack 中的 #sig-node 频道(如果你需要邀请,请访问 https://slack.k8s.io/)
- SIG Node 邮件列表,kubernetes-sig-node@googlegroups.com