为什么内存受限的 LXC 容器中的应用程序将大文件写入磁盘会被 OOM 杀死?

为什么内存受限的 LXC 容器中的应用程序将大文件写入磁盘会被 OOM 杀死?

EDIT2:这个问题似乎也存在于 3.8.0-25-generic #37-Ubuntu SMP 下

编辑:我修改了原始标题中的问题“为什么使用 dd 写入文件会触发 Linux 内存不足管理器?”,以更好地反映我担心​​下面描述的一般问题:

我遇到了一个麻烦的情况,当我写入一个大小超过内存限制(设置为 300MB)的文件时,OOM 终止程序很难终止我的 LXC 容器中的进程。当我在实际上只有 512 MB RAM 的 Xen 虚拟机(EC2 t1.micro)上运行应用程序时,不会出现此问题,因此文件缓冲似乎存在一些问题,不符合容器的内存限制。

作为一个简单的例子,我可以演示 dd 写入的大文件将如何导致问题。同样,这个问题困扰着所有应用程序。我正在寻找解决应用程序缓存过大的一般问题的方法;我知道如何让“dd”工作。

设想:

我有一个 LXC 容器,其中 memory.limit_in_bytes 设置为 300 MB。

我尝试添加一个~500 MB 的文件,如下所示:

dd if=/dev/zero of=test2 bs=100k count=5010

大约 20% 的时间里,Linux OOM 管理器会由此命令触发并终止一个进程。毋庸置疑,这是非常意外的行为;dd 旨在模拟容器内运行的程序实际写入“有用”的文件。

详细信息:虽然文件缓存变大(260 MB),但 rss 和文件映射似乎保持相当低。以下是写入过程中 memory.stat 的示例:

cache 278667264
rss 20971520
mapped_file 24576
pgpgin 138147
pgpgout 64993
swap 0
pgfault 55054
pgmajfault 2
inactive_anon 10637312
active_anon 10342400
inactive_file 278339584
active_file 319488
unevictable 0
hierarchical_memory_limit 300003328
hierarchical_memsw_limit 300003328
total_cache 278667264
total_rss 20971520
total_mapped_file 24576
total_pgpgin 138147
total_pgpgout 64993
total_swap 0
total_pgfault 55054
total_pgmajfault 2
total_inactive_anon 10637312
total_active_anon 10342400
total_inactive_file 278339584
total_active_file 319488
total_unevictable 0

这是来自 dmesg 的粘贴,其中 OOM 触发了终止。我不太熟悉内存类型之间的区别;有一点很突出,那就是虽然“节点 0 正常”非常低,但有大量节点 0 DMA32 内存可用。有人能解释为什么文件写入会导致 OOM 吗?我该如何防止这种情况发生?

日志:

[1801523.686755] Task in /lxc/c-7 killed as a result of limit of /lxc/c-7
[1801523.686758] memory: usage 292972kB, limit 292972kB, failcnt 39580
[1801523.686760] memory+swap: usage 292972kB, limit 292972kB, failcnt 0
[1801523.686762] Mem-Info:
[1801523.686764] Node 0 DMA per-cpu:
[1801523.686767] CPU    0: hi:    0, btch:   1 usd:   0
[1801523.686769] CPU    1: hi:    0, btch:   1 usd:   0
[1801523.686771] CPU    2: hi:    0, btch:   1 usd:   0
[1801523.686773] CPU    3: hi:    0, btch:   1 usd:   0
[1801523.686775] CPU    4: hi:    0, btch:   1 usd:   0
[1801523.686778] CPU    5: hi:    0, btch:   1 usd:   0
[1801523.686780] CPU    6: hi:    0, btch:   1 usd:   0
[1801523.686782] CPU    7: hi:    0, btch:   1 usd:   0
[1801523.686783] Node 0 DMA32 per-cpu:
[1801523.686786] CPU    0: hi:  186, btch:  31 usd: 158
[1801523.686788] CPU    1: hi:  186, btch:  31 usd: 114
[1801523.686790] CPU    2: hi:  186, btch:  31 usd: 133
[1801523.686792] CPU    3: hi:  186, btch:  31 usd:  69
[1801523.686794] CPU    4: hi:  186, btch:  31 usd:  70
[1801523.686796] CPU    5: hi:  186, btch:  31 usd: 131
[1801523.686798] CPU    6: hi:  186, btch:  31 usd: 169
[1801523.686800] CPU    7: hi:  186, btch:  31 usd:  30
[1801523.686802] Node 0 Normal per-cpu:
[1801523.686804] CPU    0: hi:  186, btch:  31 usd: 162
[1801523.686806] CPU    1: hi:  186, btch:  31 usd: 184
[1801523.686809] CPU    2: hi:  186, btch:  31 usd:  99
[1801523.686811] CPU    3: hi:  186, btch:  31 usd:  82
[1801523.686813] CPU    4: hi:  186, btch:  31 usd:  90
[1801523.686815] CPU    5: hi:  186, btch:  31 usd:  99
[1801523.686817] CPU    6: hi:  186, btch:  31 usd: 157
[1801523.686819] CPU    7: hi:  186, btch:  31 usd: 138
[1801523.686824] active_anon:60439 inactive_anon:28841 isolated_anon:0
[1801523.686825]  active_file:110417 inactive_file:907078 isolated_file:64
[1801523.686827]  unevictable:0 dirty:164722 writeback:1652 unstable:0
[1801523.686828]  free:445909 slab_reclaimable:176594
slab_unreclaimable:14754
[1801523.686829]  mapped:4753 shmem:66 pagetables:3600 bounce:0
[1801523.686831] Node 0 DMA free:7904kB min:8kB low:8kB high:12kB
active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB
unevictable:0kB isolated(anon):0kB isolated(file):0kB present:7648kB
mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB
slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB
unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0
all_unreclaimable? no
[1801523.686841] lowmem_reserve[]: 0 4016 7048 7048
[1801523.686845] Node 0 DMA32 free:1770072kB min:6116kB low:7644kB
high:9172kB active_anon:22312kB inactive_anon:12128kB active_file:4988kB
inactive_file:2190136kB unevictable:0kB isolated(anon):0kB
isolated(file):256kB present:4112640kB mlocked:0kB dirty:535072kB
writeback:6452kB mapped:4kB shmem:4kB slab_reclaimable:72888kB
slab_unreclaimable:1100kB kernel_stack:120kB pagetables:832kB unstable:0kB
bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
[1801523.686855] lowmem_reserve[]: 0 0 3031 3031
[1801523.686859] Node 0 Normal free:5660kB min:4616kB low:5768kB
high:6924kB active_anon:219444kB inactive_anon:103236kB
active_file:436680kB inactive_file:1438176kB unevictable:0kB
isolated(anon):0kB isolated(file):0kB present:3104640kB mlocked:0kB
dirty:123816kB writeback:156kB mapped:19008kB shmem:260kB
slab_reclaimable:633488kB slab_unreclaimable:57916kB kernel_stack:2800kB
pagetables:13568kB unstable:0kB bounce:0kB writeback_tmp:0kB
pages_scanned:0 all_unreclaimable? no
[1801523.686869] lowmem_reserve[]: 0 0 0 0
[1801523.686873] Node 0 DMA: 2*4kB 3*8kB 0*16kB 2*32kB 4*64kB 3*128kB
2*256kB 1*512kB 2*1024kB 2*2048kB 0*4096kB = 7904kB
[1801523.686883] Node 0 DMA32: 129*4kB 87*8kB 86*16kB 89*32kB 87*64kB
65*128kB 12*256kB 5*512kB 2*1024kB 13*2048kB 419*4096kB = 1769852kB
[1801523.686893] Node 0 Normal: 477*4kB 23*8kB 1*16kB 5*32kB 0*64kB 3*128kB
3*256kB 1*512kB 0*1024kB 1*2048kB 0*4096kB = 5980kB
[1801523.686903] 1017542 total pagecache pages
[1801523.686905] 0 pages in swap cache
[1801523.686907] Swap cache stats: add 0, delete 0, find 0/0
[1801523.686908] Free swap  = 1048572kB
[1801523.686910] Total swap = 1048572kB
[1801523.722319] 1837040 pages RAM
[1801523.722322] 58337 pages reserved
[1801523.722323] 972948 pages shared
[1801523.722324] 406948 pages non-shared
[1801523.722326] [ pid ]   uid  tgid total_vm      rss cpu oom_adj
oom_score_adj name
[1801523.722396] [31266]     0 31266     6404      511   6       0
    0 init
[1801523.722445] [32489]     0 32489    12370      688   7     -17
-1000 sshd
[1801523.722460] [32511]   101 32511    10513      325   0       0
    0 rsyslogd
[1801523.722495] [32625]     0 32625    17706      838   2       0
    0 sshd
[1801523.722522] [32652]   103 32652     5900      176   0       0
    0 dbus-daemon
[1801523.722583] [  526]     0   526     1553      168   5       0
    0 getty
[1801523.722587] [  530]     0   530     1553      168   1       0
    0 getty
[1801523.722593] [  537]  2007   537    17706      423   5       0
    0 sshd
[1801523.722629] [  538]  2007   538    16974     5191   1       0
    0 python
[1801523.722650] [  877]  2007   877     2106      157   7       0
    0 dd
[1801523.722657] Memory cgroup out of memory: Kill process 538 (python)
score 71 or sacrifice child
[1801523.722674] Killed process 538 (python) total-vm:67896kB,
anon-rss:17464kB, file-rss:3300kB

我在 Amazon EC2 上的 Linux ip-10-8-139-98 3.2.0-29-virtual #46-Ubuntu SMP Fri Jul 27 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 上运行。

答案1

编辑:我会保留我原来的答案,但我会尝试解释这里发生的事情并为您提供一个通用的解决方案。

编辑 2:提供了另一种选择。

您在这里遇到的问题与内核如何管理 I/O 有关。当您对文件系统进行写入时,该写入不会立即提交到磁盘;这将是非常低效的。相反,写入缓存在称为页面缓存的内存区域中,并定期以块的形式写入磁盘。日志的“脏”部分描述了尚未写入磁盘的此页面缓存的大小:

dirty:123816kB

那么什么会清空这个脏缓存呢?为什么它没有完成它的工作?

Linux 上的“Flush”负责将脏页写入磁盘。它是一个守护进程,会定期唤醒以确定是否需要写入磁盘,如果需要,则执行写入。如果您是 C 语言爱好者,请开始这里。Flush 非常高效;在需要时,它可以很好地将内容刷新到磁盘。而且它的工作方式与预期完全一致。

冲洗运行外部您的 LXC 容器,因为您的 LXC 容器没有自己的内核。LXC 容器作为围绕以下结构而存在:cgroups,这是 Linux 内核的一个特性,可以更好地限制和隔离进程组,但不能限制和隔离其自身的内核或刷新守护进程。

由于 LXC 的内存限制低于内核可用的内存,因此会发生奇怪的事情。Flush 假设它拥有主机的全部内存来缓存写入。LXC 中的程序开始写入一个大文件,它会缓冲...缓冲...最终达到其硬限制,并开始调用 OOM 管理器。这不是任何特定组件的故障;这是预期的行为。有点。 这种事情应该由 cgroups 来处理,但是看起来似乎并非如此。

这完全解释了您在实例大小之间看到的行为。与大型实例相比,您在微型实例(具有 512MB RAM)上开始刷新到磁盘的速度要快得多

好吧,这很有道理。但这没用。我还需要写一个大文件。

好吧,flush 不知道你的 LXC 限制。因此,除了修补内核之外,你还可以尝试以下几个选项来调整:

/proc/sys/vm/dirty_expire_centiseconds

这控制页面在脏缓存中保留并写入磁盘的时间。默认情况下为 30 秒;尝试将其设置得更低以开始更快地将其推出。

/proc/sys/vm/dirty_background_ratio

这控制在开始强制写入之前允许填充多少百分比的活动内存刷新。在整理准确总数这里,但最简单的解释是只看你的总内存。默认情况下是 10%(在某些发行版中是 5%)。将其设置得较低;它将强制更快地写入磁盘,并可能防止你的 LXC 超出其限制。

我不能稍微改动一下文件系统吗?

嗯,是的。但一定要测试一下……这可能会影响性能。在您要写入此内容的 /etc/fstab 中的挂载上,添加“同步' 安装选项。

原始答案:

尝试减少 DD 使用的块大小:

dd if=/dev/zero of=test2 bs=512 count=1024000

您一次只能写入一个扇区(旧硬盘上为 512 字节,新硬盘上为 4096 字节)。如果 DD 将写入推送到磁盘的速度快于磁盘接受的速度,它将开始将写入缓存在内存中。这就是文件缓存不断增长的原因。

答案2

您的文件是否正在写入 /tmp?如果是,则它可能不在实际的文件系统上,而是驻留在磁盘上。因此,当您写入文件时,会占用越来越多的内存来满足文件的需求。最终,您会耗尽内存和交换空间,并且性能会下降到令人极度沮丧的地步。

答案3

除非您正在写入 RAM 磁盘,否则可以使用 oflag=direct 来避免缓存

dd if=/dev/zero of=test2 bs=100k oflag=direct count=5010

相关内容