Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

27. aprill konverentsil Streik-2019, DevOps sektsiooni raames, esitati ettekande „Kuberneteses automaatne skaleerimine ja ressursside haldamine“. Ettekandes räägitakse, kuidas K8s saab tagada rakenduste kõrge kättesaadavuse ja maksimaalse jõudluse.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Traditsiooniliselt oleme uhked esitama ettekande video vaatamiseks (44 minutit, oluliselt informatiivsem kui artikkel) ja põhijooned tekstivormis. Alustame!

Arutame ettekande teemat sõna-sõnalt ja alustame lõpust.

Kubernetes

Kujutage ette, et meil on serveris Docker-konteinerid. Miks? Et tagada korduvus ja isoleeritus, mis omakorda võimaldab lihtsat ja kvaliteetset juurutamist, CI/CD. Meil on palju selliseid konteineritega masinaid.

Mida me sellisel juhul Kuberneteselt saame?

  1. Me lõpetame nende masinate üle mõtlemise ja hakkame töötama „pilve“ konteinerite klastriga või pod‘idega (konteinerigruppidega).
  2. Veelgi enam, me ei mõtle isegi erilistele pod’idele, vaid haldame veelgiosuuremaid gruppe. Tooted kõrgtaseme primitivid annavad meile võimaluse öelda, et on mall teatud töökoormuse käivitamiseks, ja siin on vajalik nende instantside arv. Kui hiljem muudame malli — muudavad ka kõik instantsid.
  3. Käesoleva abil deklaratiivne API meie kirjeldame maailma struktuuri » (YAML-is), mis luuakse Kubernetes'i abil, selle asemel, et täita konkreetse käskude järjestust. Ja jälle: muudatuste korral muutub ka selle tegelik kuvamine.

Ressursside haldamine

CPU

Kujutage ette, et serveris käivitame nginx, php-fpm ja mysql. Neil teenustel on tegelikult veel rohkem töötavaid protsesse, igaühel neist on vaja arvutusressursse:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)
(slaidil olevad numbrid — „papagoi“, iga protsessi arvutusvõimsuse abstraktne vajadus)

Selle mugavaks käsitlemiseks on mõistlik rühmitada protsessid gruppidesse (näiteks kõik nginx protsessid ühte gruppi „nginx“). Lihtsaim ja ilmsem viis selleks on paigutada iga grupp konteinerisse:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Enne jätkamist tuleb meeles pidada, mis on konteiner (Linuxis). Nende teke sai võimalikuks kolme peamise funktsiooni tõttu tuumas, mis on juba ammu rakendatud: võimed, namespaces ja cgroups. Edasisele arengule aitasid kaasa ka teised tehnoloogiad (sealhulgas mugavad „shellid“ nagu Docker):

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Käesolevas ettekandes huvitab meid ainult cgroups, kuna see just kontrollgrupid on konteinerite (nt Docker) funktsioonide osa, mis haldab ressursse. Protsessid, mis on ühendatud gruppidesse, nagu soovisime, ongi need kontrollgrupid.

Naaseme nende protsesside CPU vajaduste juurde ning nüüd — protsessigruppide vajadustele:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)
(kordan, et kõik numbrid on abstraktne ressursivajaduse väljendus)

Samuti on CPU-l teatud lõplik ressurss (näites on see 1000), mille kõigile ei pruugi piisata (kõigi gruppide vajaduste summa — 150+850+460=1460). Mis sel juhul juhtub?

Tuuma hakkab ressursse jaotama ja teeb seda "ausalt", jagades igale grupile sama hulga ressursse. Kuid esimeses olukorras on neid rohkem kui vajalik (333>150), seega jääb ülejääk (333-150=183) reservi, mis jagatakse samuti võrdselt kahe teise konteineri vahel:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Lõpuks: esimesel konteineril oli ressursse piisavalt, teisel — palju ei piisanud, kolmandal — veidi ei piisanud. Nii on tulemuseks "aus" planeerija LinuxisCFS. Tema tööd saab reguleerida, määrates kaalu iga konteineri jaoks. Näiteks selliselt:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Vaata olukorda, kus teise konteineri (php-fpm) ressursid on piiratud. Kõik konteineri ressursid jagatakse protsesside vahel ühtlaselt. Seetõttu töötab master-protsess hästi, kuid kõik worker'id peavad hädalikult leppima vähem kui poolega vajalikust:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Nii töötab CFS planeerija. Kaalud, mille me konteineritele määrame, nimetame edaspidi päringuteks. Miks just nii — vt lähemalt.

Vaata olukorda ka teiselt poolt. Nagu öeldakse, kõik teed viivad Rooma, ja arvuti puhul — CPU juurde. CPU on üks, ülesandeid palju — vajalik on reguleerimine. Lihtsaim viis ressursside juhtimiseks on 'reguleeringuline': anname ühele protsessile fikseeritud aja CPU juurde pääsemiseks, siis järgmisele jne.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Seda lähenemist nimetatakse rangeks kvoteerimiseks (hard limiting). Salvestame selle lihtsalt kui piirid. Siiski, kui jagada kõigile konteineritele piirid, tekib probleem: mysql sõitis teel ja ühel hetkel lõppes selle vajadus CPU järele, kuid kõik teised protsessid peavad ootama, kuni CPU on vabastatud.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Naaseme Linuxi tuuma juurde ja selle suhtlemisse CPU-ga — üldine pilt on järgmine:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Cgroup'il on kaks seadistust — põhimõtteliselt kaks lihtsat 'nuppu', mis määravad:

  1. konteineri kaal (request'id) — see on shares;
  2. CPU üldise töötamise ajast konteineri ülesannete täitmiseks (limiidid) — see on quota.

Kuidas CPU-d mõõta?

On erinevaid viise:

  1. Mis see on papagei, keegi ei tea — peab iga kord kokku leppima.
  2. Protsendid on arusaadavamad, kuid suhtelised: 50% 4 tuumaga serverist ja 20 tuumaga serverist on täiesti erinevad asjad.
  3. Võib kasutada juba mainitud kaalu, mida Linux tunneb, kuid need on samuti suhtelised.
  4. Kõige adekvaatsem valik on mõõta arvutusressursse sekundites. St. protsessoritööaja sekundid võrreldes tegeliku aja sekunditega: anti 1 sekund protsessoritööd 1 tegelikus sekundis — see on täielik CPU tuum.

Et asi veel lihtsamaks teha, hakati mõõtma otse tuumade, mõistes nende all CPU aega võrreldes tegeliku ajaga. Kuna Linux mõistab kaalu, mitte protsessoritööd/tuumasid, oli vajalik mehanism ühe teisest teisendamiseks.

Vaatame lihtsat näidet serverist, kus on 3 CPU tuuma, kus kolmele pod'ile on valitud kaalukategooriad (500, 1000 ja 1500), mis muudetakse kergesti vastavaks nendele eraldatud tuumadele (0,5, 1 ja 1,5).

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Kui võtta teine server, kus tuumade arv on kahekordne (6), ja jagada sinna samad pod'id, on tuumade jaotus kergesti arvutatav lihtsa korrutamise abil 2-ga (1, 2 ja 3 vastavalt). Kuid oluline hetk saabub siis, kui sellel serveril ilmub neljas pod, mille kaal on mugavuse huvides 3000. See võtab endale osa CPU ressursidest (pool tuumadest), samas kui teiste pod'ide ressursid arvestatakse ümber (väheneb poole võrra):

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Kubernetes ja CPU ressursid

Kubernetes'is mõõdetakse CPU ressursse milliarhades, st põhikaaluks on 0,001 tuuma. (Seda samme nimetatakse Linuxi/cgroups terminoloogias CPU share, kuigi täpsemalt öeldes on 1000 milliarhad = 1024 CPU jagu.) K8s jälgib, et serveris ei oleks rohkem pod'e kui CPU ressursid kõigi pod'ide kaalude kogusummale.

Kuidas see toimub? Kui server lisatakse Kubernetes'i klastrisse, teatatakse, mitu CPU tuuma on sellel saadaval. Uue pod'i loomisel teab Kubernetes'i planeerija, kui palju tuumi see pod vajab. Niisiis määratakse pod serverisse, kus on piisavalt tuumi.

Mis juhtub, kui ei request on määratud (st pod'il ei ole määratud vajalike tuumade arvu)? Vaatame, kuidas Kubernetes üldiselt ressursse arvutab.

Pod'il saab määrata nii request'id (CFS planeerija) kui ka piirangud (kas meenutate foori?):

  • Kui need on määratud ühesugustena, määratakse pod'ile QoS-klass garanteeritud. See tähendab, et antud hulga tuumade kättesaadavus on garanteeritud.
  • Kui request on väiksem, siis QoS-klass on burstable. See tähendab, et me eeldame, et pod kasutab näiteks alati 1 tuuma, kuid see väärtus ei ole tema jaoks piirang: mõnikord pod võib kasutada ka rohkem (kui serveris on sellele vabu ressursse).
  • On veel QoS-klass parim pingutus — sinna kuuluvad need pod'id, mille jaoks request'i ei ole määratud. Ressursse antakse neile viimases järjekorras.

Mälu

Mälu puhul on olukord sarnane, kuid natuke erinev — need ressursid on oma olemuselt erinevad. Üldiselt on analoogia järgmine:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Vaadakem, kuidas mälus request'id ellu viiakse. Oletame, et pod'id elavad serveris, muutes tarbitavat mälu, kuni üks neist on nii suur, et mälu lõpeb. Sellisel juhul tuleb OOM killer ja tapab suurima protsessi:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Kuid see ei ole alati vastuvõetav, seetõttu on võimalus reguleerida, millised protsessid on meie jaoks olulised ja ei tohiks ära mõõda. Selleks kasutatakse parameetrit oom_score_adj.

Naaseme CPU QoS klasside juurde ja teeme analoogia väärtustega oom_score_adj, mis määravad pod'ide prioriteedid mälutarbimise osas:

  • Madalaim oom_score_adj väärtus pod'il on -998, mis tähendab, et selline pod tuleks tapmist edasi lükata, see garanteeritud.
  • Kõrgeim väärtus on 1000, see parim pingutus, need pod'id tapetakse kõige varem.
  • Ülejäänud väärtuste arvutamiseks (burstable) on olemas valem, mille sisu seisneb selles, et mida rohkem pod on ressursse tellinud, seda väiksem on tõenäosus, et teda tapetakse.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Teine "nupp" — limit_in_bytes — piiride jaoks. Siin on kõik lihtsam: me määrame lihtsalt maksimaalse mäluhulga ja siin (erinevalt CPU-st) ei ole küsimust, milles seda (mälu) mõõta.

Kokku

Iga pod'ile Kuberneteses määratakse requests ja limits — nii CPU kui ka mälumõõturite parameetrid:

  1. Kubernetesi planeerija töötab requests'i alusel, jaotades pod'id serverite vahel;
  2. kõikide parameetrite alusel määratakse pod'i QoS-klass;
  3. CPU requests'i alusel arvutatakse suhtelised kaalud;
  4. CPU requests'i alusel konfigureeritakse CFS-plaane;
  5. mälurequestide põhjal konfigureeritakse OOM tapja;
  6. CPU limitide alusel konfigureeritakse "foor";
  7. mälulimitide alusel seadistatakse cgroup'i piirang.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Kokkuvõttes vastab see pilt kõigile küsimustele, kuidas toimub peamine ressursihaldus Kuberneteses.

Automaatne skaleerimine

K8s kluster-automaatskaalija

Kujutame ette, et kogu kluster on juba hõivatud ja peab olema loodud uus pod. Seni kuni pod ei saa ilmuda, on see staatuses Ootel. Et see ikkagi ilmuks, saame liita uue serveri klusteriga või… installida cluster-automaatskaalija, mis teeb selle meie eest: tellib virtuaalse masina pilveteenuse pakkujalt (API päringu kaudu) ja liidab selle klusteriga, pärast mida pod lisatakse.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

See ongi Kubernetesi klustere automaatne skaleerimine, mis meie kogemuse kohaselt töötab suurepäraselt. Kuid nagu alati, on siin oma nüansid...

Kuna me klastrit suurendasime, oli kõik hästi, aga mis juhtub, kui klaster hakkab vabastama? Проблема в том, что мигрировать pod’ы (для освобождения хостов) очень технически сложно и дорого по ресурсам. В Kubernetes работает совсем другой подход.

Vaatame klastrit, kus on 3 serverit ja Deployment. Seal on 6 pod'i: praegu on need igaühes 2. Kui mingil põhjusel tahame ühe serveri välja lülitada. Selleks kasutame käsku kubectl drain, mis:

  • keelab uute pod'ide saatmise sellele serverile;
  • eemaldab serveris olemasolevad pod'id.

Kuna Kubernetes jälgib pod'ide arvu (6), siis lihtsalt loob need teistele sõlmedele, kuid mitte välja lülitatud, kuna see on juba märgitud kui mitte saadaval uute pod'ide paigutamiseks. See on Kubernetes'i põhiprintsiip.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

siiski on siin üks nüanss. Sarnases olukorras StatefulSet'i (või Deployment'i asemel) puhul on tegevused erinevad. Nüüd on meil juba stateful-rakendus — näiteks kolm pod'i MongoDB-ga, millest ühel on tekkinud mingi probleem (andmed on rikutud või mõni muu viga, mis ei võimalda pod'il korralikult käivituda). Ja me otsustame jälle ühe serveri välja lülitada. Mis siis juhtub?

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

MongoDB võiks suretuks, kuna tal on vajalik kvorum: kolmest installeeritud klastrist peab vähemalt kaks toimima. Kuid seda ei juhtu — tänu PodDisruptionBudget. See parameeter määrab minimaalset vajalikku töötavate pod'ide arvu. Teades, et üks MongoDB pod'idest ei tööta, ja nähes, et MongoDB jaoks on PodDisruptionBudget seadistatud minAvailable: 2, Kubernetes ei luba pod'i eemaldada.

Kokkuvõtteks: et pod'ide liigutamine (tegelikult nende taastamine) klastris töötaks õigesti, tuleb seadistada PodDisruptionBudget.

Horiontaalne skaleerimine

Vaatame teist olukorda. On rakendus, mis on käivitatud kui Deployment Kuberneteses. Selle pod'ides (näiteks kolme) on kasutajaliiklus ja me mõõdame seal mingit mõõdikku (ütleme, CPU-koormust). Kui koormus suureneb, paneme seda graafikult tähele ja suurendame pod'ide arvu, et jagada päringuid.

Tänapäeval ei pea Kuberneteses seda käsitsi tegema: seadistatakse automaatne pod'ide arvu suurendamine/vähenemine sõltuvalt mõõdetud koormuse väärtustest.

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Siin on peamised küsimused, mida täpselt mõõta ja kuidas tõlgendada saadud väärtused (otsustamiseks, et muuta pod'ide arvu). Saame mõõta väga palju:

Automaatskalaarimine ja ressursside haldamine Kuberneteses (ülevaade ja ettekande video)

Kuidas seda tehniliselt teha — mõõtmete kogumine jne — olen ma detailselt kirjeldanud oma ettekandes Jälgimine ja Kubernetes. Peamine soovitus optimaalseid parameetreid valides on katsetage!

On metoodika USE (Kasutuse Saturatsioon ja Vead), mille mõte on järgmine. Millal on mõistlik skaleerida, näiteks php-fpm? Tõenduseks on see, et töörühmad ei lõppe, — see on kasutamine. Ja kui töötajad on otsas ja uusi ühendusi ei võeta vastu — siis on see juba saturatsioon. Mõlemat parameetrit tuleb mõõta ning väärtuste põhjal tuleks skaleerimisega edasi minna.

Kokkuvõtte asemel

Ettekandel on jätk: vertikaalsest skaleerimisest ja sellest, kuidas õigesti ressursse valida. Räägin sellest tulevastes videotes meie YouTube'is — tellige, et mitte midagi maha magada!

Video ja slaidid

Esitluse video (44 minutit):

Vaata videot

Ettekande esitlus:

P.S.

Teised ettekanded Kubernetesest meie blogis:

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster