Řešení 1:
„Pánovým“ přístupem by však byl upgrade na 64bitový O/S (toto je 32bitový), protože rozložení zón je provedeno jinak.
OK, takže se zde pokusím odpovědět, proč jste zde zažili OOM. Zde hraje roli řada faktorů.
- Velikost objednávky požadavku a způsob, jakým jádro zachází s určitými velikostmi objednávek.
- Vybraná zóna.
- Vodoznaky, které tato zóna používá.
- Fragmentace v zóně.
Když se podíváte na samotný OOM, je tam zjevně spousta volné paměti, ale byl vyvolán OOM-killer? Proč?
Velikost objednávky požadavku a způsob, jakým jádro zachází s určitými velikostmi objednávek
Jádro alokuje paměť podle pořadí. „Objednávka“ je oblast souvislé paměti RAM, která musí být splněna, aby požadavek fungoval. Objednávky jsou uspořádány podle řádů (tedy pořadí názvů) pomocí algoritmu 2^(ORDER + 12)
. Takže objednávka 0 je 4096, objednávka 1 je 8192, objednávka 2 je 16384 a tak dále.
Jádro má pevně zakódovanou hodnotu toho, co je považováno za „vysoký řád“ (> PAGE_ALLOC_COSTLY_ORDER
). Toto je řád 4 a vyšší (64 kb nebo více je vyšší řád).
Vysoké objednávky jsou uspokojeny pro alokace stránek jinak než nízké objednávky. Alokace vysokého řádu, pokud se mu nepodaří uchopit paměť, na moderních jádrech ano.
- Zkuste spustit rutinu komprimace paměti a defragmentovat paměť.
- Nikdy zavolejte OOM-killer, abyste splnili požadavek.
Velikost vaší objednávky je uvedena zde
Dec 27 09:19:05 2013 kernel: : [277622.359064] squid invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0
Objednávka 3 je nejvyšším z požadavků nižšího řádu a (jak vidíte) ve snaze vyhovět mu vyvolá OOM-killer.
Všimněte si, že většina alokací uživatelského prostoru nepoužívá požadavky vyššího řádu. Typicky je to jádro, které vyžaduje souvislé oblasti paměti. Výjimkou může být situace, kdy uživatelský prostor používá velké stránky – ale to není tento případ.
Ve vašem případě je alokace 3. řádu volána jádrem, které chce zařadit paket do fronty do síťového zásobníku – k tomu vyžaduje alokaci 32 kb.
Vybraná zóna.
Jádro rozděluje oblasti paměti do zón. Toto sekání se provádí proto, že na x86 jsou určité oblasti paměti adresovatelné pouze určitým hardwarem. Starší hardware může být například schopen adresovat paměť pouze v zóně „DMA“. Když chceme alokovat nějakou paměť, nejprve se vybere zóna a pouze volná paměť z této zóny se započítává při rozhodování o alokaci.
I když nemám úplně znalosti o algoritmu výběru zón, typickým případem použití není nikdy alokace z DMA, ale obvykle výběr nejnižší adresovatelné zóny, která by mohla uspokojit požadavek.
Během OOM je vyplivováno mnoho zónových informací, které lze také získat z /proc/zoneinfo
.
Dec 27 09:19:05 2013 kernel: : [277622.359382] DMA free:2332kB min:36kB low:44kB high:52kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15968kB managed:6960kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:8kB slab_unreclaimable:288kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359393] Normal free:114488kB min:3044kB low:3804kB high:4564kB active_anon:0kB inactive_anon:0kB active_file:252kB inactive_file:256kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:894968kB managed:587540kB mlocked:0kB dirty:0kB writeback:0kB mapped:4kB shmem:0kB slab_reclaimable:117712kB slab_unreclaimable:138616kB kernel_stack:11976kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:982 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359404] HighMem free:27530668kB min:512kB low:48272kB high:96036kB active_anon:2634060kB inactive_anon:217596kB active_file:4688452kB inactive_file:1294168kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:36828872kB managed:36828872kB mlocked:0kB dirty:0kB writeback:0kB mapped:183132kB shmem:39400kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:430856kB unstable:0kB bounce:367564104kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
Zóny, které máte, DMA, Normal a HighMem označují 32bitovou platformu, protože zóna HighMem na 64bitech neexistuje. Také na 64bitových systémech je Normal mapován na 4 GB a více, zatímco na 32bitových systémech mapuje až 896 Mb (ačkoli ve vašem případě jádro hlásí, že spravuje pouze menší část než toto:- managed:587540kB
.)
Odkud tato alokace pochází, je možné zjistit opětovným pohledem na první řádek, gfp_mask=0x42d0
nám říká, jaký typ alokace byl proveden. Poslední bajt (0) nám říká, že se jedná o alokaci z normální zóny. Významy gfp jsou umístěny v include/linux/gfp.h.
Vodoznaky, které tato zóna používá.
Když je paměť málo, akce k jejímu získání jsou určeny vodoznakem. Zobrazují se zde:min:3044kB low:3804kB high:4564kB
. Pokud volná paměť dosáhne „nízké“, pak dojde k odkládání, dokud nepřekročíme práh „vysoké“. Pokud paměť dosáhne 'min', musíme zabít věci, abychom uvolnili paměť pomocí OOM-killeru.
Fragmentace v zóně.
Aby bylo možné zjistit, zda lze uspokojit požadavek na konkrétní objednávku paměti, jádro počítá s počtem volných a dostupných stránek z každé objednávky. Toto je čitelné v /proc/buddyinfo
. Zprávy OOM-killer navíc také vyplivnou informace o kamarádech, jak je vidět zde:
Normal: 5360*4kB (UEM) 3667*8kB (UEM) 3964*16kB (UEMR) 13*32kB (MR) 0*64kB 1*128kB (R) 1*256kB (R) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 115000kB
Aby alokace paměti byla splněna, musí být k dispozici volná paměť v požadované velikosti objednávky nebo vyšší alokaci. Mít spoustu a spoustu volných dat v nižších řádech a žádné ve vyšších řádech znamená, že vaše paměť je fragmentovaná. Pokud získáte alokaci velmi vysokého řádu, je možné (i se spoustou volné paměti) neuspokojit, protože nejsou k dispozici žádné stránky vyššího řádu. Jádro může defragmentovat paměť (toto se nazývá zhušťování paměti) přesouváním mnoha stránek nízkého řádu, takže nezanechávají mezery v adresovatelném prostoru RAM.
Byl vyvolán OOM-killer? Proč?
Takže, vezmeme-li tyto věci v úvahu, můžeme říci následující;
- Došlo k pokusu o souvislé přidělení 32 kB. Z normální zóny.
- Ve vybrané zóně bylo dostatek volné paměti.
- K dispozici byla paměť 3, 5 a 6
13*32kB (MR) 1*128kB (R) 1*256kB (R)
Pokud tedy bylo volnou paměť, jiné objednávky mohly vyhovět žádosti. co se stalo?
Alokace z objednávky je více než jen kontrola množství volné paměti dostupné pro tuto objednávku nebo vyšší. Jádro efektivně odečte paměť od všech nižších řádů od celkového volného řádku a poté provede minimální kontrolu vodoznaku na tom, co zbývá.
Co se stane ve vašem případě, je zkontrolovat naši volnou paměť pro tu zónu, kterou musíme udělat.
115000 - (5360*4) - (3667*8) - (3964*16) = 800
Toto množství volné paměti je porovnáno s min
vodoznak, což je 3044. Technicky vzato -- nemáte žádnou volnou paměť pro provedení požadované alokace. A to je důvod, proč jste vyvolali OOM-killer.
Oprava
Existují dvě opravy. Upgrade na 64bit změní rozdělení vaší zóny tak, že „Normální“ je 4 GB až 36 GB, takže neskončíte s „výchozím“ přidělováním paměti do zóny, která může být tak silně fragmentovaná. Tento problém neřeší to, že máte více adresovatelné paměti (protože PAE již používáte), pouze zóna, ze které vybíráte, má více adresovatelné paměti.
Druhý způsob (který jsem nikdy netestoval) je pokusit se přimět jádro, aby agresivněji komprimovalo vaši paměť.
Pokud změníte hodnotu vm.extfrag_threshold
od 500 do 100, je pravděpodobnější, že zhutní paměť ve snaze dodržet alokaci vysokého řádu. I když jsem se s touto hodnotou nikdy předtím nepletl - bude to také záviset na tom, jaký je váš index fragmentace, který je k dispozici v /sys/kernel/debug/extfrag/extfrag_index
. V tuto chvíli nemám krabici s dostatečně novým jádrem, abych viděl, co ukazuje, že nabízí víc než toto.
Alternativně můžete spustit nějakou úlohu cron (je to strašně, strašně ošklivé), abyste ručně zkomprimovali paměť sami zápisem do /proc/sys/vm/compact_memory
.
Upřímně řečeno, nemyslím si, že skutečně existuje způsob, jak vyladit systém, aby se tomuto problému vyhnul - je to povaha alokátoru paměti, který takto funguje. Změna architektury platformy, kterou používáte, je pravděpodobně jediné zásadně řešitelné řešení.
Řešení 2:
Hned od začátku:měli byste opravdu zvolte 64bitový operační systém. Máte dobrý důvod zůstat u 32bitové verze zde?
Je těžké diagnostikovat tento problém, aniž byste se blíže podívali na systém, nejlépe v době, kdy selže, takže můj (rychlý) příspěvek je víceméně obecně zaměřen na problémy s pamětí na 32bitových systémech. Zmínil jsem se, že přechod na 64bitovou verzi toto všechno odstraní?
Váš problém je trojí.
Za prvé, dokonce i na jádře PAE je adresní prostor na proces omezen na 4GiB[1]. To znamená, že vaše instance chobotnice nebude nikdy schopna sníst více než 4GiB RAM na proces. S chobotnicí se moc nevyznám, ale pokud je toto váš hlavní proxy server, stejně to nemusí stačit.
Zadruhé, na 32bitovém systému s obrovským množstvím paměti RAM se velké množství paměti v takzvaném 'ZONE_NORMAL' používá k ukládání datových struktur, které jsou potřebné k použití paměti v ZONE_HIGHMEM. Tyto datové struktury nelze přesunout do ZONE_HIGHMEM samotné, protože paměť, kterou jádro používá pro své vlastní účely, musí být vždy v ZONE_NORMAL (tj. v prvním 1GiB-ish). Čím více paměti máte v ZONE_HIGHMEM (ve vašem případě hodně), tím větší je to problém, protože jádro pak potřebuje stále více paměti od ZONE_NORMAL ke správě ZONE_HIGHMEM. Jak množství volné paměti v ZONE_NORMAL vyschne, váš systém může u některých úloh selhat, protože ZONE_NORMAL je hodně věcí se děje na 32bitovém systému. Všechny operace s pamětí související s jádrem, například;)
Za třetí, i když v ZONE_NORMAL zbývá nějaká paměť (neprocházel jsem podrobně vaše protokoly), některé operace s pamětí budou vyžadovat nefragmentovanou paměť. Pokud je například veškerá vaše paměť fragmentována na opravdu malé kousky, některé operace, které potřebují více, selžou. [3] Krátký pohled na vaše protokoly ukazuje poměrně významnou fragmentaci v ZONE_DMA a ZONE_NORMAL.
Edit:Výše uvedená odpověď Mlfe má vynikající vysvětlení, jak to podrobně funguje.
Znovu:na 64bitovém systému je veškerá paměť v ZONE_NORMAL. Na 64bitových systémech není zóna HIGHMEM. Problém vyřešen.
Edit:Můžete se podívat sem [4] a zjistit, zda můžete oom-killerovi říct, aby nechal vaše důležité procesy na pokoji. To nevyřeší všechno (pokud vůbec něco), ale za pokus to možná stojí.
[1] http://en.wikipedia.org/wiki/Physical_address_extension#Design
[2] http://www.redhat.com/archives/rhelv5-list/2008-September/msg00237.html a https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/5/html /Tuning_and_Optimizing_Red_Hat_Enterprise_Linux_for_Oracle_9i_and_10g_Databases/sect-Oracle_9i_and_10g_Tuning_Guide-Hardware_Architectures_and_Linux_Kernels_emch_Kechu.html_em_Arch_Kechu.
[3] http://bl0rg.krunch.be/oom-frag.html
[4] http://lwn.net/Articles/317814/
Řešení 3:
@MIfe již poskytl vynikající informace o tom, jak se zachází s alokací paměti v jádře, a také vám poskytl správné řešení, jako je přechod na 64bitový operační systém a ošklivý hack, jako je ruční zhutňování paměti pomocí /proc/sys/vm/compact_memory
v cron
.
Moje 2 centy by byly dalším řešením, které vám může pomoci:
Všiml jsem si, že máte tcp_tso_segment
v backtrace vašeho jádra, takže:
# ethtool -K ethX tso off gso off lro off
může snížit tlak na mm
tím, že jej donutíte používat nižší objednávky.
PS . seznam všech zátěží lze získat přes # ethtool -k ethX
Řešení 4:
Panika je způsobena tím, že je nastaveno sysctl "vm.panic_on_oom =1" - myšlenka je taková, že restartování systému jej vrátí do zdravého stavu. Toto můžete změnit v sysctl.conf.
Hned nahoře čteme chobotnice vyvolané oom killer. Můžete zkontrolovat konfiguraci olihně a její maximální využití paměti (nebo přejít na 64bitový operační systém).
/proc/meminfo ukazuje velkou používanou paměťovou zónu, takže používáte 32bitové jádro s 36GB pamětí. Můžete také vidět, že v normální zóně jádro, aby uspokojilo požadavky chobotnice na paměť, bez úspěchu naskenovalo 982 stránek:
pages_scanned:982 all_unreclaimable? yes