Efekty konfiguracji pamięci vm.overcommit_memory

41

Mój serwer internetowy VPS działający na CentOS 5.4 (jądro Linuksa 2.6.16.33-xenU) nieregularnie (jak raz w miesiącu daj lub zajmij kilka tygodni) przestaje odpowiadać z powodu uruchomienia programu oom-killer. Monitorowanie serwera pokazuje, że nie działa zwykle brakuje pamięci, co jakiś czas.

Przeczytałem kilka blogów wskazujących na tę stronę, która omawia konfigurację jądra, aby lepiej zarządzać nadpisywaniem przy użyciu następujących ustawień sysctl:

vm.overcommit_memory = 2
vm.overcommit_ratio = 80

Rozumiem to (co może być błędne, ale nie mogę znaleźć kanonicznej definicji do wyjaśnienia), że zapobiega to nadmiernej alokacji pamięci przez jądro poza wymianą + 80% pamięci fizycznej.

Jednak mam również przeczytać kilka innych źródeł sugeruje, że te ustawienia nie są dobrym pomysłem - choć krytycy tego podejścia zdają się mówić „nie rób rzeczy zepsuć system, a nie próbować to kludge” w założeniu, że związek przyczynowy jest zawsze znany.

Moje pytanie brzmi: jakie są zalety i wady tego podejścia w kontekście serwera WWW Apache2 obsługującego około 10 witryn o niskim natężeniu ruchu? W moim konkretnym przypadku serwer WWW ma 512 MB pamięci RAM i 1024 MB przestrzeni wymiany. Wydaje się to wystarczające przez zdecydowaną większość czasu.

dunxd
źródło

Odpowiedzi:

32

Ustawienie wartości overcommit_ratio80 prawdopodobnie nie jest właściwym działaniem. Ustawienie wartości mniejszej niż 100 jest prawie zawsze niepoprawne.

Powodem tego jest to, że aplikacje linuksowe przydzielają więcej, niż naprawdę potrzebują. Powiedzmy, że przydzielają 8 KB do przechowywania ciągu znaków złożonego z kilku znaków. To kilka nieużywanych KB. Aplikacje często to robią i do tego właśnie jest przeznaczona overcommit.

Zasadniczo przy nadmiernej liczbie poleceń na poziomie 100 jądro nie pozwoli aplikacjom na przydzielenie więcej pamięci niż masz (swap + ram). Ustawienie go na mniej niż 100 oznacza, że ​​nigdy nie wykorzystasz całej pamięci. Jeśli zamierzasz ustawić to ustawienie, powinieneś ustawić je na wartość wyższą niż 100 ze względu na wyżej wspomniany scenariusz, który jest dość powszechny.

Teraz, jeśli chodzi o problem z wyzwalaniem zabójcy OOM, ręczne ustawienie overcommit prawdopodobnie tego nie naprawi. Ustawienie domyślne (określanie heurystyczne) jest dość inteligentne.

Jeśli chcesz sprawdzić, czy to naprawdę jest przyczyną problemu, sprawdź, /proc/meminfokiedy działa zabójca OOM. Jeśli widzisz, że Committed_ASjest blisko CommitLimit, ale freewciąż pokazuje dostępną wolną pamięć, to tak, możesz ręcznie dostosować overcommit do swojego scenariusza. Ustawienie zbyt niskiej wartości spowoduje, że OOM Killer zacznie zabijać aplikacje, gdy nadal będzie dużo wolnej pamięci. Ustawienie go zbyt wysoko może spowodować śmierć losowych aplikacji, które próbują użyć pamięci, do której zostały przydzielone, ale nie są faktycznie dostępne (gdy cała pamięć faktycznie się zużyje).

Patrick
źródło
1
Dzięki - próbuję rzeczy z overcommit_ratio ustawionym na 100, aby zobaczyć, co się stanie. Główny problem, jaki mam, polega na tym, że po uruchomieniu Oom-Killera niezmiennie zabija sshd, uniemożliwiając mi dostęp do serwera i sprawdzenie, co się dzieje. Wydaje mi się, że tak naprawdę potrzebuję, aby zatrzymać działanie programu Oom-Killer i jakiś sposób na zapisanie tego, co się stanie, gdy się uruchomi, abym mógł znaleźć przyczynę problemu.
dunxd
4
@ Dunxd możesz użyć /proc/<PID>/oom_score_adjdo tego celu. Na przykład, jeśli dla sshd ustawisz oom_score_adj na -1000, Oom Killer nigdy nie będzie atakował sshd, gdy chce coś zabić. Całkowite zatrzymanie zabójcy zabójców nie jest dobrym pomysłem, ponieważ wtedy twoje programy nie będą w stanie przywrócić pamięci, a i tak umrą.
Patrick
4
@unxd jest dziedziczony. niech skrypt inicjujący ustawi go na sobie, a wszystko, co uruchomi skrypt skryptowy, dziedziczy go.
Patrick
4
Twój przykład 4 KB jest nieprawidłowy. Pamięć wirtualna jest używana ze stronami, a (najmniejszy) rozmiar strony w systemie Linux wynosi 4 KB. Oznacza to, że przechowywanie kilku znaków wymaga gdzieś mapowania 4 KB niezależnie od ustawień nadmiernego zaangażowania. Właściwym przykładem przekroczenia pamięci byłoby na przykład przydzielenie 10 KB i użycie tylko pierwszych 4100 bajtów. Oznacza to, że dwie strony o rozmiarze 4 KB muszą przechowywać dane, a jedna dodatkowa strona jest nieużywana. Systemy, które nie są nadmiernie zapraszane, zawsze będą miały tę trzecią stronę gotową do przechowywania danych w przypadku nadejścia popytu, a systemy zatwierdzające nie będą tego wymuszać.
jlliagre
2
/ proc / self wskazuje na bieżący proces, więc / proc / self / oom_score_adj może zostać użyty do zmiany oom_score_adj bieżącego procesu.
r_2
23

Rozdział 9.6 „Overcommit and OOM” w dokumencie, o którym wspomina @dunxd, jest szczególnie graficzny na temat niebezpieczeństw związanych z dopuszczeniem overcommit. Jednak 80wyglądało to również dla mnie interesująco, więc przeprowadziłem kilka testów.

Odkryłem, że overcommit_ratiowpływa na całkowitą pamięć RAM dostępną dla WSZYSTKICH procesów. Procesy rootowania nie wydają się być traktowane inaczej niż zwykłe procesy użytkownika.

Ustawienie współczynnika na 100lub mniej powinno zapewnić klasyczną semantykę, w której wartości zwracane malloc/sbrksą wiarygodne. Ustawienie niższych współczynników 100może być sposobem na zarezerwowanie większej ilości pamięci RAM na działania niezwiązane z procesem, takie jak buforowanie i tak dalej.

Tak więc na moim komputerze z 24 GiB RAM, z wyłączoną zamianą, 9 GiB w użyciu, z topwyświetlaniem

Mem:  24683652k total,  9207532k used, 15476120k free,    19668k buffers
Swap:        0k total,        0k used,        0k free,   241804k cached

Oto niektóre overcommit_ratioustawienia i ilość pamięci RAM, którą mój program RAM-konsument może pobrać (dotykając każdej strony) - w każdym przypadku program wyszedł poprawnie po mallocawarii.

 50    ~680 MiB
 60   ~2900 MiB
 70   ~5200 MiB
100  ~12000 MiB

Uruchamianie kilku jednocześnie, nawet z niektórymi użytkownikami root, nie zmieniło łącznej kwoty, którą zużyli razem. Interesujące jest to, że nie był w stanie zużyć ostatnich 3+ GiB; freenie spadnie znacznie poniżej tego, co jest pokazane tutaj:

Mem:  24683652k total, 20968212k used,  3715440k free,    20828k buffers

Eksperymenty były chaotyczne - wszystko, co korzysta z malloc w chwili, gdy cała pamięć RAM jest w użyciu, ma tendencję do zawieszania się, ponieważ wielu programistów obawia się sprawdzania błędów malloc w C, niektóre popularne biblioteki kolekcji ignorują to całkowicie, a C ++ i różne inne języki są nawet gorzej.

Większość wczesnych implementacji wyobrażonej pamięci RAM dotyczyła bardzo konkretnego przypadku, w którym jeden duży proces - powiedzmy ponad 51% dostępnej pamięci - był potrzebny fork(), aby exec()jakiś program wsparcia, zwykle o wiele, znacznie mniejszy. Systemy operacyjne z semantyką kopiowania przy zapisie pozwoliłyby na to fork(), ale pod warunkiem, że jeśli rozwidlony proces faktycznie spróbuje zmodyfikować zbyt wiele stron pamięci (każda z nich musiałaby zostać utworzona jako nowa strona niezależna od początkowego ogromnego procesu) w końcu zostanie zabity. Proces nadrzędny był zagrożony tylko przy przydzielaniu większej ilości pamięci i mógł poradzić sobie z wyczerpaniem, w niektórych przypadkach po prostu czekając trochę na śmierć innego procesu, a następnie kontynuując. Proces potomny zwykle po prostu zastępuje się (zwykle mniejszym) programem przezexec() i wtedy był wolny od zastrzeżenia.

Nadmierna koncepcja Linuksa jest ekstremalnym podejściem pozwalającym zarówno na fork()wystąpienie, jak i na masową ogólną alokację pojedynczych procesów. Zgony OOM-killer-spowodowane zdarzyć asynchronicznie, nawet do programów, które zrobienia alokacji pamięci uchwyt odpowiedzialnie. Ja osobiście nienawidzę nadmiernego zaangażowania całego systemu, a szczególnie zabójcy zagłady - sprzyja to diabelnie ostrożnemu podejściu do zarządzania pamięcią, które infekuje biblioteki i za ich pośrednictwem każdą aplikację, która ich używa.

Sugeruję ustawienie współczynnika na 100, a także posiadanie partycji wymiany, która na ogół kończy się przyzwyczajeniem się przez ogromne procesy - które często wykorzystują tylko niewielką część siebie, która zostaje upchnięta w swap, a zatem chronić ogromną większość procesów przed błędami zabójcy OOM. To powinno zabezpieczyć twój serwer przed przypadkową śmiercią, a jeśli został napisany z myślą o mallocodpowiedzialnym postępowaniu , nawet bezpieczny przed samobójstwem (ale nie stawiaj na to drugie).

To znaczy, że używam tego w /etc/sysctl.d/10-no-overcommit.conf

vm.overcommit_memory = 2
vm.overcommit_ratio = 100
Alex North-Keys
źródło
I czy poleciłbyś zachować vm.overcommit_memory na 2?
Ut xD
1
Dobra uwaga - tego właśnie używam; Myślę, że pominąłem to w mojej odpowiedzi, ponieważ jest już w pytaniu
Alex North-Keys