我正在尝试了解内存请求和限制如何与 cgroup v2 配合使用。在 Kubernetes 清单中,我们可以配置内存请求和内存限制。然后使用这些值来配置 cgroup 接口:
- memory.min 设置为内存请求
- memory.max 设置为内存限制
- memory.high 设置为内存限制 * 0.8,除非内存请求 == 限制,在这种情况下 memory.high 保持未设置状态
- memory.low 始终未设置
memory.max 的含义非常明显:当 cgroup 中的进程尝试分配页面,这会使内存使用量超过 memory.max,并且无法从 cgroup 中回收足够的页面来满足 memory.max 内的请求时,则会调用 OOM killer 来终止 cgroup 中的进程。memory.high 更难理解:内核文档说,当达到高水位时,cgroup 会面临“高回收压力”,但这究竟意味着什么?
后来又说:
当达到上限时,它会通过强制直接回收来限制分配,以消除多余的部分,但它永远不会调用 OOM 杀手。
我是否正确地假设这意味着当 cgroup 尝试分配超出 memory.high 水位线的页面时,它将同步查看 lruvecs 并尝试从列表末尾回收尽可能多的页面,直到它回到高水位线以下?或者“回收压力”是异步发生的(通过 kswapd)?
问题 2:在 Kubernetes 上使用 memory.high 有什么意义?据我所知,Kubernetes 节点通常在没有交换空间的情况下运行。唯一可回收的页面是匿名页面(如果有足够的交换空间)和页面缓存。由于没有交换空间,因此只剩下页面缓存。问题是,在达到 memory.max 时也会回收页面缓存,如果无法回收任何内容,则在最后调用 OOM 终止程序。那么 memory.high 基本上是无用的:
只要使用页面缓存,它总是可以被回收,memory.max 也会这样做。使用 memory.high,我们只是在必要之前限制应用程序。不妨首先将 memory.max 设置得更低一些。
如果没有使用显著的页面缓存(目前运行 Kubernetes 的大多数应用程序可能都是这种情况),那么就无法回收任何内容,因此没有节流(没有分页未使用的匿名内存,压力失速信息中没有可见的抖动来警告我们),我们将毫无察觉地遇到 memory.max。使用 memory.high 没有任何效果。
我认为它不会立即进入直接回收(您称之为同步),但我不确定。根据我的经验,它最终会进入直接回收内存。高需求被过度拉伸。无论如何,它肯定会增加内存压力。
在没有交换空间的情况下运行通常是愚蠢的,而且长期以来都是如此。但无论如何,唯一可回收的页面确实大部分都在页面缓存中。不过,还有其他策略可能会发生。
但收获甚微。
总的来说,当没有交换来驱逐匿名页面时,您的观察也与我的现实相符 - MemoryHigh 会使抖动变得更糟,因为您只是将页面缓存保持在绝对最小值并最终进行大量 IO。
我们也在 LXD/LXC 实例上将其关闭,因为它会导致不必要的抖动(这是代码中的硬性限制,我们必须稍后再回去“修复”)。
然而,MemoryLow 可以作为一种有用的软预留机制,告诉内核“不要从这个控制组中抢夺低于这个内存范围的页面,选择另一个受害者”。