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