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


10

EDIT2:此问题在3.8.0-25-通用#37-Ubuntu SMP下似乎也存在

编辑:我修改了原始标题为“为什么用dd写入文件会触发Linux内存不足管理器”的问题?为了更好地反映出我对以下所述的一般问题感到担忧:

我遇到一个麻烦的情况,当我写一个大小超过内存限制(设置为300MB)的文件时,OOM杀手正在我的LXC容器中强行杀死进程。当我在实际上只有512 MB RAM的Xen虚拟机(EC2 t1.micro)上运行应用程序时,不会发生此问题,因此在尊重容器内存限制的情况下文件缓冲似乎存在一些问题。

作为一个简单的示例,我可以演示dd写入的大文件将如何引起问题。同样,此问题困扰着所有应用程序。我正在寻找解决应用程序缓存过大的一般问题。我了解如何使“ dd”工作。

场景:

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

我尝试将dd添加到约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

我正在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 Amazon EC2上的GNU / Linux


1
作为对所有阅读它的人的简要总结,这是一个Linux内核错误
UsAaR33 2013年

Answers:


13

编辑:我将在下面保留原始答案,但我将尝试解释此处发生的情况并为您提供一般的解决方案。

编辑2:提供了另一个选项。

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

dirty:123816kB

那么什么清空了这个肮脏的缓存呢?为什么不做它的工作?

Linux上的“刷新”负责将脏页写到磁盘上。它是一个守护程序,会定期唤醒以确定是否需要对磁盘进行写操作,如果需要,则执行写操作。如果您是C型人,请从此处开始。冲洗效率极高。在需要时,它可以很好地将内容刷新到磁盘。它正在按预期运行。

Flush在LXC容器之外运行,因为LXC容器没有自己的内核。LXC容器以cgroups周围的构造形式存在,这是Linux内核的功能,可以更好地限制和隔离进程组,但不能提供其自己的内核或刷新守护程序。

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

这完全解释了您在实例大小之间看到的行为。与大型实例相比,您将在微型实例(具有512MB RAM)上更快地刷新磁盘。

好的,那很有道理。但这没用。我仍然需要写一个大文件。

好吧,同花顺不知道您的LXC限制。因此,这里不提供修补内核的方法,而是提供一些选项供您尝试调整:

/proc/sys/vm/dirty_expire_centiseconds

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

/proc/sys/vm/dirty_background_ratio

这可以控制在开始强制写入之前允许填满活动内存刷新的百分比。在这里整理出确切的总数有一点麻烦,但是最简单的解释是只看一下您的总内存。默认情况下为10%(在某些发行版中为5%)。降低这个值;它会强制将其更快地写出到磁盘,并可能使您的LXC不受限制。

我能不能稍微破坏一下文件系统?

是的,是的。但是请确保您对此进行了测试。..这可能会影响性能。在您将要写入此内容的/ etc / fstab中的挂载上,添加' sync '挂载选项。

原始答案:

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

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

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


我应该注意,如果我在python中手动刷新文件对象时运行了类似的测试,则错误发生的可能性仍然相似。缓存当然会增长,但是应该清除掉它,而不是杀死进程。
UsAaR33 2013年

1
无论如何我都会试一试。我发现用Python强制执行fsync()并不总是能达到您的期望。
alexphilipp

1
@ UsAaR33获取更快的磁盘。
2013年

1
@ UsAaR33应用程序将尽快写入。它期望内核处理IO。我以前没有使用过LXC容器,但是从粗略的浏览看,它似乎没有在它创建的chroot中提供它自己的内核?在这种情况下,内核将向IO提供一个假设,即它具有可用主机系统的全部内存。完全不知道您将速率限制为300MB。一旦达到该限制,OOM将开始终止进程​​。
alexphilipp

1
@ UsAaR33:错误的设置会导致错误的结果。告诉系统的一部分可以将大量内存用作高速缓存,如果高速缓存太大,则告诉系统的另一部分杀死进程。当有足够的可用RAM时,为什么还要等待磁盘?如果有足够的可用RAM,为什么不让它使用它呢?
大卫·史瓦兹

3

您的文件正在写入/ tmp吗?如果是这样,它可能不在实际的文件系统上,而是驻留在磁盘上。因此,当您对其进行写入时,会占用越来越多的内存来满足文件的需求。最终,您的内存+交换空间用完了,并且性能下降到了完全沮丧的地步。


它正在写入$ HOME,后者位于AUFS挂载上,触发对基础磁盘的写入。(EC2 EBS)
UsAaR33

2

除非您要写入RAM磁盘,否则可以使用oflag = direct避免进行缓存

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

direct会导致“无效参数”错误,但使用oflag = dsync确实可以。
UsAaR33 2013年

很抱歉,如果它对您不起作用,请参阅手册页“直接使用直接I / O进行数据处理”
Kevin Parker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.