Wyłączyć domyślnie zabójcę Linux OOM?

37

Zabójca OOM w Linuksie sieją spustoszenie w różnych aplikacjach co jakiś czas i wydaje się, że tak naprawdę niewiele robi się po stronie programistycznej jądra, aby to poprawić. Czy nie byłoby lepiej, jako najlepszą praktykę podczas konfigurowania nowego serwera , odwrócić domyślną pamięć nadmiarową pamięci, to znaczy wyłączyć ją ( vm.overcommit_memory=2), chyba że wiesz, że chcesz ją włączyć do określonego zastosowania? A jakie byłyby te przypadki użycia, w których wiesz, że chcesz przesadzać?

Jako bonus, skoro zachowanie w przypadku vm.overcommit_memory=2zależy od vm.overcommit_ratioi zamiany przestrzeni, jaka byłaby dobra zasada przy doborze tych dwóch ostatnich, aby cała konfiguracja działała rozsądnie?

Peter Eisentraut
źródło

Odpowiedzi:

63

Interesująca analogia (z http://lwn.net/Articles/104179/ ):

Pewna firma lotnicza odkryła, że ​​tańsze jest latanie samolotami przy mniejszej ilości paliwa na pokładzie. Samoloty byłyby lżejsze i zużywałyby mniej paliwa, a pieniądze zostały zaoszczędzone. Jednak w rzadkich przypadkach ilość paliwa była niewystarczająca i samolot się rozbijał. Problem ten został rozwiązany przez inżynierów firmy poprzez opracowanie specjalnego mechanizmu OOF (brak paliwa). W nagłych przypadkach pasażer został wybrany i wyrzucony z samolotu. (W razie potrzeby procedurę powtórzono.) Opracowano obszerną teorię i wiele publikacji poświęcono problemowi właściwego wyboru ofiary, która ma zostać wyrzucona. Czy ofiarę należy wybrać losowo? A może należy wybrać najcięższą osobę? A może najstarszy? Jeśli pasażerowie zapłacą, aby nie zostać wyrzuconymi, żeby ofiara była najbiedniejsza na pokładzie? A jeśli na przykład wybrano najcięższą osobę, czy powinien istnieć specjalny wyjątek na wypadek, gdyby to był pilot? Czy pasażerowie pierwszej klasy powinni być zwolnieni? Teraz, gdy istniał mechanizm OOF, od czasu do czasu był aktywowany i wyrzucał pasażerów nawet wtedy, gdy brakowało paliwa. Inżynierowie wciąż dokładnie badają przyczyny tej usterki.

Andrzej
źródło
11
Bardzo mi się podobało, dziękuję za wykopanie go.
Nick Bolton
32

Zabójca OOM sieje spustoszenie tylko wtedy, gdy przeciąłeś swój system. Daj mu wystarczająco dużo zamiany i nie uruchamiaj aplikacji, które nagle decydują się zjeść ogromne ilości pamięci RAM, a nie będziesz mieć problemu.

Aby dokładnie odpowiedzieć na twoje pytania:

  • Nie sądzę, że dobrym pomysłem jest wyłączenie nadmiernego zaangażowania w ogólnym przypadku; bardzo niewiele aplikacji jest napisanych, aby poprawnie radzić sobie z brk(2) (a opakowania, które ich używają, takie jak malloc(3)) zwracają błąd. Kiedy eksperymentowałem z tym w mojej poprzedniej pracy, uważałem, że trudniej jest uzyskać wszystko, co jest w stanie poradzić sobie z błędami braku pamięci, niż po prostu poradzić sobie z konsekwencjami OOM (które w naszym przypadku było o wiele gorsze niż konieczność restartowania okazjonalnej usługi, jeśli wystąpiła OOM - musieliśmy zrestartować cały klaster, ponieważ GFS jest parą kału).
  • Chcesz przesadzić z każdym procesem, który zastępuje pamięć. Dwoma najczęstszymi sprawcami są tutaj Apache i JVM, ale wiele aplikacji robi to w większym lub mniejszym stopniu. Myślą , że w przyszłości mogą potrzebować dużo pamięci, więc od razu chwytają dużą część. W systemie z włączoną funkcją overcommit jądro mówi „meh, cokolwiek, przeszkadzaj mi, kiedy naprawdę chcesz pisać na tych stronach” i nic złego się nie dzieje. W systemie z nadmiernym zaangażowaniem jądro mówi „nie, nie możesz mieć tyle pamięci, jeśli zdarzy ci się pisać do tego wszystkiego w przyszłości, jestem bez kości, więc nie ma dla ciebie pamięci!” i przydział nie powiedzie się. Ponieważ nicwychodzi „och, OK, czy mogę mieć mniejszą ilość segmentu danych procesowych?”, a następnie proces (a) kończy się z błędem braku pamięci lub (b) nie sprawdza kodu powrotu z malloc, uważa, że ​​można jechać i zapisuje w niepoprawnej lokalizacji pamięci, powodując awarię. Na szczęście JVM robi wszystko, co w jego mocy, podczas uruchamiania (więc JVM albo uruchamia się albo umiera natychmiast, co zwykle zauważasz), ale Apache robi to z funkcjami z każdym nowym dzieckiem, co może mieć ekscytujące efekty w produkcji (niereprodukowalne "brak obsługi połączeń „rodzaje emocji).
  • Nie chciałbym ustawiać mojego overcommit_ratio na wartość wyższą niż domyślna 50%. Ponownie, z moich testów, chociaż ustawienie go na około 80 lub 90 może brzmieć jak fajny pomysł, jądro wymaga dużych porcji pamięci w niewygodnych czasach, a w pełni załadowany system z wysokim współczynnikiem przepełnienia prawdopodobnie nie będzie miał wystarczającej wolnej pamięci kiedy jądro tego potrzebuje (prowadząc do strachu, zarazy i przestojów). Dlatego gra z nadmiernym zaangażowaniem wprowadza nowy, jeszcze bardziej zabawny tryb awarii - zamiast tylko restartować proces, w którym OOMed skończył się, gdy zabraknie pamięci, teraz komputer ulega awarii, co prowadzi do awarii wszystkiego na maszynie. NIESAMOWITE!
  • Zamiana przestrzeni w systemie bez nadmiernego zaangażowania zależy od ilości wymaganej, ale nieużywanej pamięci potrzebnej twoim aplikacjom oraz zdrowego marginesu bezpieczeństwa. Wypracowanie tego, co jest potrzebne w konkretnym przypadku, pozostaje zadaniem dla czytelnika.

Zasadniczo z mojego doświadczenia wynika, że ​​wyłączenie nadmiernego zaangażowania jest fajnym eksperymentem, który rzadko sprawdza się w praktyce tak, jak brzmi teoretycznie. To ładnie koresponduje z moimi doświadczeniami z innymi tuningami w jądrze - programiści jądra Linuksa są prawie zawsze mądrzejsi od ciebie, a domyślne ustawienia działają najlepiej w ogromnej większości przypadków. Zostaw je w spokoju, a zamiast tego znajdź proces powodujący wyciek i napraw go.

womble
źródło
2
Nie chcę, aby mój proces tworzenia kopii zapasowej został zabity, ponieważ ktoś robi na moim serwerze WWW. Wyjątki są w porządku, ale domyślnie powinno być bezpieczeństwo i spójność. Optymalizacje takie jak OOM powinny być włączane ręcznie IMHO. To jest jak kodowanie, kodujesz czysto, a następnie optymalizujesz. Nadmierne zatwierdzanie jest fajną funkcją, ale nie powinno być domyślne.
Aki
1
Jeśli nie chcesz, aby proces tworzenia kopii zapasowej został zabity, ponieważ ktoś wykonuje DoS-serwer, nie konfiguruj swojego serwera w taki sposób, aby DoS mógł spowodować nadmierne obciążenie zasobów systemu.
womble
Mam 8 GB pamięci RAM i właśnie uruchomiłem Firefoksa, a maszyna wirtualna czasami powoduje, że zabójca OOM zabija maszynę wirtualną. Kompilując Unreal Engine 4, każde wywołanie klanu zajmuje 1 ~ 1,5 GB pamięci i ponownie, zabójca OOM zabija raz na jakiś czas. Teraz ogólnie mam się dobrze, bez zabójcy OOM prawdopodobnie i tak by się segregowali. Po prostu za każdym razem, gdy zabójca OOM chce zabić proces, mój system zawiesza się na 10 minut, zanim zły proces zostanie zabity. Bug może? Najprawdopodobniej. Czy ja tego chce Absolutnie nie. I to jest powód, dla którego można chcieć wyłączyć zabójcę OOM.
Shahbaz,
1
Jeśli robisz to wszystko na pudełku, potrzebujesz więcej pamięci RAM, a wyłączenie funkcji overcommit tylko pogorszy sytuację.
Ben Lutgens
6

Hmm, nie przekonują mnie argumenty przemawiające za przesadnym zabójstwem OOM ... Kiedy pisze Womble,

„Zabójca OOM sieje spustoszenie tylko wtedy, gdy przeciążiłeś swój system. Daj mu dość zamiany i nie uruchamiaj aplikacji, które nagle zdecydują się zjeść ogromne ilości pamięci RAM, i nie będziesz mieć problemu”.

Opisuje scenariusz środowiskowy, w którym nadmierne zaangażowanie i zabójca OOM nie są wymuszane lub nie działają „tak naprawdę” (jeśli wszystkie aplikacje przydzielą pamięć w razie potrzeby i będzie wystarczająca ilość pamięci wirtualnej do przydzielenia, zapisy pamięci będą ściśle zgodne z przydzieleniem pamięci bez błędy, więc tak naprawdę nie moglibyśmy mówić o zbyt dużym systemie, nawet gdyby włączona była strategia nadmiernego zaangażowania). Chodzi o ukryte przyznanie, że przesadne zabójstwo i zabójca OOM działają najlepiej, gdy ich interwencja nie jest potrzebna, co w pewnym stopniu podziela większość zwolenników tej strategii, o ile mogę powiedzieć (i przyznaję, że nie mogę powiedzieć wiele ...). Co więcej, odnosząc się do aplikacji o określonych zachowaniach podczas wstępnej alokacji pamięci, myślę, że konkretna obsługa może być dostrojona na poziomie dystrybucji, zamiast mieć wartość domyślną,

Jeśli chodzi o JVM, to jest to maszyna wirtualna, do pewnego stopnia musi przydzielić wszystkie zasoby, których potrzebuje podczas uruchamiania, aby mogła stworzyć swoje „fałszywe” środowisko dla swoich aplikacji i trzymać swój wolny zasób oddzielony od hosta środowisko, w miarę możliwości. Dlatego może być bardziej pożądane, aby nie uruchamiał się podczas uruchamiania, zamiast po pewnym czasie w wyniku „zewnętrznego” stanu OOM (spowodowanego nadmiernym zaangażowaniem / zabójcą OOM / cokolwiek) lub w każdym razie cierpienia z powodu takiego stanu zakłócającego jego własny wewnętrzne strategie obsługi OOM (ogólnie rzecz biorąc, maszyna wirtualna powinna uzyskać wszelkie wymagane zasoby od początku, a system hosta powinien je „ignorować” do końca, w ten sam sposób, w jaki jakakolwiek ilość fizycznego RAM udostępnionego z kartą graficzną nigdy nie jest - i nie może być - dotknięty przez system operacyjny).

Jeśli chodzi o Apache, wątpię, aby od czasu do czasu zabić cały serwer i zrestartować go lepiej, niż pozwolić jednemu dziecku, wraz z jednym połączeniem, zawieść od początku (= dziecka / połączenia) na początku (jakby to była zupełnie nowa instancja JVM utworzony po uruchomieniu innej instancji przez chwilę). Myślę, że najlepsze „rozwiązanie” może zależeć od konkretnego kontekstu. Na przykład, biorąc pod uwagę usługę e-commerce, może być o wiele lepszym rozwiązaniem, aby czasami kilka losowych połączeń z planem zakupów zawodziło losowo zamiast tracić całą usługę, z ryzykiem, na przykład, zakłócenia trwającej finalizacji zamówienia lub (może gorzej) proces płatności ze wszystkimi konsekwencjami sprawy (może nieszkodliwy, ale może szkodliwy - i na pewno, gdy pojawią się problemy,

W ten sam sposób, na stacji roboczej proces, który zużywa najwięcej zasobów, a więc dostosowywanie go jako pierwszego wyboru dla zabójcy OOM, może być aplikacją wymagającą dużej ilości pamięci, taką jak transkoder wideo lub oprogramowanie do renderowania, prawdopodobnie jedyną aplikacją użytkownik chce pozostać nietknięty. Te rozważania podpowiadają mi, że domyślna polityka zabójcy OOM jest zbyt agresywna. Wykorzystuje podejście „najgorszego dopasowania”, które jest w pewnym stopniu podobne do niektórych systemów plików (OOMK próbuje zwolnić jak najwięcej pamięci, jednocześnie zmniejszając liczbę zabitych podprocesów, aby zapobiec dalszej interwencji w krótkim czasie, ponieważ a także fs może przydzielić więcej miejsca na dysku, niż jest faktycznie potrzebne dla określonego pliku, aby zapobiec dalszemu przydziałowi, jeśli plik się powiększy, a tym samym do pewnego stopnia zapobiec fragmentacji).

Uważam jednak, że polityka przeciwna, na przykład podejście „najlepiej dopasowane”, może być lepsza, aby w pewnym momencie uwolnić potrzebną dokładną pamięć i nie zawracać sobie głowy „dużymi” procesami, które mogą być marnotrawstwem pamięć, ale także może nie, a jądro nie może tego wiedzieć (hmm, mogę sobie wyobrazić, że śledzenie liczby dostępów do stron i czasu może wskazywać, że proces alokuje pamięć, nie potrzebuje więcej, więc zgadnij, czy proces marnuje pamięć lub po prostu dużo zużywa, ale opóźnienia dostępu powinny być ważone w cyklach procesora, aby odróżnić marnowanie pamięci od aplikacji intensywnie korzystającej z pamięci i procesora, ale, chociaż potencjalnie niedokładna, taka heurystyka może mieć nadmierny narzut).

Co więcej, może nie być prawdą, że zabicie mniejszej liczby możliwych procesów jest zawsze dobrym wyborem. Na przykład w środowisku komputerowym (na przykład nettop lub netbook z ograniczonymi zasobami, na przykład) użytkownik może uruchamiać przeglądarkę z kilkoma kartami (w ten sposób zużywając pamięć - załóżmy, że jest to pierwszy wybór dla OOMK) , a także kilka innych aplikacji (edytor tekstu z niezapisanymi danymi, klient poczty, czytnik pdf, odtwarzacz multimediów, ...) oraz kilka demonów (systemowych), a także kilka instancji menedżera plików. Teraz pojawia się błąd OOM i OOMK decyduje się zabić przeglądarkę, gdy użytkownik robi coś, co uznaje się za „ważne” w sieci ... użytkownik byłby rozczarowany. Z drugiej strony zamknięcie kilku menedżerów plików

W każdym razie uważam, że użytkownik powinien mieć możliwość samodzielnego podjęcia decyzji o tym, co należy zrobić. W systemie stacjonarnym (= interaktywnym) powinno to być względnie łatwe, pod warunkiem, że zarezerwowano wystarczającą ilość zasobów, aby poprosić użytkownika o zamknięcie dowolnej aplikacji (ale wystarczy nawet zamknięcie kilku kart) i obsłużenie jego wyboru (opcja może polega na utworzeniu dodatkowego pliku wymiany, jeśli jest wystarczająco dużo miejsca). W przypadku usług (i ogólnie) rozważę również dwa dalsze możliwe ulepszenia: jeden rejestruje interwencje zabójcy OOM, a także procesy uruchamiania / rozwidlania awarii w taki sposób, że awaria może być łatwo debugowana (na przykład interfejs API może poinformować proces, który wydał utworzenie nowego procesu lub rozwidlenie - dlatego serwer taki jak Apache, z odpowiednią poprawką, może zapewnić lepsze rejestrowanie niektórych błędów); można tego dokonać niezależnie od wysiłku nadmiernego zaangażowania / OOMK; po drugie, ale nie ważne, można ustanowić mechanizm dostrajania algorytmu OOMK - wiem, że jest możliwe, do pewnego stopnia, zdefiniowanie konkretnej polityki dla poszczególnych procesów, ale chciałbym „scentralizowany” mechanizm konfiguracji oparty na jednej lub większej liczbie list nazw aplikacji (lub identyfikatorów) w celu zidentyfikowania odpowiednich procesów i nadania im pewnego stopnia ważności (zgodnie z wymienionymi atrybutami); taki mechanizm powinien (lub przynajmniej mógłby) być również warstwowy, tak aby istniała lista zdefiniowana przez użytkownika najwyższego poziomu, lista zdefiniowana przez system (dystrybucję) oraz wpisy zdefiniowane przez aplikację (na najniższym poziomie) , na przykład menedżer plików DE może poinstruować OOMK, aby bezpiecznie zabił dowolną instancję,

Ponadto można zapewnić interfejs API, aby umożliwić aplikacjom zwiększenie lub obniżenie poziomu „ważności” w czasie wykonywania (w odniesieniu do celów zarządzania pamięcią i niezależnie od priorytetu wykonania), aby na przykład procesor tekstu mógł zacząć małe „znaczenie”, ale wzrasta, gdy niektóre dane są przechowywane przed opróżnieniem do pliku lub wykonywana jest operacja zapisu, a mniejsza ważność ponownie, gdy taka operacja się skończy (analogicznie menedżer plików może zmienić poziom, gdy przejdzie z tylko udostępnianie plików do obsługi danych i odwrotnie, zamiast używania osobnych procesów, a Apache może nadać różny poziom ważności różnym dzieciom lub zmienić stan dziecka zgodnie z niektórymi zasadami ustalonymi przez sysadmins i ujawnionymi przez Apache - lub dowolny inny serwer - ustawienia). Oczywiście, taki interfejs API mógłby i byłby nadużywany / nadużywany, ale myślę, że jest to niewielki problem w porównaniu do jądra arbitralnie zabijającego procesy w celu zwolnienia pamięci bez żadnych istotnych informacji o tym, co dzieje się w systemie (oraz o zużyciu pamięci / czasie tworzenia lub podobnych) dla mnie wystarczające lub „sprawdzające”) - tylko użytkownicy, administratorzy i autorzy programów mogą naprawdę ustalić, czy proces jest „nadal potrzebny” z jakiegoś powodu, jaki jest powód i / lub czy aplikacja jest w stanie wiodącym do utraty danych lub innych szkód / problemów w przypadku śmierci; można jednak poczynić pewne założenia, na przykład szukać zasobów pewnego rodzaju (deskryptory plików, gniazda sieciowe itp.) pozyskanych przez proces, a przy oczekujących operacjach można stwierdzić, czy proces powinien być w wyższym „stanie” niż jeden zestaw,

Lub po prostu unikaj nadmiernego zaangażowania i pozwól jądru robić to, co musi zrobić jądro, przydzielając zasoby (ale nie ratując ich arbitralnie jak zabójca OOM), planując procesy, zapobiegając głodom i impasom (lub ratując przed nimi), zapewniając pełne zapobieganie i separacja przestrzeni pamięci i tak dalej ...

Chciałbym również poświęcić więcej słów na temat przesadnych podejść. Z innych dyskusji pomyślałem, że jedną z głównych obaw związanych z nadmiernym zaangażowaniem (zarówno jako powód, aby tego chcieć, jak i jako źródło możliwych problemów) jest obsługa rozwidlenia: szczerze mówiąc, nie wiem, jak dokładnie kopia- Strategia zapisu jest zaimplementowana, ale myślę, że każdą agresywną (lub optymistyczną) politykę można złagodzić dzięki strategii podobnej do zamiany. Oznacza to, że zamiast klonować (i dostosowywać) rozwidlone strony kodowe procesu i struktury planowania, kilka innych stron danych można skopiować przed faktycznym zapisem, wybierając spośród tych stron, do których proces macierzysty uzyskiwał częstsze zapisywanie (to znaczy za pomocą licznika do operacji zapisu).

Wszystko oczywiście IMHO.


źródło
5
„Co więcej, można zapewnić interfejs API, aby umożliwić aplikacjom zwiększenie lub obniżenie poziomu„ ważności ”w czasie wykonywania” /proc/$PID/oom_adj.
Vi.
1
Jeśli chodzi o JVM, istnieje gotcha, która powoduje, że w niektórych przypadkach chcesz przesadzić pamięć: jeśli chcesz utworzyć kolejną JVM z oryginalnej JVM, wywoła fork (). Wywołanie rozwidlenia przydzieli tyle pamięci, co oryginalny proces (pierwszy), dopóki tak naprawdę nie rozpocznie procesu. Powiedzmy, że masz maszynę JVM o pojemności 4 GB i chcesz z niej utworzyć nową maszynę JVM o pojemności 512 KB, chyba że masz nadmiar poleceń, potrzebujesz 8 GB pamięci, aby to zrobić ...
alci
4
@Vi. Wydaje się, że teraz jest/proc/$PID/oom_score_adj
erm3nda
1

Jeśli procesy są wyczerpane przez procesy w stopniu, który może zagrozić stabilności systemu, pojawia się zabójca OOM. Zadaniem OOM Killera jest zabijanie procesów, dopóki nie zostanie zwolniona wystarczająca ilość pamięci do sprawnego funkcjonowania reszty procesu. OOM Killer musi wybrać „najlepszy” proces do zabicia. „Najlepsze” tutaj odnosi się do tego procesu, który zwolni maksymalną pamięć po zabiciu i jest również najmniej ważny dla systemu. Podstawowym celem jest zabicie jak najmniejszej liczby procesów, które minimalizują wyrządzone szkody, a jednocześnie maksymalizują ilość uwolnionej pamięci. Aby to ułatwić, jądro utrzymuje oom_score dla każdego z procesów. Możesz zobaczyć wynik oom_score każdego z procesów w systemie plików / proc w katalogu pid

# cat /proc/10292/oom_score

Im wyższa wartość oom_score dowolnego procesu, tym większe jest prawdopodobieństwo, że zabije go OOM Killer w sytuacji braku pamięci.

Credit: - Jądro Linux uruchamia zabójcę OOM

dinkey jhanwar
źródło