Fură: cine fură timpul procesorului de la mașinile virtuale

Fură: cine fură timpul procesorului de la mașinile virtuale

Buna ziua! Vreau să vă spun în termeni simpli despre mecanica furtului în interiorul mașinilor virtuale și despre câteva artefacte neevidente pe care am reușit să le aflăm în timpul cercetării sale, în care a trebuit să mă scufund în calitate de director tehnic al unei platforme cloud. Mail.ru Cloud Solutions. Platforma rulează pe KVM.

Timpul de furt al procesorului este timpul în care mașina virtuală nu primește resurse de procesor pentru execuția sa. Acest timp este luat în calcul numai în sistemele de operare invitate din mediile de virtualizare. Motivele unde merg aceste resurse cele mai alocate, ca și în viață, sunt foarte vagi. Dar am decis să ne dăm seama și chiar am efectuat o serie de experimente. Nu este că acum știm totul despre furt, dar vă vom spune ceva interesant acum.

1. Ce este furtul

Deci, furtul este o metrică care indică o lipsă de timp de procesor pentru procesele din interiorul unei mașini virtuale. Așa cum este descris în patch-ul kernel-ului KVMStealth este timpul în care hypervisorul execută alte procese pe sistemul de operare gazdă, chiar dacă a pus în coadă procesul mașinii virtuale pentru execuție. Adică, furtul este calculat ca diferența dintre timpul în care procesul este gata de executat și momentul în care procesului i se alocă timpul procesorului.

Nucleul mașinii virtuale primește metrica de furt de la hypervisor. În același timp, hypervisorul nu specifică exact ce alte procese rulează, spune pur și simplu „în timp ce sunt ocupat, nu vă pot oferi timp”. Pe KVM, a fost adăugat suport pentru calculul furtului petice. Există două puncte cheie aici:

  • Mașina virtuală învață despre furtul de la hypervisor. Adică, din punct de vedere al pierderilor, pentru procesele de pe mașina virtuală în sine aceasta este o măsurare indirectă care poate fi supusă diferitelor distorsiuni.
  • Hipervizorul nu împărtășește informații cu mașina virtuală despre ce altceva face - principalul lucru este că nu îi dedică timp. Din această cauză, mașina virtuală în sine nu poate detecta distorsiuni în indicatorul de furt, care ar putea fi evaluate după natura proceselor concurente.

2. Ce afectează furtul

2.1. Fură calculul

În esență, furtul este calculat aproximativ la fel ca timpul normal de utilizare a procesorului. Nu există prea multe informații despre modul în care este luată în considerare reciclarea. Probabil pentru că majoritatea oamenilor consideră această întrebare evidentă. Dar aici există și capcane. Pentru a vă familiariza cu acest proces, puteți citi articol de Brendan Gregg: veți învăța despre o mulțime de nuanțe la calcularea utilizării și despre situațiile în care acest calcul va fi eronat din următoarele motive:

  • Procesorul se supraîncălzește, determinând săritul ciclurilor.
  • Activați/dezactivați turbo boost, care modifică frecvența tacului procesorului.
  • O modificare a duratei intervalului de timp care apare atunci când se utilizează tehnologii de economisire a energiei procesorului, cum ar fi SpeedStep.
  • Problema cu calcularea mediei: estimarea utilizării unui minut la 80% poate ascunde o explozie pe termen scurt de 100%.
  • O blocare de rotație face ca procesorul să fie revendicat, dar procesul utilizatorului nu vede niciun progres în execuția sa. Ca rezultat, utilizarea procesorului calculată de către proces va fi sută la sută, deși procesul nu va consuma fizic timp procesorului.

Nu am găsit un articol care să descrie un calcul similar pentru furt (dacă știți, împărtășiți-l în comentarii). Dar, judecând după codul sursă, mecanismul de calcul este același ca și pentru reciclare. Pur și simplu, se adaugă un alt contor în nucleu, direct pentru procesul KVM (proces de mașină virtuală), care numără durata procesului KVM în așteptarea timpului CPU. Contorul preia informații despre procesor din specificațiile sale și verifică dacă toate bifoanele sale sunt utilizate de procesul mașinii virtuale. Dacă asta este tot, atunci presupunem că procesorul a fost ocupat doar cu procesul mașinii virtuale. În caz contrar, informăm că procesorul făcea altceva, a apărut furtul.

Procesul de numărare a furtului este supus acelorași probleme ca și numărarea obișnuită a reciclării. Ca să nu spun că astfel de probleme apar des, dar par descurajatoare.

2.2. Tipuri de virtualizare pe KVM

În linii mari, există trei tipuri de virtualizare, toate fiind acceptate de KVM. Mecanismul de apariție a furtului poate depinde de tipul de virtualizare.

traducere. În acest caz, funcționarea sistemului de operare a mașinii virtuale cu dispozitive fizice hipervizoare are loc cam așa:

  1. Sistemul de operare invitat trimite o comandă către dispozitivul său invitat.
  2. Driverul de dispozitiv invitat primește comanda, generează o solicitare pentru BIOS-ul dispozitivului și o trimite la hypervisor.
  3. Procesul hypervisor traduce comanda în comandă pentru dispozitivul fizic, făcându-l, printre altele, mai sigur.
  4. Driverul dispozitivului fizic acceptă comanda modificată și o trimite către dispozitivul fizic însuși.
  5. Rezultatele executării comenzilor merg înapoi pe aceeași cale.

Avantajul traducerii este că vă permite să emulați orice dispozitiv și nu necesită pregătire specială a nucleului sistemului de operare. Dar pentru asta trebuie să plătești, în primul rând, în viteză.

Virtualizare hardware. În acest caz, dispozitivul la nivel hardware înțelege comenzile din sistemul de operare. Acesta este cel mai rapid și cel mai bun mod. Dar, din păcate, nu este acceptat de toate dispozitivele fizice, hipervizoarele și sistemele de operare invitate. În prezent, principalele dispozitive care suportă virtualizarea hardware sunt procesoarele.

Paravirtualizarea. Cea mai comună opțiune pentru virtualizarea dispozitivelor pe KVM și, în general, cel mai comun mod de virtualizare pentru sistemele de operare invitate. Particularitatea sa este că lucrul cu unele subsisteme de hypervisor (de exemplu, cu rețeaua sau stiva de discuri) sau alocarea paginilor de memorie are loc folosind API-ul hypervisor, fără a traduce comenzile de nivel scăzut. Dezavantajul acestei metode de virtualizare este că nucleul sistemului de operare invitat trebuie modificat astfel încât să poată comunica cu hypervisorul folosind acest API. Dar acest lucru este de obicei rezolvat prin instalarea de drivere speciale pe sistemul de operare invitat. În KVM, acest API este numit virtio API.

Cu paravirtualizarea, în comparație cu difuzarea, calea către dispozitivul fizic este redusă semnificativ prin trimiterea de comenzi direct de la mașina virtuală la procesul de hipervizor de pe gazdă. Acest lucru vă permite să accelerați execuția tuturor instrucțiunilor din interiorul mașinii virtuale. În KVM, acest lucru este realizat de virtio API, care funcționează numai pentru anumite dispozitive, cum ar fi un adaptor de rețea sau de disc. Acesta este motivul pentru care driverele virtio sunt instalate în interiorul mașinilor virtuale.

Dezavantajul acestei accelerări este că nu toate procesele care rulează în interiorul mașinii virtuale rămân în interiorul acesteia. Acest lucru creează unele efecte speciale care pot duce la apariția la furt. Recomand să începeți un studiu detaliat al acestei probleme cu Un API pentru I/O virtuale: virtio.

2.3. Programare „corectă”.

O mașină virtuală pe un hipervizor este, de fapt, un proces obișnuit care respectă legile de planificare (distribuirea resurselor între procese) în nucleul Linux, așa că haideți să aruncăm o privire mai atentă la el.

Linux folosește așa-numitul CFS, Completely Fair Scheduler, care a devenit programul implicit de la kernel-ul 2.6.23. Pentru a înțelege acest algoritm, puteți citi Linux Kernel Architecture sau codul sursă. Esența CFS este de a distribui timpul procesorului între procese în funcție de durata execuției acestora. Cu cât un proces necesită mai mult timp CPU, cu atât primește mai puțin timp CPU. Acest lucru asigură că toate procesele sunt executate „în mod corect” - astfel încât un proces nu ocupă în mod constant toate procesoarele, iar alte procese pot fi executate și ele.

Uneori, această paradigmă duce la artefacte interesante. Utilizatorii Linux de lungă durată își amintesc probabil de înghețarea unui editor de text obișnuit pe un desktop în timp ce rulează aplicații care necesită mult resurse, cum ar fi un compilator. Acest lucru s-a întâmplat deoarece sarcinile care nu necesită multă resurse din aplicațiile desktop au concurat cu sarcinile care necesită mult resurse, cum ar fi compilatorul. CFS consideră că acest lucru este nedrept, așa că oprește periodic editorul de text și lasă procesorul să se ocupe de sarcinile compilatorului. Acest lucru a fost corectat folosind un mecanism sched_autogroup, dar au rămas multe alte caracteristici ale distribuției timpului procesorului între sarcini. De fapt, aceasta nu este o poveste despre cât de rău este totul în CFS, ci o încercare de a atrage atenția asupra faptului că distribuirea „echitabilă” a timpului procesorului nu este cea mai banală sarcină.

Un alt punct important în planificator este preempțiunea. Acest lucru este necesar pentru a elimina procesul de chicotire din procesor și pentru a-i lăsa pe alții să lucreze. Procesul de ejectare se numește comutare de context. În acest caz, se păstrează întregul context al sarcinii: starea stivei, registre etc., după care procesul este trimis în așteptare, iar altul îi ia locul. Aceasta este o operațiune costisitoare pentru sistemul de operare și este folosită rar, dar nu este nimic în neregulă cu ea. Comutarea frecventă a contextului poate indica o problemă în sistemul de operare, dar de obicei este continuă și nu indică nimic anume.

Este nevoie de o poveste atât de lungă pentru a explica un fapt: cu cât un proces încearcă să consume mai multe resurse de procesor într-un planificator Linux onest, cu atât va fi oprit mai repede, astfel încât alte procese să poată funcționa și ele. Dacă acest lucru este corect sau nu, este o întrebare complexă care poate fi rezolvată diferit sub sarcini diferite. În Windows, până de curând, programatorul era axat pe procesarea prioritară a aplicațiilor desktop, ceea ce ar putea cauza înghețarea proceselor de fundal. Sun Solaris avea cinci clase diferite de programatori. Când am lansat virtualizarea, am adăugat un al șaselea, Programator de cotă echitabilă, deoarece cele cinci anterioare nu au funcționat adecvat cu virtualizarea zonelor Solaris. Recomand să începeți un studiu detaliat al acestei probleme cu cărți precum Solaris Internals: Solaris 10 și OpenSolaris Kernel Architecture sau Înțelegerea kernelului Linux.

2.4. Cum să monitorizezi furtul?

Monitorizarea furtului în interiorul unei mașini virtuale, ca orice altă măsurătoare de procesor, este simplă: puteți utiliza orice instrument de măsurători de procesor. Principalul lucru este că mașina virtuală este pe Linux. Din anumite motive, Windows nu furnizează aceste informații utilizatorilor săi. 🙁

Fură: cine fură timpul procesorului de la mașinile virtuale
Ieșirea comenzii de sus: detalii despre încărcarea procesorului, în coloana din dreapta - fură

Dificultatea apare atunci când încercați să obțineți aceste informații de la hipervizor. Puteți încerca să preziceți furtul pe mașina gazdă, de exemplu, folosind parametrul Load Average (LA) - valoarea medie a numărului de procese care așteaptă în coada de execuție. Metoda de calcul a acestui parametru nu este simplă, dar, în general, dacă LA normalizat prin numărul de fire de execuție a procesorului este mai mare de 1, aceasta indică faptul că serverul Linux este supraîncărcat cu ceva.

Ce așteaptă toate aceste procese? Răspunsul evident este procesorul. Dar răspunsul nu este pe deplin corect, pentru că uneori procesorul este gratuit, dar LA iese din scară. Tine minte cum cade NFS și cum crește LA. Același lucru se poate întâmpla cu un disc și alte dispozitive de intrare/ieșire. Dar, de fapt, procesele pot aștepta sfârșitul oricărei blocări, fie fizice, asociate cu un dispozitiv I/O, fie logice, cum ar fi un mutex. Aceasta include și blocarea la nivel de hardware (același răspuns de la disc) sau logica (așa-numitele primitive de blocare, care include o grămadă de entități, mutex adaptive și spin, semafore, variabile de condiție, blocări rw, blocaje ipc ...).

O altă caracteristică a LA este că este considerată ca o medie a sistemului de operare. De exemplu, 100 de procese concurează pentru un fișier, iar apoi LA=50. O valoare atât de mare ar părea să indice că sistemul de operare este prost. Dar pentru alt cod scris strâmb, aceasta poate fi o stare normală, în ciuda faptului că numai acesta este rău, iar alte procese din sistemul de operare nu au de suferit.

Din cauza acestei medii (și în nu mai puțin de un minut), a determina orice prin indicatorul LA nu este sarcina cea mai plină de satisfacții, cu rezultate foarte incerte în cazuri specifice. Dacă încercați să vă dați seama, veți descoperi că articolele de pe Wikipedia și alte resurse disponibile descriu doar cazurile cele mai simple, fără o explicație profundă a procesului. Trimit din nou tuturor celor interesați, aici la Brendan Gregg  - urmați linkurile de mai jos. Cui îi este prea lene să vorbească engleza - traducere a articolului său popular despre LA.

3. Efecte speciale

Acum să ne uităm la principalele cazuri de furt pe care le-am întâlnit. Vă voi spune cum decurg din toate cele de mai sus și cum se raportează la indicatorii de pe hypervisor.

Reciclare. Cel mai simplu și mai comun: hipervizorul a fost refolosit. Într-adevăr, există o mulțime de mașini virtuale care rulează, consum mare de procesor în interiorul lor, multă concurență, utilizarea LA este mai mare de 1 (normalizată de firele procesorului). Totul din interiorul tuturor mașinilor virtuale încetinește. Furtul transmis de la hipervizor este, de asemenea, în creștere, este necesar să redistribuiți sarcina sau să opriți pe cineva. În general, totul este logic și de înțeles.

Paravirtualizarea vs. Instanțe unice. Există o singură mașină virtuală pe hypervisor; aceasta consumă o mică parte din ea, dar produce o încărcare mare de I/O, de exemplu pe disc. Și de undeva apare un mic furt în el, până la 10% (după cum arată mai multe experimente).

Cazul este interesant. Steal apare aici tocmai din cauza blocării la nivelul șoferilor paravirtualizați. O întrerupere este creată în interiorul mașinii virtuale, procesată de șofer și trimisă la hypervisor. Datorită tratării întreruperilor pe hypervisor, pentru mașina virtuală arată ca o cerere trimisă, este gata de execuție și așteaptă procesorul, dar nu i se acordă timp procesor. Fata virtuală crede că această dată a fost furată.

Acest lucru se întâmplă în momentul în care bufferul este trimis, acesta intră în spațiul kernel al hypervisorului și începem să îl așteptăm. Deși, din punctul de vedere al mașinii virtuale, ar trebui să revină imediat. Prin urmare, conform algoritmului de calcul a furtului, această dată este considerată furată. Cel mai probabil, în această situație pot exista și alte mecanisme (de exemplu, procesarea altor apeluri sistem), dar nu ar trebui să fie mult diferite.

Scheduler versus mașini virtuale foarte încărcate. Când o mașină virtuală suferă de furt mai mult decât altele, acest lucru se datorează planificatorului. Cu cât un proces încarcă mai mult procesorul, cu atât mai repede programatorul îl va da afară, astfel încât și celelalte să poată funcționa. Dacă mașina virtuală consumă puțin, cu greu va vedea furat: procesul său a stat și a așteptat sincer, trebuie să îi acordăm mai mult timp. Dacă o mașină virtuală produce sarcina maximă pe toate nucleele sale, este adesea scoasă din procesor și încearcă să nu-i acorde mult timp.

Este și mai rău atunci când procesele din interiorul mașinii virtuale încearcă să obțină mai mult procesor, deoarece nu pot face față procesării datelor. Apoi sistemul de operare de pe hypervisor, datorită optimizării oneste, va oferi din ce în ce mai puțin timp de procesor. Acest proces are loc ca o avalanșă și furtul saltă în cer, deși alte mașini virtuale pot observa cu greu acest lucru. Și cu cât sunt mai multe nuclee, cu atât mașina afectată este mai rău. Pe scurt, mașinile virtuale foarte încărcate cu multe nuclee suferă cel mai mult.

LA scăzut, dar există furt. Dacă LA este de aproximativ 0,7 (adică hipervizorul pare să fie subîncărcat), dar se observă furt în interiorul mașinilor virtuale individuale:

  • Opțiunea cu paravirtualizare deja descrisă mai sus. Mașina virtuală poate primi valori care indică furtul, deși hypervisorul este bine. Conform rezultatelor experimentelor noastre, această opțiune de furt nu depășește 10% și nu ar trebui să aibă un impact semnificativ asupra performanței aplicațiilor din interiorul mașinii virtuale.
  • Parametrul LA este calculat incorect. Mai exact, în fiecare moment specific este calculat corect, dar când se face media peste un minut se dovedește a fi subestimat. De exemplu, dacă o mașină virtuală pe treime din hypervisor își consumă toate procesoarele exact o jumătate de minut, atunci LA pe minut pe hypervisor va fi 0,15; patru astfel de mașini virtuale care lucrează simultan vor da 0,6. Iar faptul că pentru o jumătate de minut pe fiecare dintre ele a fost un furt sălbatic la 25% conform indicatorului LA nu mai poate fi scos.
  • Din nou, din cauza programatorului care a decis că cineva mănâncă prea mult și l-a lăsat pe cineva să aștepte. Între timp, voi schimba contextul, voi gestiona întreruperile și voi avea grijă de alte lucruri importante ale sistemului. Drept urmare, unele mașini virtuale nu văd nicio problemă, în timp ce altele se confruntă cu o degradare gravă a performanței.

4. Alte distorsiuni

Există încă un milion de motive pentru a distorsiona revenirea echitabilă a timpului procesorului pe o mașină virtuală. De exemplu, hyperthreadingul și NUMA introduc dificultăți în calcule. Ele confundă complet alegerea nucleului pentru executarea procesului, deoarece planificatorul folosește coeficienți - ponderi, care fac calculul și mai dificil la schimbarea contextului.

Există distorsiuni datorate tehnologiilor precum turbo boost sau, dimpotrivă, modul de economisire a energiei, care, la calcularea utilizării, poate crește sau micșora artificial frecvența sau chiar intervalul de timp de pe server. Activarea turbo boost reduce performanța unui thread de procesor datorită creșterii performanței altuia. În acest moment, informațiile despre frecvența actuală a procesorului nu sunt transmise către mașina virtuală și crede că cineva îi fură timpul (de exemplu, a cerut 2 GHz, dar a primit jumătate).

În general, pot exista multe motive pentru denaturare. Este posibil să găsiți altceva pe un anumit sistem. Este mai bine să începeți cu cărțile la care am dat link-uri mai sus și să preluați statistici de la hypervisor folosind utilități precum perf, sysdig, systemtap, dintre care zeci de.

5. Concluzii

  1. O anumită cantitate de furt poate apărea din cauza paravirtualizării și poate fi considerată normală. Ei scriu pe Internet că această valoare poate fi de 5-10%. Depinde de aplicațiile din interiorul mașinii virtuale și de încărcarea pe care o pune pe dispozitivele sale fizice. Aici este important să acordați atenție modului în care se simt aplicațiile în interiorul mașinilor virtuale.
  2. Raportul dintre sarcina de pe hypervisor și furtul din interiorul mașinii virtuale nu sunt întotdeauna în mod clar legate între ele; ambele estimări ale furtului pot fi eronate în situații specifice sub sarcini diferite.
  3. Planificatorul are o atitudine proastă față de procesele care solicită mult. El încearcă să dea mai puțin celor care cer mai mult. Mașinile virtuale mari sunt rele.
  4. Un pic de furt poate fi norma chiar și fără paravirtualizare (ținând cont de încărcarea din interiorul mașinii virtuale, de caracteristicile încărcăturii vecinilor, de distribuția sarcinii pe fire și de alți factori).
  5. Dacă doriți să vă dați seama de furtul într-un anumit sistem, trebuie să explorați diverse opțiuni, să colectați valori, să le analizați cu atenție și să vă gândiți la modul de distribuire uniformă a încărcăturii. Sunt posibile abateri de la orice caz, care trebuie confirmate experimental sau analizate în depanatorul nucleului.

Sursa: www.habr.com

Adauga un comentariu