Warum werden Anwendungen in einem speicherbeschränkten LXC-Container, die große Dateien auf die Festplatte schreiben, vom OOM beendet?

10

EDIT2: Dieses Problem scheint auch unter 3.8.0-25-generic # 37-Ubuntu SMP zu bestehen

BEARBEITEN: Ich habe die Frage aus dem Originaltitel "Warum sollte der Linux Out of Memory Manager durch Schreiben in eine Datei mit dd ausgelöst werden?" Geändert. um besser zu reflektieren, dass ich mir Sorgen um das unten beschriebene allgemeine Problem mache:

Ich stoße auf ein problematisches Szenario, in dem der OOM-Killer Prozesse in meinem LXC-Container hart beendet, wenn ich eine Datei schreibe, deren Größe die Speicherbeschränkung überschreitet (auf 300 MB festgelegt). Das Problem tritt nicht auf, wenn ich die Anwendung auf einer virtuellen Xen-Maschine (einem EC2 t1.micro) ausführe, die tatsächlich nur 512 MB RAM hat. Daher scheint es ein Problem mit der Dateipufferung zu geben, die das Speicherlimit des Containers einhält.

Als einfaches Beispiel kann ich zeigen, wie eine große Datei, die von dd geschrieben wurde, Probleme verursacht. Auch dieses Problem betrifft alle Anwendungen. Ich möchte das allgemeine Problem lösen, dass der Cache der Anwendung zu groß wird. Ich verstehe, wie ich das "dd" zum Laufen bringen kann.

Szenario:

Ich habe einen LXC-Container, in dem memory.limit_in_bytes auf 300 MB festgelegt ist.

Ich versuche, eine ~ 500 MB-Datei wie folgt zu erstellen:

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

In ungefähr 20% der Fälle wird der Linux-OOM-Manager durch diesen Befehl ausgelöst und ein Prozess wird abgebrochen. Es ist unnötig zu erwähnen, dass dies ein höchst unbeabsichtigtes Verhalten ist. dd soll eine tatsächliche "nützliche" Datei simulieren, die von einem Programm geschrieben wird, das im Container ausgeführt wird.

Details: Während Dateicaches groß werden (260 MB), scheinen RSS und Dateikarte ziemlich niedrig zu bleiben. Hier ist ein Beispiel dafür, wie memory.stat während des Schreibens aussehen kann:

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

Hier ist eine Paste aus dmesg, bei der der OOM einen Kill ausgelöst hat. Ich bin mit den Unterschieden zwischen den Arten des Gedächtnisses nicht allzu vertraut. Eine Sache, die auffällt, ist, dass "Node 0 Normal" zwar sehr niedrig ist, aber genügend Node 0 DMA32-Speicher frei ist. Kann jemand erklären, warum ein Dateischreiben das OOM verursacht? Wie verhindere ich dies?

Das Protokoll:

[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

Ich verwende Linux ip-10-8-139-98 3.2.0-29-virtual # 46-Ubuntu SMP Fr 27 Jul 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux unter Amazon EC2.

UsAaR33
quelle
1
Als
kurze

Antworten:

13

Bearbeiten: Ich werde meine ursprüngliche Antwort unten behalten, aber ich werde versuchen zu erklären, was hier passiert, und eine allgemeine Lösung für Sie bereitstellen.

Bearbeiten 2: Eine weitere Option bereitgestellt.

Das Problem, auf das Sie hier stoßen, hängt damit zusammen, wie der Kernel E / A verwaltet. Wenn Sie in Ihr Dateisystem schreiben, wird dieser Schreibvorgang nicht sofort auf die Festplatte übertragen. das wäre unglaublich ineffizient. Stattdessen werden Schreibvorgänge in einem Speicherbereich zwischengespeichert, der als Seitencache bezeichnet wird, und regelmäßig in Blöcken auf die Festplatte geschrieben. Der Abschnitt "Dirty" Ihres Protokolls beschreibt die Größe dieses Seitencaches, der noch nicht auf die Festplatte geschrieben wurde:

dirty:123816kB

Was leert diesen schmutzigen Cache? Warum macht es nicht seinen Job?

'Flush' unter Linux ist dafür verantwortlich, schmutzige Seiten auf die Festplatte zu schreiben. Es ist ein Daemon, der regelmäßig aktiviert wird, um festzustellen, ob Schreibvorgänge auf die Festplatte erforderlich sind, und diese in diesem Fall ausführt. Wenn Sie ein C-Typ sind, fangen Sie hier an . Flush ist unglaublich effizient; Es macht einen großartigen Job, wenn es darum geht, Dinge bei Bedarf auf die Festplatte zu spülen. Und es funktioniert genau so, wie es soll.

Flush wird außerhalb Ihres LXC-Containers ausgeführt, da Ihr LXC-Container keinen eigenen Kernel hat. LXC-Container existieren als Konstrukt um cgroups , eine Funktion des Linux-Kernels, die bessere Einschränkungen und Isolation von Prozessgruppen ermöglicht, jedoch nicht den eigenen Kernel oder Flush-Daemon.

Da Ihr LXC ein niedrigeres Speicherlimit hat als der vom Kernel verfügbare Speicher, passieren seltsame Dinge. Flush geht davon aus, dass es den vollen Speicher des Hosts zum Zwischenspeichern von Schreibvorgängen hat. Ein Programm in Ihrem LXC beginnt, eine große Datei zu schreiben, puffert ... puffert ... und erreicht schließlich das harte Limit und ruft den OOM-Manager auf. Dies ist kein Fehler einer bestimmten Komponente. es ist erwartetes Verhalten. So'ne Art. Diese Art von Dingen sollte von cgroups gehandhabt werden, aber es scheint nicht so zu sein.

Dies erklärt das Verhalten zwischen den Instanzgrößen vollständig. Sie werden auf der Mikroinstanz (mit 512 MB RAM) viel früher als auf einer großen Instanz mit dem Flushing auf die Festplatte beginnen

Ok, das macht Sinn. Aber es ist nutzlos. Ich muss mir noch eine großartige Datei schreiben.

Nun, Flush kennt Ihr LXC-Limit nicht. Anstatt den Kernel zu patchen, gibt es hier einige Optionen für Dinge, die Sie optimieren können:

/proc/sys/vm/dirty_expire_centiseconds

Hiermit wird gesteuert, wie lange eine Seite im fehlerhaften Cache gehalten und auf die Festplatte geschrieben werden kann. Standardmäßig sind es 30 Sekunden. Versuchen Sie, es niedriger einzustellen, um es schneller herauszudrücken.

/proc/sys/vm/dirty_background_ratio

Hiermit wird gesteuert, wie viel Prozent der aktiven Speicherbereinigung gefüllt werden dürfen, bevor Schreibvorgänge erzwungen werden. Es gibt ein bisschen Fummelei, um die genaue Summe hier zu sortieren , aber die einfachste Erklärung ist, nur Ihren Gesamtspeicher zu betrachten. Standardmäßig sind es 10% (in einigen Distributionen 5%). Stellen Sie dies niedriger ein; Dadurch werden Schreibvorgänge früher auf die Festplatte erzwungen und Ihr LXC kann möglicherweise nicht an seine Grenzen stoßen.

Kann ich nicht einfach ein bisschen mit dem Dateisystem schrauben?

Gut ja. Aber stellen Sie sicher, dass Sie dies testen. Sie könnten die Leistung beeinträchtigen. Fügen Sie auf Ihren Mounts in / etc / fstab, in die Sie dies schreiben, die Mount-Option ' sync ' hinzu.

Ursprüngliche Antwort:

Versuchen Sie, die von DD verwendete Blockgröße zu reduzieren:

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

Sie können jeweils nur einen Sektor schreiben (512 Byte auf älteren Festplatten, 4096 auf neueren). Wenn DD Schreibvorgänge schneller auf die Festplatte überträgt, als die Festplatte sie akzeptieren kann, werden die Schreibvorgänge im Speicher zwischengespeichert. Deshalb wächst Ihr Datei-Cache.

alexphilipp
quelle
Ich sollte beachten, dass, wenn ich ähnliche Tests in Python durchführe, bei denen ich das Dateiobjekt manuell lösche, der Fehler immer noch mit ähnlicher Wahrscheinlichkeit auftritt. Der Cache wächst natürlich, aber das sollte gelöscht werden, würde man eher denken als den Prozess zu beenden.
UsAaR33
1
Ich würde es trotzdem versuchen. Ich habe festgestellt, dass das Erzwingen eines fsync () mit Python nicht immer das tut, was Sie erwarten.
Alexphilipp
1
@ UsAaR33 Holen Sie sich schnellere Festplatte.
Tink
1
@ UsAaR33 Eine Anwendung schreibt so schnell wie möglich. Es wird erwartet, dass der Kernel E / A verarbeitet. Ich habe noch nie einen LXC-Container verwendet, aber auf den ersten Blick sieht es so aus, als würde er keinen eigenen Kernel in der von ihm erstellten Chroot bereitstellen. In diesem Fall stellt der Kernel IO die Annahme zur Verfügung, dass der gesamte Speicher des Hostsystems verfügbar ist. Es hat keine Ahnung, dass Sie die Rate auf 300 MB beschränkt haben. Sobald dieses Limit erreicht ist, beginnt OOM, Prozesse zu beenden.
Alexanderph
1
@ UsAaR33: Schlechte Einstellungen führen zu schlechten Ergebnissen. Einem Teil des Systems wird mitgeteilt, dass viel Speicher als Cache verwendet werden kann. Ein anderer Teil des Systems wird angewiesen, Prozesse abzubrechen, wenn der Cache zu groß ist. Warum sollte es auf die Festplatte warten, wenn genügend RAM verfügbar ist? Und wenn genügend RAM verfügbar ist, lassen Sie es doch nutzen.
David Schwartz
3

Schreibt Ihre Datei in / tmp? In diesem Fall befindet es sich möglicherweise nicht in einem tatsächlichen Dateisystem, sondern befindet sich auf der Festplatte. Während Sie darauf schreiben, wird immer mehr Speicherplatz benötigt, um den Anforderungen der Datei gerecht zu werden. Schließlich geht Ihnen der Speicherplatz + der Swap-Speicherplatz aus und Ihre Leistung verschlechtert sich bis zur völligen Frustration.

mdpc
quelle
Es wird in $ HOME geschrieben, das sich auf einem AUFS-Mount befindet und Schreibvorgänge auf die zugrunde liegende Festplatte auslöst. (EC2 EBS)
UsAaR33
2

Wenn Sie nicht auf die RAM-Disk schreiben, können Sie das Caching mit oflag = direct vermeiden

dd if=/dev/zero of=test2 bs=100k oflag=direct count=5010
Kevin Parker
quelle
direct verursacht einen Fehler "Ungültiges Argument", aber die Verwendung von oflag = dsync funktioniert.
UsAaR33
Es tut mir leid, wenn es bei Ihnen nicht funktioniert hat, laut Manpage "Direktverwendung direkter E / A für Daten"
Kevin Parker