Steal: kto kradnie czas procesora w wirtualnych maszynach

Steal: kto kradnie czas procesora w wirtualnych maszynach

Cześć! Chciałbym prostym językiem opowiedzieć o mechanice powstawania steal wewnątrz maszyn wirtualnych oraz o niektórych nieoczywistych artefaktach, które udało nam się ustalić podczas jego badania, w które musiałem się zagłębić jako dyrektor techniczny platformy chmurowej. Mail.ru Cloud Solutions. Platforma działa na KVM.

Czas steal CPU to czas, w którym maszyna wirtualna nie otrzymuje zasobów procesora do swojej realizacji. Czas ten jest liczony tylko w systemach operacyjnych gościa w środowiskach wirtualizacji. Powody, dla których te przydzielone zasoby znikają, są dość niejasne. Ale postanowiliśmy to zbadać, nawet przeprowadziliśmy szereg eksperymentów. Nie to, że teraz wszystko wiemy o steal, ale kilka interesujących rzeczy chętnie teraz opowiemy.

1. Co to jest steal

Tak więc, steal to metryka wskazująca na brak czasu procesora dla procesów wewnątrz maszyny wirtualnej. Jak opisano w łatce jądra KVM, steal to czas, w którym hypervisor wykonuje inne procesy na systemie operacyjnym hosta, chociaż postawił proces maszyny wirtualnej w kolejce do wykonania. Zatem, steal jest liczony jako różnica między czasem, kiedy proces jest gotowy do wykonania, a czasem, kiedy procesowi przydzielono czas procesora.

Metrykę steal jądro maszyny wirtualnej otrzymuje od hypervisora. Przy tym hypervisor nie precyzuje, jakie dokładnie inne procesy wykonuje, po prostu "jestem zajęty, nie mogę ci poświęcić czasu". W KVM wsparcie dla liczenia steal dodano w łatkach.Dwa kluczowe punkty tutaj to:

  • Maszyna wirtualna dowiaduje się o steal od hypervisora. To znaczy, z punktu widzenia strat, dla procesów na samej wirtualce jest to pomiar pośredni, który może być podatny na różne zniekształcenia.
  • Hypervisor nie dzieli się z wirtualką informacją o tym, czym innym jest zajęty — najważniejsze, że nie poświęca jej czasu. Z tego powodu sama wirtualka nie może wykryć zniekształceń w wskaźniku steal, które można by ocenić na podstawie charakteru konkurujących procesów.

2. Co wpływa na steal

2.1. Obliczanie steal

W istocie, steal oblicza się mniej więcej tak, jak zwykły czas wykorzystania procesora. Informacji o tym, jak oblicza się wykorzystanie, jest niewiele. Może dlatego, że większość uważa to za oczywiste. Ale tutaj też mogą być pułapki. Aby zaznajomić się z tym procesem, można poczytać. artykuł Brendanna Gregga: dowiesz się o wielu szczegółach przy obliczaniu wykorzystania oraz o sytuacjach, w których to obliczenie może być błędne z następujących powodów:

  • Przegrzanie procesora, w wyniku którego są pomijane takty.
  • Włączanie/wyłączanie turbo boosta, co zmienia częstotliwość taktowania procesora.
  • Zmiana długości kwantu czasowego, występująca przy użyciu technologii oszczędzania energii procesora, na przykład SpeedStep.
  • Problem z obliczeniem średniej: ocenienie wykorzystania na poziomie 80% przez jedną minutę może ukryć krótkotrwały wzrost do 100%.
  • Cykliczne blokowanie (spin lock) powoduje, że procesor jest wykorzystywany, ale proces użytkownika nie widzi postępu w swoim wykonaniu. W rezultacie, obliczone wykorzystanie procesora przez proces będzie wynosić 100%, chociaż fizyczny czas procesora nie będzie używany przez proces.

Nie znalazłem artykułu, który opisuje podobne obliczenia dla steal (jeśli wiecie — podzielcie się w komentarzach). Ale według źródeł, mechanizm obliczeń jest taki sam jak dla wykorzystania. Po prostu w jądrze dodawany jest dodatkowy licznik, bezpośrednio dla procesu KVM (procesu maszyny wirtualnej), który zlicza czas, jaki proces KVM spędza w oczekiwaniu na czas procesora. Licznik zbiera informacje o procesorze z jego specyfikacji i sprawdza, czy wszystkie jego tyki były wykorzystywane przez proces wirtualny. Jeśli tak, to uznajemy, że procesor zajmował się tylko procesem maszyny wirtualnej. W przeciwnym razie informujemy, że procesor zajmował się czymś innym, wystąpił steal.

Proces obliczania steal podlega tym samym problemom, co zwykłe obliczanie wykorzystania. Nie można powiedzieć, że takie problemy występują często, ale wyglądają demotywująco.

2.2. Typy wirtualizacji na KVM

Ogólnie rzecz biorąc, istnieją trzy typy wirtualizacji, które są obsługiwane przez KVM. Typ wirtualizacji może wpływać na mechanizm powstawania steal.

Transmisja. W tym przypadku praca systemu operacyjnego maszyny wirtualnej z fizycznymi urządzeniami hiperwizora przebiega mniej więcej tak:

  1. Gościnny system operacyjny wysyła swojemu urządzeniu gościowemu polecenie.
  2. Sterownik urządzenia gościowego przyjmuje polecenie, formułuje zapytanie do BIOS-u urządzenia i wysyła je do hiperwizora.
  3. Proces hyperwizora przetwarza polecenie na polecenie dla urządzenia fizycznego, czyniąc je, między innymi, bardziej bezpiecznym.
  4. Sterownik urządzenia fizycznego przyjmuje zmodyfikowane polecenie i przesyła je już do samego urządzenia fizycznego.
  5. Wyniki wykonania poleceń wracają tą samą drogą.

Zaletą translacji jest to, że pozwala emulować dowolne urządzenie i nie wymaga specjalnej konfiguracji jądra systemu operacyjnego. Jednak za to trzeba płacić, przede wszystkim, wydajnością.

Wirtualizacja sprzętowa. W tym przypadku urządzenie na poziomie sprzętu rozumie polecenia z systemu operacyjnego. Jest to najszybszy i najlepszy sposób. Niestety, nie jest on wspierany przez wszystkie urządzenia fizyczne, hipernadzorcze i systemy operacyjne gości. Obecnie główne urządzenia, które wspierają wirtualizację sprzętową, to procesory.

Pseudowirtualizacja (paravirtualization). Najbardziej rozpowszechniona forma wirtualizacji urządzeń w KVM i w ogóle najczęściej stosowany tryb wirtualizacji dla systemów operacyjnych gości. Jego cechą jest to, że interakcja z niektórymi podsystemami hipernadzorcy (na przykład ze stosem sieciowym lub dyskowym) lub alokacja stron pamięci odbywa się za pomocą API hipernadzorcy, bez translacji niskopoziomowych poleceń. Wadą tej metody wirtualizacji jest konieczność modyfikacji jądra systemu operacyjnego gościa, aby mogło ono interagować z hipernadzorcą za pomocą tego API. Jednak zazwyczaj problem ten rozwiązuje się poprzez instalację specjalnych sterowników na systemie operacyjnym gościa. W KVM to API nazywa się API virtio.

W przypadku pseudowirtualizacji w porównaniu do translacji, droga do urządzenia fizycznego znacznie się skraca dzięki bezpośredniemu przesyłaniu poleceń z wirtualnej maszyny do procesu hipernadzorcy na hoście. Pozwala to przyspieszyć wykonanie wszystkich instrukcji wewnątrz wirtualnej maszyny. W KVM odpowiada za to API virtio, które działa tylko dla określonych urządzeń, takich jak adapter sieciowy lub dyskowy. Dlatego w wirtualnych maszynach instalowane są sterowniki virtio.

Odwróconą stroną takiego przyspieszenia jest to, że nie wszystkie procesy, które są wykonywane wewnątrz maszyny wirtualnej, pozostają w niej. Tworzy to pewne efekty uboczne, które mogą prowadzić do pojawienia się na steal. Szczegółowe zbadanie tej kwestii polecam rozpocząć od Interfejs API dla wirtualnego I/O: virtio.

2.3. „Sprawiedliwe” planowanie

Maszyna wirtualna na hypervisorze jest w rzeczywistości zwykłym procesem, który podlega zasadom planowania (przydzielania zasobów między procesami) w jądrze Linux, dlatego rozważmy to bardziej szczegółowo.

W systemie Linux używa się tzw. CFS, Completely Fair Scheduler, który od jądra 2.6.23 stał się domyślnym menedżerem. Aby zrozumieć ten algorytm, można przeczytać Linux Kernel Architecture lub źródła. Istota CFS polega na przydzielaniu czasu procesora między procesami w zależności od długości ich wykonywania. Im więcej czasu procesora wymaga proces, tym mniej go otrzymuje. Gwarantuje to „sprawiedliwe” wykonanie wszystkich procesów — aby jeden proces nie zajmował wszystkich procesorów na stałe, i inne procesy również mogły być wykonywane.

Czasami taka paradygma prowadzi do interesujących artefaktów. Wieloletni użytkownicy Linuxa z pewnością pamiętają zawieszanie zwykłego edytora tekstu na pulpicie podczas uruchamiania zasobochłonnych aplikacji, takich jak kompilatory. Działo się tak, ponieważ zadania mniej wymagające zasobów z aplikacji desktopowych konkurowały z zadaniami intensywnie wykorzystującymi zasoby, takimi jak kompilator. CFS uznaje, że to jest niesprawiedliwe, dlatego okresowo zatrzymuje edytor tekstu i daje procesorowi czas na przetworzenie zadań kompilatora. Naprawiono to za pomocą mechanizmu sched_autogroup, ale pozostało wiele innych specyfiki przydzielania czasu procesora między zadaniami. Właściwie, to opowieść nie o tym, jak źle jest w CFS, ale próba zwrócenia uwagi na to, że „sprawiedliwy” podział czasu procesora nie jest trywialnym zadaniem.

Kolejnym ważnym punktem w schedulerze jest preemption. Jest to potrzebne, aby usunąć zbyt obciążony proces z procesora i dać szansę innym. Proces usuwania nazywa się przełączaniem kontekstu, czyli context switching. Przy tym zachowywany jest cały kontekst zadania: stan stosu, rejestry i inne, po czym proces jest wysyłany w stan oczekiwania, a na jego miejsce wchodzi inny. Jest to kosztowna operacja dla systemu operacyjnego i używana jest rzadko, ale nie ma w niej nic złego. Częste przełączanie kontekstu może sygnalizować problem w systemie operacyjnym, ale zazwyczaj odbywa się ono w sposób ciągły i nie wskazuje na nic szczególnego.

Tak długi opis jest potrzebny, aby wyjaśnić jeden fakt: im więcej zasobów CPU stara się wykorzystać proces w uczciwym schedulerze Linux, tym szybciej zostanie zatrzymany, aby inne procesy mogły także pracować. Czy to jest dobre, czy nie — to skomplikowane pytanie, na które odpowiedź różni się w zależności od obciążenia. W systemie Windows do niedawna scheduler był zaprojektowany z myślą o priorytetowym przetwarzaniu aplikacji desktopowych, przez co mogły zawieszać się procesy w tle. W Sun Solaris było pięć różnych klas schedulerów. Gdy uruchomiono wirtualizację, dodano szósty, scheduler sprawiedliwego podziału, ponieważ wcześniejsze pięć działało z wirtualizacją Solaris Zones w sposób niewłaściwy. Szczegółowe zbadanie tego zagadnienia polecam rozpocząć od książek takich jak Solaris Internals: Solaris 10 and OpenSolaris Kernel Architecture lub Understanding the Linux Kernel.

2.4. Jak monitorować steal?

Monitorowanie steal wewnątrz maszyny wirtualnej, jak i każdej innej metryki procesora, jest proste: można korzystać z dowolnego narzędzia do zbierania metryk CPU. Ważne, aby wirtualka była na Linuxie. Windows jakoś nie udostępnia takiej informacji swoim użytkownikom. 🙁

Steal: kto kradnie czas procesora w wirtualnych maszynach
Wynik polecenia top: szczegółowy podział obciążenia CPU, w skrajnej prawej kolumnie — steal

Problemy pojawiają się przy próbie uzyskania tych informacji z hipernadzorcy. Można spróbować przewidzieć steal na maszynie hosta, na przykład, na podstawie parametru Load Average (LA) — uśrednionej wartości liczby procesów oczekujących w kolejce na wykonanie. Metoda obliczania tego parametru nie jest łatwa, ale ogólnie, jeśli znormalizowane w stosunku do liczby wątków CPU LA wynosi więcej niż 1, to oznacza, że serwer z Linuxem jest w jakiś sposób przeciążony.

Czego więc oczekują te wszystkie procesy? Odpowiedź jest oczywista — procesora. Ale odpowiedź nie jest do końca poprawna, ponieważ czasami procesor jest wolny, a LA szaleje. Przypomnijcie sobie, jak odłącza się NFS i jak w tym czasie rośnie LA. Podobnie może być z dyskiem i innymi urządzeniami wejścia/wyjścia. W rzeczywistości procesy mogą oczekiwać zakończenia dowolnej blokady, zarówno fizycznej, związanej z urządzeniem wejścia/wyjścia, jak i logicznej, na przykład mutexa. Do tego dochodzą blokady na poziomie sprzętu (np. odpowiedzi od dysku) lub logiki (tzw. prymitywy blokujące, obejmujące wiele bytów, takich jak mutex adaptive i spin, semafory, zmienne warunkowe, rw locks, ipc locks…).

Inną cechą LA jest to, że jest obliczane jako średnia wartość dla systemu operacyjnego. Na przykład, 100 procesów konkuruje o jeden plik, wówczas LA=50. Taka wysoka wartość, wydaje się, mówi, że system operacyjny ma problemy. Ale dla jednego źle napisanego kodu może to być normalny stan, chociaż dla niego jest źle, a inne procesy w systemie operacyjnym nie cierpią.

Z powodu tej uśrednionej wartości (i to przez co najmniej minutę), określenie czegokolwiek na podstawie wskaźnika LA — jest dość nieprzyjemnym zadaniem, z bardzo nieokreślonymi wynikami w konkretnych przypadkach. Jeśli spróbujesz to zrozumieć, odkryjesz, że w artykułach na Wikipedii i innych dostępnych źródłach opisane są tylko najprostsze przypadki, bez głębszego wyjaśnienia procesu. Wszystkich zainteresowanych odsyłam, znowu, tutaj, do Brendanna Gregga  — dalej w linkach. Kto ma lenistwo do angielskiego — tłumaczenie jego popularnego artykułu o LA.

3. Efekty specjalne

Teraz skupmy się na głównych przypadkach występowania steal, z którymi się spotykaliśmy. Opowiem, jak wynikają one z powyższego i jak odnoszą się do wskaźników na hypervisorze.

Recykling. Najprostsze i najczęstsze: hypervisor jest przeładowany. Rzeczywiście, wiele uruchomionych wirtualek, duże zużycie procesora w ich wnętrzu, duża konkurencja, utylizacja po LA powyżej 1 (w odniesieniu do wątków procesora). Wewnątrz wszystkich wirtualek wszystko zamula. Steal, przekazywany z hypervisora, również rośnie, należy rozdzielić obciążenie lub kogoś wyłączyć. Ogólnie rzecz biorąc, wszystko jasne i zrozumiałe.

Parawirtualizacja kontra samotne instancje. Na hipervizorze znajduje się jedna jedyna maszyna wirtualna, która zużywa niewielką część jego zasobów, ale generuje dużą obciążenie wejścia/wyjścia, na przykład na dysku. I w niej zjawia się niewielki steal, do 10% (jak pokazują przeprowadzone eksperymenty).

Interesujący przypadek. Steal pojawia się tutaj z powodu blokad na poziomie półwirtualizowanych sterowników. Wewnątrz maszyny wirtualnej generowane jest przerwanie, które jest obsługiwane przez sterownik i trafia do hipervizora. Z powodu przetwarzania przerwania w hipervizorze dla maszyny wirtualnej wygląda to jak wysłane żądanie, jest gotowa do wykonania i czeka na procesor, ale nie otrzymuje czasu procesora. Maszyna wirtualna myśli, że ten czas został skradziony.

Dzieje się to w momencie wysyłania bufora, który trafia do przestrzeni jądra hipervizora, i zaczynamy go oczekiwać. Chociaż z punktu widzenia maszyny wirtualnej powinien on natychmiast wrócić. W związku z tym, według algorytmu obliczania steal, ten czas jest uznawany za skradziony. Prawdopodobnie w tej sytuacji mogą występować inne mechanizmy (na przykład obsługa innych wywołań systemowych), ale nie powinny one znacznie się różnić.

Harmonogram przeciwko wysokozadaniowym maszynom wirtualnym. Kiedy jedna maszyna wirtualna doświadcza większego steala niż inne, jest to spowodowane harmonogramem. Im silniej proces obciąża procesor, tym szybciej harmonogram go usunie, aby inne mogły również pracować. Jeśli maszyna wirtualna zużywa niewiele, prawie nie zauważy steala: jej proces uczciwie siedział i czekał, więc powinno mu się dać więcej czasu. Jeśli maszyna wirtualna generuje maksymalne obciążenie na wszystkich swoich rdzeniach, jest częściej usuwana z procesora i stara się nie dawać jej dużo czasu.

Jeszcze gorzej, gdy procesy wewnątrz maszyny wirtualnej próbują zdobyć więcej czasu procesora, ponieważ nie radzą sobie z przetwarzaniem danych. Wtedy system operacyjny na hipervizorze, dzięki uczciwej optymalizacji, będzie przyznawał coraz mniej czasu procesora. Ten proces zachodzi lawinowo, a steal rośnie do nieba, podczas gdy inne maszyny wirtualne mogą go prawie nie zauważać. Im więcej rdzeni, tym gorzej dla maszyny, która jest w trudnej sytuacji. Krótko mówiąc, najbardziej cierpią wysokoobciążone maszyny wirtualne z wieloma rdzeniami.

Niski LA, ale jest steal. Jeżeli LA wynosi około 0,7 (to znaczy, hipervizor wydaje się być niedoładowany), ale w poszczególnych maszynach wirtualnych obserwuje się steal:

  • Już opisany powyżej model z parawirtualizacją. Wirtualna maszyna może otrzymywać metryki wskazujące na steal, chociaż hypervisor działa prawidłowo. Na podstawie naszych eksperymentów, taki poziom steal nie przekracza 10% i nie powinien mieć istotnego wpływu na wydajność aplikacji działających w wirtualnej maszynie.
  • Parametr LA jest błędnie obliczany. Dokładniej, w każdym konkretnym momencie jest liczony prawidłowo, ale przy uśrednieniu za minutę wychodzi zaniżony. Na przykład, jeśli jedna wirtualna maszyna na jedną trzecią hypervisora wykorzystuje wszystkie swoje procesory dokładnie przez pół minuty, to LA za minutę na hypervisorze wyniesie 0,15; cztery takie wirtualne maszyny działające jednocześnie dadzą 0,6. A to, że przez pół minuty każda z nich miała ogromny steal na poziomie 25% w zakresie LA, nie zostanie już uwzględnione.
  • Znów, z powodu schedulera, który zdecydował, że ktoś zbyt wiele konsumuje i niech ten ktoś poczeka. A ja w tym czasie przełączę kontekst, obsłużę przerwania i zajmę się innymi ważnymi kwestiami systemowymi. W rezultacie niektóre wirtualne maszyny nie widzą żadnych problemów, a inne doświadczają poważnej degradacji wydajności.

4. Inne zniekształcenia

Jest jeszcze milion powodów do zniekształceń uczciwego przydziału czasu procesora w wirtualnej maszynie. Na przykład, problemy w obliczeniach wprowadzają hyper-threading i NUMA. Ostatecznie mylą one wybór rdzenia do wykonania procesu, ponieważ scheduler wykorzystuje współczynniki - wagi, które przy przełączaniu kontekstu sprawiają, że obliczenia są jeszcze trudniejsze.

Mogą występować zniekształcenia z powodu technologii takich jak turbo boost lub odwrotnie, trybu oszczędzania energii, które przy obliczaniu wykorzystania mogą sztucznie zwiększać lub zmniejszać częstotliwość lub nawet kwant czasu na serwerze. Włączenie turbo boost zmniejsza wydajność jednego wątku procesora z powodu zwiększenia wydajności innego. W tym momencie informacja o aktualnej częstotliwości procesora nie jest przekazywana wirtualnej maszynie, która sądzi, że jej czas jest kradziony (na przykład, żądała 2 GHz, a otrzymała połowę mniej).

W sumie, może być wiele przyczyn zniekształceń. W konkretnym systemie możesz odkryć coś jeszcze. Zacznij najlepiej od książek, do których podałem linki powyżej, oraz zbierania statystyk z hypervisorów za pomocą narzędzi takich jak perf, sysdig, systemtap, których jest dziesiątki.

5. Wnioski

  1. Pewna ilość steal może występować z powodu parawirtualizacji i można uznać to za normalne. W internecie piszą, że ta wartość może wynosić 5-10%. Zależy to od aplikacji w wirtualce oraz od tego, jaką generują obciążenie na swoich fizycznych urządzeniach. Ważne jest, aby zwrócić uwagę na to, jak aplikacje funkcjonują w wirtualkach.
  2. Stosunek obciążenia na hypervisorze i steal w wirtualce nie zawsze jest jednoznacznie powiązany, obie oceny steal mogą być błędne w konkretnych sytuacjach przy różnych obciążeniach.
  3. Scheduler źle traktuje procesy, które dużo żądają. Stara się dawać mniej tym, którzy proszą więcej. Duże wirtualki to zło.
  4. Niewielki steal może być normą również bez parawirtualizacji (biorąc pod uwagę obciążenie w wirtualce, szczególności obciążenia sąsiadów, rozkład obciążenia na wątki i inne czynniki).
  5. Jeśli chcesz ustalić steal w konkretnej systemie, musisz badać różne możliwości, zbierać metryki, dokładnie je analizować i przemyśleć, jak równomiernie rozdzielać obciążenie. Od wszelkich przypadków mogą występować odchylenia, które należy potwierdzać eksperymentalnie lub sprawdzać w debuggerze jądra.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster