Mengapa aplikasi dalam wadah LXC terbatas memori menulis file besar ke disk dibunuh oleh OOM?

10

EDIT2: Masalah ini tampaknya ada juga di bawah 3.8.0-25-generik # 37-Ubuntu SMP

EDIT: Saya mengubah pertanyaan dari judul asli "Mengapa manajer Linux Out of Memory dipicu dengan menulis ke file dengan dd?" untuk lebih mencerminkan bahwa saya khawatir tentang masalah umum yang dijelaskan di bawah ini:

Saya mengalami skenario yang merepotkan di mana pembunuh OOM adalah proses pembunuhan yang sulit dalam wadah LXC saya ketika saya menulis file dengan ukuran melebihi batasan memori (diatur ke 300MB). Masalahnya tidak terjadi ketika saya menjalankan aplikasi pada mesin virtual Xen (sebuah EC2 t1.micro) yang sebenarnya hanya memiliki RAM 512 MB, sehingga tampaknya ada beberapa masalah dengan buffering file sehubungan dengan batas memori kontainer.

Sebagai contoh sederhana, saya dapat menunjukkan bagaimana file besar yang ditulis oleh dd akan menyebabkan masalah. Sekali lagi, masalah ini mengganggu semua aplikasi. Saya ingin menyelesaikan masalah umum cache aplikasi menjadi terlalu besar; Saya mengerti bagaimana saya bisa membuat "dd" bekerja.

Skenario:

Saya memiliki wadah LXC di mana memory.limit_in_bytes diatur ke 300 MB.

Saya mencoba menemukan ~ 500 MB file sebagai berikut:

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

Sekitar 20% dari waktu, manajer OOM Linux dipicu oleh perintah ini dan sebuah proses terbunuh. Tak perlu dikatakan, ini adalah perilaku yang sangat tidak disengaja; dd dimaksudkan untuk mensimulasikan penulisan file "berguna" yang sebenarnya oleh program yang berjalan di dalam container.

Detail: Sementara cache file menjadi besar (260 MB), rss dan file map tampaknya tetap rendah. Berikut adalah contoh dari apa yang memory.stat mungkin terlihat selama penulisan:

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

Ini pasta dari dmesg tempat OOM memicu pembunuhan. Saya tidak terlalu akrab dengan perbedaan antara jenis-jenis memori; satu hal yang menonjol adalah bahwa sementara "Node 0 Normal" sangat rendah, ada banyak memori Node 0 DMA32. Adakah yang bisa menjelaskan mengapa menulis file menyebabkan OOM? Bagaimana saya mencegah hal ini terjadi?

Log:

[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

Saya menjalankan Linux ip-10-8-139-98 3.2.0-29-virtual # 46-Ubuntu SMP Jumat 27 Jul 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux di Amazon EC2.

UsAaR33
sumber
1
Sebagai ringkasan singkat untuk semua yang membacanya, ini adalah bug kernel linux
UsAaR33

Jawaban:

13

Sunting: Saya akan menyimpan jawaban asli saya di bawah, tetapi saya akan mencoba menjelaskan apa yang terjadi di sini dan memberikan solusi umum untuk Anda.

Sunting 2: Diberikan pilihan lain.

Masalah yang Anda alami di sini berkaitan dengan bagaimana kernel mengelola I / O. Ketika Anda menulis ke sistem file Anda, penulisan itu tidak langsung dilakukan ke disk; itu akan sangat tidak efisien. Sebaliknya, menulis di-cache dalam area memori yang disebut sebagai cache halaman, dan secara berkala ditulis dalam potongan ke disk. Bagian "kotor" log Anda menjelaskan ukuran cache halaman ini yang belum ditulis ke disk:

dirty:123816kB

Jadi apa yang mengosongkan cache kotor ini? Kenapa tidak melakukan itu pekerjaan?

'Flush' di Linux bertanggung jawab untuk menulis halaman kotor ke disk. Ini adalah daemon yang bangun secara berkala untuk menentukan apakah menulis ke disk diperlukan, dan, jika demikian, lakukanlah. Jika Anda tipe pria C, mulailah dari sini . Flush sangat efisien; itu pekerjaan yang bagus untuk membuang barang ke disk saat dibutuhkan. Dan itu bekerja persis seperti yang seharusnya.

Flush berjalan di luar wadah LXC Anda, karena wadah LXC Anda tidak memiliki kernel sendiri. Wadah LXC ada sebagai konstruk di sekitar cgroup , yang merupakan fitur dari kernel Linux yang memungkinkan pembatasan dan isolasi kelompok proses yang lebih baik, tetapi bukan kernel atau flush daemonnya sendiri.

Karena LXC Anda memiliki batas memori yang lebih rendah dari memori yang disediakan kernel, hal-hal aneh terjadi. Flush mengasumsikan ia memiliki memori penuh dari host untuk melakukan cache. Program di LXC Anda mulai menulis file besar, buffer ... buffer ... dan akhirnya mencapai batas yang sulit, dan mulai memanggil manajer OOM. Ini bukan kegagalan komponen tertentu; itu perilaku yang diharapkan. Agak. Hal semacam ini harus ditangani oleh cgroup, tetapi sepertinya tidak.

Ini sepenuhnya menjelaskan perilaku yang Anda lihat antara ukuran instance. Anda akan mulai membilas ke disk lebih cepat pada instance mikro (dengan RAM 512MB) vs pada instance besar

Oke, itu masuk akal. Tapi itu tidak berguna. Saya masih perlu menulis file besar untuk saya.

Nah, flush tidak mengetahui batas LXC Anda. Jadi, alih-alih menambal kernel, ada beberapa opsi di sini untuk hal-hal yang dapat Anda coba ubah:

/proc/sys/vm/dirty_expire_centiseconds

Ini mengontrol berapa lama halaman dapat disimpan dalam cache kotor dan ditulis ke disk. Secara default 30 detik; coba atur lebih rendah untuk mulai mendorongnya lebih cepat.

/proc/sys/vm/dirty_background_ratio

Ini mengontrol berapa persentase memori aktif yang boleh diisi sebelum mulai memaksa menulis. Ada sedikit mengutak-atik yang masuk ke memilah total yang tepat di sini, tetapi penjelasan termudah adalah dengan hanya melihat total memori Anda. Secara default 10% (pada beberapa distro 5%). Atur ini lebih rendah; itu akan memaksa menulis ke disk lebih cepat dan dapat menjaga LXC Anda dari kehabisan batas itu.

Tidak bisakah saya sedikit mengacaukan sistem file?

Yah begitulah. Tapi pastikan Anda menguji ini .. Anda bisa memengaruhi kinerja. Pada mount Anda di / etc / fstab tempat Anda akan menulis ini, tambahkan opsi mount ' sync '.

Jawaban asli:

Coba kurangi ukuran blok yang digunakan oleh DD:

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

Anda hanya dapat menulis satu sektor pada satu waktu (512 byte pada HDD lama, 4096 pada yang lebih baru). Jika DD mendorong menulis ke disk lebih cepat daripada yang dapat diterima oleh disk, itu akan mulai membuat cache tulis dalam memori. Itu sebabnya cache file Anda berkembang.

alexphilipp
sumber
Saya harus mencatat bahwa jika saya menjalankan tes serupa di python di mana saya secara manual menyiram objek file, kesalahan masih terjadi dengan probabilitas yang sama. Cache tumbuh tentu saja, tetapi yang seharusnya dibersihkan seseorang akan berpikir daripada proses dibunuh.
UsAaR33
1
Saya akan mencobanya. Saya menemukan memaksa fsync () dengan Python tidak selalu melakukan apa yang Anda harapkan.
alexphilipp
1
@ UsAaR33 Dapatkan disk lebih cepat.
tink
1
@ UsAaR33 Suatu aplikasi akan menulis secepat mungkin; ia mengharapkan kernel untuk menangani IO. Saya belum pernah menggunakan wadah LXC sebelumnya, tetapi dari pandangan sepintas sepertinya tidak menyediakan kernel itu sendiri di chroot yang dibuatnya? Jika itu yang terjadi, kernel menyediakan IO dengan asumsi bahwa ia memiliki memori penuh dari sistem host yang tersedia. Tidak tahu bahwa Anda menilai terbatas hingga 300MB. Setelah batas itu tercapai, OOM mulai membunuh proses.
alexphilipp
1
@ UsAaR33: Pengaturan yang buruk menyebabkan hasil yang buruk. Salah satu bagian dari sistem dikatakan banyak memori dapat digunakan sebagai cache, bagian lain dari sistem diberitahu untuk mematikan proses jika cache terlalu besar. Mengapa harus menunggu disk ketika ada banyak RAM yang tersedia? Dan jika ada banyak RAM yang tersedia, mengapa tidak membiarkannya menggunakannya?
David Schwartz
3

Apakah file Anda menulis ke / tmp? Jika demikian, itu mungkin bukan pada sistem file yang sebenarnya tetapi tetap pada disk. Dengan demikian saat Anda menulis kepadanya, semakin banyak memori yang diambil untuk memenuhi kebutuhan file. Akhirnya, Anda kehabisan memori + ruang swap dan kinerja Anda memburuk ke titik frustrasi total.

mdpc
sumber
Itu menulis ke $ HOME, yang ada di mount AUFS yang memicu menulis ke disk yang mendasarinya. (EC2 EBS)
UsAaR33
2

kecuali Anda menulis ke disk RAM, Anda dapat menghindari caching dengan menggunakan oflag = direct

dd if=/dev/zero of=test2 bs=100k oflag=direct count=5010
Kevin Parker
sumber
langsung menyebabkan kesalahan "Argumen tidak valid", tetapi menggunakan oflag = dsync berfungsi.
UsAaR33
saya minta maaf jika itu tidak berhasil untuk Anda, sesuai halaman manual "langsung menggunakan I / O langsung untuk data"
Kevin Parker