Treści na tej stronie zostały przetłumaczone przy użyciu sztucznej inteligencji (AI) lub technologii tłumaczenia maszynowego i mogą zawierać błędy.

Skip to content

Poprawa wykorzystania zasobów serwera poprzez śledzenie wycieków pamięci

Około rok temu zauważyliśmy pierwsze oznaki pojawiającego się trendu wskazującego na spadek wydajności naszych serwerów gier. Na początku nie miało to prawie żadnego wpływu na graczy, ponieważ dysponowaliśmy dużym zapasem mocy obliczeniowej, ale nasze rezerwy szybko się kurczyły. Serwer gry bez problemu przyjmuje nowych graczy lub uruchamia nowe gry, o ile ma wystarczające zasoby procesora i pamięci. W tym przypadku to właśnie próg pamięci ograniczał liczbę gier hostowanych na serwerze. Niezwiązanie tego problemu oznaczałoby konieczność rozbudowy naszych centrów danych, aby obsłużyć naszą bazę graczy, co wiązałoby się z wysokimi kosztami pracy i finansowymi.

Pierwszą hipotezą, którą zbadaliśmy, było to, że zmienia się wzorzec wykorzystania pamięci przez gry. Zachęcamy deweloperów do przekraczania granic platformy i wykorzystywania dostępnych zasobów do tworzenia niesamowitych i przełomowych gier. Łatwo było to sprawdzić – mogliśmy zsumować wykorzystanie pamięci przez wszystkie gry i zobaczyć, czy wzrosło. Jednak nic z tego – średnie i procentowe wartości agregowane dla poszczególnych gier pozostawały na stosunkowo stałym poziomie, podczas gdy nasza pojemność systematycznie malała.

Co miesiąc przechowujemy terabajty danych dotyczących wydajności i wykorzystania zasobów, które można agregować i filtrować, aby znaleźć pierwotną przyczynę tego typu problemów. Próbowaliśmy zawęzić problem do konkretnego regionu geograficznego, typu sprzętu lub wersji oprogramowania, ale niestety problem występował wszędzie. Postanowiliśmy więc przeprowadzić wyrywkowe kontrole kilku serwerów gier i przeprowadzić szczegółowe dochodzenie. Początkowo zmyliła mnie koncepcja „wolnej” pamięci w systemach Linux. Jest to tak powszechny problem, że skłoniło to kogoś do zarejestrowania domeny i założenia strony internetowej wyjaśniającej kategorie pamięci: https://www.linuxatemyram.com.

TL;DR: Wykorzystanie większości pamięci to dobra rzecz, a wolna pamięć to zmarnowana pamięć. Musimy się martwić tylko wtedy, gdy dostępna pamięć zbliża się do 0.

Kiedy ustaliliśmy, że prawidłowo śledzimy pamięć, rozpoczęliśmy serię eksperymentów, aby ustalić, gdzie jest ona wykorzystywana. Nasze podejście polegało na śledzeniu konkretnych podkategorii pamięci, abyśmy mogli opracować ukierunkowane rozwiązanie.

(குறிப்பு: நினைவகப் பயன்பாட்டு % ஆனது (மொத்த இயற்பியல் நினைவகம் - கிடைக்கும் நினைவகம்) / மொத்த இயற்பியல் நினைவகம் எனக் கணக்கிடப்படுகிறது)

Dostępna pamięć jest obliczana jako w przybliżeniu suma MemFree + Active(file) + Inactive(file) + SReclaimable.

MemFree śledzi pamięć niewykorzystaną.

Active(file) i Inactive(file) śledzą pamięć bufora stron. Bufor stron przechowuje dane, do których uzyskano dostęp, w pamięci, aby zmniejszyć ilość operacji wejścia/wyjścia na dysku.

SReclaimable śledzi pamięć slabową, którą można odzyskać. Pamięć slabowa służy do przechowywania pamięci podręcznej zainicjowanych obiektów, z których często korzysta jądro.

Pierwszym eksperymentem było dostosowanie ustawień pamięci podręcznej, a konkretnie: vm.vfs_cache_pressure i vm.dirty_background_ratio.

Zwiększenie wartości vfs_cache_pressure sprawi, że będziemy częściej odzyskiwać obiekty z pamięci podręcznej, którą można odzyskać. Ma to wpływ na wydajność (zarówno w przypadku braków w pamięci podręcznej, jak i czasu wyszukiwania obiektów, które można zwolnić).

dirty_background_ratio to procent (pamięci pamięci podręcznej stron, która jest brudna) w momencie rozpoczęcia zapisu na dysk w trybie nieblokującym.

Wprowadziliśmy następujące zmiany i obserwowaliśmy ich wpływ na pamięć, slabinfo i cgroups.

vm.vfs_cache_pressure= 100 ==&gt; 10000<br>vm.dirty_background_ratio= 10 ==&gt; 5

Nieninwazyjnym sposobem śledzenia wyników było okresowe uruchamianie zadania cron, które tworzyło migawkę stanu pamięci. Coś w rodzaju:

#!/bin/bash<br>now=`date +%Y-%m-%d.%H:%M`
  # create test dir<br>mkdir -p ~/memtest&nbsp;
# log meminfo<br>sudo cat /proc/meminfo 
  ~/memtest/meminfo_$now
# log slabinfo<br>sudo cat /proc/slabinfo
  ~/memtest/slabinfo_$now&nbsp;
# log cgroups<br>sudo cat /proc/cgroups
  ~/memtest/cgroups_$now&nbsp;

Po zastosowaniu zmian dotyczących obciążenia pamięci podręcznej i kilkugodzinnej obserwacji rozpoczęliśmy analizę. Doszliśmy do wniosku, że zyskaliśmy około 8 GB wolnej pamięci, ale pamięć ta pochodziła bezpośrednio z pamięci podręcznej stron i dysku, z kategorii Active(file) i Inactive(file). Był to rozczarowujący wynik, nie nastąpił znaczący wzrost dostępnej pamięci netto, a ponadto nie wykorzystywaliśmy już tej pamięci w sposób efektywny. Musieliśmy odzyskać pamięć z innego źródła.

Po nieudanej próbie bezpośredniego zwiększenia dostępnej pamięci spróbowaliśmy zmniejszyć konkurencyjne kategorie. Zauważyliśmy, że kategoria pamięci SUnreclaim była duża, w niektórych przypadkach rosnąc do 60 GB w ciągu kilku miesięcy. Kategoria SUnreclaim śledzi pamięć używaną przez system operacyjny na pule obiektów, której nie można odzyskać w warunkach presji pamięciowej. Pierwszym sygnałem problemu była stale rosnąca liczba cgroupów. Spodziewaliśmy się co najwyżej kilkuset cgroupów wynikających z działania naszych procesów w Dockerze, ale widzieliśmy ich SETKI TYSIĘCY. Na szczęście dla nas wygląda na to, że inny inżynier, Roman Gushchin z Facebooka, niedawno znalazł i naprawił ten sam problem na poziomie jądra https://patchwork.kernel.org/cover/10943797/. Stwierdza on:

Podstawowy problem jest dość prosty: każda strona przypisana do cgroup zawiera do niej odwołanie, więc cgroup nie może zostać zwolniona, dopóki nie znikną wszystkie przypisane strony. Jeśli obiekt slab jest aktywnie używany przez inne cgroupy, nie zostanie zwolniony i uniemożliwi zwolnienie pierwotnej cgroup.

Wydawało się, że to właśnie nasz problem, więc z niecierpliwością czekaliśmy na jądro 5.3, aby zweryfikować poprawkę.

Ponownie wykorzystaliśmy skrypt do śledzenia pamięci z eksperymentu dotyczącego obciążenia pamięci podręcznej, ale w przypadku eksperymentu z jądrem chcieliśmy utworzyć grupę kontrolną i eksperymentalną. Odciążyliśmy ruch produkcyjny z 2 szaf serwerowych, a następnie zaktualizowaliśmy jądro do wersji 5.3 na jednej szafie i pozostawiliśmy jądro 5.0 na drugiej. Następnie zrestartowaliśmy obie szafy i ponownie udostępniliśmy je dla ruchu produkcyjnego. Po około tygodniu prześledziliśmy, jak zmieniały się z czasem grupy cgroups i pamięć slab, której nie można odzyskać. Oto wyniki:

W jądrze 5.0.0 obserwuje się nieprzerwany wzrost cgroups, a w ciągu tygodnia przybywa 4 GB pamięci slab, której nie można odzyskać, co daje łącznie 6 GB. Z drugiej strony, w jądrze 5.3.7 codziennie obserwuje się znaczne zmniejszenie cgroups, a wzrost pamięci slab, której nie można odzyskać, jest bardzo powolny. Po tygodniu pamięć typu slab, której nie można odzyskać, wynosi około 2 GB. W nowym jądrze pamięć typu slab, której nie można odzyskać, stabilizuje się na poziomie około 4 GB, nawet po kilku miesiącach pracy.

Ostatecznym problemem, który chcieliśmy rozwiązać, była utrata wydajności naszych serwerów gier w miarę upływu czasu. Wynikało to z mniejszej ilości dostępnej pamięci, co było spowodowane stale rosnącą ilością pamięci slab, której nie można odzyskać. Jak więc wpłynęło na wydajność serwerów to, że dzięki poprawce jądra udało nam się to w miarę opanować?

Po lewej stronie widać nasz problem – stały spadek liczby gier na serwerze, co bardzo obciążało naszą infrastrukturę. Około marca 2020 r. wdrożyliśmy wersję jądra 5.3 w całej naszej globalnej flocie i do tej pory udało nam się utrzymać wysoką wydajność serwerów. Ogromne podziękowania dla Romana Gushchina za poprawkę jądra oraz Andre Tran za pomoc w zbadaniu problemu i wdrożeniu poprawki.

Ani firma Roblox Corporation, ani niniejszy blog nie promują ani nie wspierają żadnej firmy ani usługi. Ponadto nie udziela się żadnych gwarancji ani obietnic dotyczących dokładności, wiarygodności lub kompletności informacji zawartych w niniejszym blogu.

Ten wpis na blogu został pierwotnie opublikowany na blogu technicznym Roblox.