Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

április 27-án a konferencián Sztrájk 2019, a „DevOps” rész részeként elhangzott az „Autoscaling and Resource Management in Kubernetes” című jelentés. Arról szól, hogyan használhatja a K8-at az alkalmazások magas rendelkezésre állása és a csúcsteljesítmény biztosítása érdekében.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A hagyományoknak megfelelően örömmel mutatjuk be videó a riportról (44 perc, sokkal informatívabb, mint a cikk) és a fő összefoglaló szöveges formában. Megy!

Elemezzük szóról szóra a riport témáját, és kezdjük a végéről.

Kubernetes

Tegyük fel, hogy Docker konténerek vannak a gazdagépünkön. Miért? A megismételhetőség és az elszigeteltség biztosítása érdekében, ami viszont lehetővé teszi az egyszerű és jó telepítést, CI/CD. Sok ilyen konténeres járműünk van.

Mit nyújt ebben az esetben a Kubernetes?

  1. Nem gondolunk többé ezekre a gépekre, és elkezdünk dolgozni a „felhővel” konténerek klasztere vagy hüvelyek (tartálycsoportok).
  2. Sőt, nem is gondolunk az egyes hüvelyekre, hanem többet kezelünkоnagyobb csoportok. Ilyen magas szintű primitívek Lehetővé teszi, hogy azt mondjuk, hogy van egy sablon egy bizonyos munkaterhelés futtatásához, és itt van a futtatásához szükséges példányok száma. Ha utólag módosítjuk a sablont, minden példány megváltozik.
  3. -Val deklaratív API Ahelyett, hogy konkrét parancsok sorozatát hajtanánk végre, leírjuk a „világ szerkezetét” (YAML-ben), amelyet a Kubernetes hoz létre. És még egyszer: ha a leírás megváltozik, a tényleges megjelenítése is megváltozik.

Erőforrás menedzsment

CPU

Futtassunk nginxet, php-fpm-t és mysql-t a szerveren. Ezek a szolgáltatások valójában még több folyamatot fognak futtatni, amelyek mindegyike számítási erőforrásokat igényel:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)
(a számok a dián „papagájok”, az egyes folyamatok absztrakt igénye a számítási teljesítményhez)

Az ezzel való munka megkönnyítése érdekében logikus a folyamatokat csoportokba vonni (például az összes nginx folyamatot egy csoportba, „nginx”). Ennek egy egyszerű és kézenfekvő módja, ha minden csoportot egy tárolóba helyezünk:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A folytatáshoz emlékeznie kell arra, hogy mi a tároló (Linux alatt). Megjelenésüket a kernel három kulcsfontosságú funkciója tette lehetővé, amelyeket meglehetősen régen implementáltak: képességek, névterek и csoportok. És a további fejlesztést más technológiák is elősegítették (beleértve a kényelmes „héjakat”, mint a Docker):

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A jelentés kapcsán csak az érdekel bennünket csoportok, mert a vezérlőcsoportok a konténerek (Docker stb.) funkcióinak erőforrás-kezelést megvalósító részei. A csoportokba egyesített folyamatok, ahogy szerettük volna, kontrollcsoportok.

Térjünk vissza a CPU-követelményekhez ezekhez a folyamatokhoz, és most a folyamatcsoportokhoz:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)
(Ismétlem, hogy minden szám a forrásigény elvont kifejezése)

Ugyanakkor magának a CPU-nak van egy bizonyos véges erőforrása (a példában ez 1000), ami mindenkinek hiányozhat (az összes csoport igényeinek összege 150+850+460=1460). Mi fog történni ebben az esetben?

A kernel elkezdi az erőforrások szétosztását, és ezt „tisztességesen” teszi, minden csoportnak ugyanannyi erőforrást adva. De az első esetben több van belőlük a szükségesnél (333>150), így a felesleg (333-150=183) tartalékban marad, ami szintén egyenlően oszlik el két másik konténer között:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Ennek eredményeként: az első konténerben volt elegendő erőforrás, a másodikban - nem volt elég erőforrás, a harmadikban - nem volt elegendő erőforrás. Ez a tettek eredménye "becsületes" ütemező Linuxban - CFS. Működése a hozzárendelés segítségével állítható súly az egyes konténerek. Például így:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Nézzük meg a második konténer (php-fpm) erőforráshiányának esetét. Minden konténer erőforrás egyenlően van elosztva a folyamatok között. Ennek eredményeként a fő folyamat jól működik, de minden dolgozó lelassul, és kevesebb mint felét kapja meg a szükségesnek:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Így működik a CFS ütemező. A továbbiakban a konténerekhez hozzárendelt súlyokat fogjuk nevezni kéréseket. Miért van ez így - lásd tovább.

Nézzük az egész helyzetet a másik oldalról. Tudniillik minden út Rómába vezet, számítógép esetén pedig a CPU-hoz. Egy CPU, sok feladat – közlekedési lámpára van szüksége. Az erőforrások kezelésének legegyszerűbb módja a közlekedési lámpa: az egyik folyamatnak fix hozzáférési időt adtak a CPU-hoz, majd a következőt stb.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Ezt a megközelítést kemény kvótáknak nevezik (kemény korlátozás). Emlékezzünk rá egyszerűen úgy korlátok. Ha azonban az összes konténerre kiosztja a korlátokat, akkor probléma adódik: a mysql az úton haladt, és egy ponton megszűnt a CPU-igénye, de az összes többi folyamat kénytelen várni, amíg a CPU tétlen.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Térjünk vissza a Linux kernelhez és annak a CPU-val való interakciójához – az összkép a következő:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A cgroupnak két beállítása van – lényegében ez két egyszerű „csavar”, amely lehetővé teszi a következők meghatározását:

  1. a konténer (kérelmek) súlya részvények;
  2. a teljes CPU-idő százaléka a konténerfeladatokon végzett munkához (korlátok). részvény.

Hogyan mérjük a CPU-t?

Különféle módok léteznek:

  1. Milyen papagájok, senki sem tudja – minden alkalommal tárgyalnia kell.
  2. érdeklődés világosabb, de relatív: egy 50 magos és 4 magos szerver 20%-a teljesen más dolog.
  3. Használhatja a már említetteket súly, amit a Linux tud, de ezek is relatívak.
  4. A legmegfelelőbb megoldás a számítási erőforrások mérése másodpercig. Azok. a processzoridő másodperceiben a valós idő másodperceihez viszonyítva: 1 másodpercnyi processzoridőt adtak meg 1 valós másodpercenként – ez egy teljes CPU mag.

Hogy még könnyebb legyen a beszéd, elkezdtek közvetlenül belemérni kernelek, ami náluk ugyanazt a CPU-időt jelenti a valódihoz képest. Mivel a Linux megérti a súlyokat, de nem annyira a CPU-időt/magokat, szükség volt egy mechanizmusra az egyikről a másikra való fordításhoz.

Tekintsünk egy egyszerű példát egy 3 CPU magos szerverre, ahol három pod kap súlyokat (500, 1000 és 1500), amelyek könnyen átalakíthatók a hozzájuk rendelt magok megfelelő részévé (0,5, 1 és 1,5).

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Ha veszünk egy második szervert, ahol kétszer annyi mag lesz (6), és ugyanazokat a podokat helyezzük el oda, akkor a magok eloszlása ​​egyszerűen kiszámítható, egyszerűen megszorozzuk 2-vel (1, 2 és 3). De egy fontos pillanat következik be, amikor egy negyedik pod jelenik meg ezen a szerveren, amelynek súlya a kényelem kedvéért 3000 lesz. Elveszi a CPU erőforrások egy részét (a magok felét), a fennmaradó podoknál pedig újraszámolják (felezik):

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Kubernetes és CPU erőforrások

A Kubernetesben a CPU erőforrásokat általában mérik milliadrax, azaz 0,001 magot vettünk alaptömegnek. (Ugyanezt a Linux/cgroups terminológiában CPU-megosztásnak nevezik, bár pontosabban 1000 millicore = 1024 CPU-megosztás.) A K8s biztosítja, hogy ne helyezzen el több podot a szerveren, mint amennyi CPU-erőforrás az összes pod súlyának összegéhez képest.

Hogyan történik ez? Amikor hozzáad egy kiszolgálót egy Kubernetes-fürthöz, a rendszer jelentésben jelzi, hogy hány CPU-mag áll rendelkezésre. Új pod létrehozásakor a Kubernetes ütemező tudja, hány magra lesz szüksége ennek a podnak. Így a pod olyan szerverhez lesz hozzárendelve, ahol elegendő mag van.

Mi lesz, ha nincs kérés van megadva (azaz a podnak nincs meghatározott számú magja, amire szüksége van)? Nézzük meg, hogy a Kubernetes általában hogyan számolja az erőforrásokat.

Egy podhoz megadhat kéréseket (CFS ütemező) és korlátokat (emlékszik a közlekedési lámpára?):

  • Ha egyenlőnek vannak megadva, akkor a podhoz QoS osztály tartozik Garantált. Ez a mindig rendelkezésre álló magszám garantált.
  • Ha a kérés kisebb, mint a korlát - QoS osztály robbanékony. Azok. Például egy podtól elvárjuk, hogy mindig 1 magot használjon, de ez az érték nem korlátozza azt: néha a pod többet is használhat (ha a kiszolgálónak szabad erőforrásai vannak erre).
  • Van QoS osztály is legjobb erőfeszítés — magában foglalja azokat a hüvelyeket, amelyekre nincs megadva a kérés. Az erőforrásokat utoljára kapják.

Память

A memória esetében a helyzet hasonló, de kissé eltérő - végül is ezeknek az erőforrásoknak a természete más. Általában az analógia a következő:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Nézzük meg, hogyan valósulnak meg a kérések a memóriában. Hagyja, hogy a podok a szerveren éljenek, változtassa a memóriafelhasználást, amíg az egyik olyan nagy lesz, hogy elfogy a memóriája. Ebben az esetben megjelenik az OOM gyilkos, és megöli a legnagyobb folyamatot:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Ez nem mindig felel meg nekünk, így szabályozható, hogy mely folyamatok fontosak számunkra, és azokat nem szabad megölni. Ehhez használja a paramétert oom_score_adj.

Térjünk vissza a CPU QoS osztályaihoz, és rajzoljunk analógiát az oom_score_adj értékekkel, amelyek meghatározzák a podok memóriafogyasztási prioritását:

  • A pod legalacsonyabb oom_score_adj értéke - -998 - azt jelenti, hogy az ilyen podokat utoljára kell megölni. Garantált.
  • A legmagasabb - 1000 - az legjobb erőfeszítés, az ilyen hüvelyeket először megölik.
  • A fennmaradó értékek kiszámításához (robbanékony) van egy képlet, amelynek lényege abban rejlik, hogy minél több erőforrást igényel egy pod, annál kisebb az esélye annak, hogy megöljék.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A második "csavar" - limit_in_bytes - korlátokért. Vele minden egyszerűbb: egyszerűen hozzárendeljük a kiadott memória maximális mennyiségét, és itt (a CPU-val ellentétben) nem kérdés, hogyan mérjük (memória).

Összességében

A Kubernetesben minden pod adott requests и limits - mindkét paraméter a CPU-hoz és a memóriához:

  1. kérések alapján működik a Kubernetes ütemező, amely elosztja a podokat a szerverek között;
  2. az összes paraméter alapján meghatározzák a pod QoS osztályát;
  3. A relatív súlyok kiszámítása a CPU kérések alapján történik;
  4. a CFS ütemező CPU kérések alapján van konfigurálva;
  5. Az OOM killer memóriakérések alapján van konfigurálva;
  6. a „közlekedési lámpa” a CPU-korlátok alapján van konfigurálva;
  7. A memóriakorlátok alapján korlát van beállítva a cgroup számára.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Általánosságban elmondható, hogy ez a kép választ ad arra a kérdésre, hogy az erőforrás-gazdálkodás fő része hogyan történik a Kubernetesben.

Automatikus skálázás

K8s cluster-autoscaler

Képzeljük el, hogy az egész fürt már foglalt, és egy új pod létrehozására van szükség. Bár a pod nem jelenhet meg, állapotában lefagy Függőben levő. Ahhoz, hogy megjelenjen, csatlakoztathatunk egy új szervert a fürthöz vagy... telepíthetjük a cluster-autoscalert, ami ezt megteszi helyettünk: rendelünk egy virtuális gépet a felhőszolgáltatótól (API kéréssel) és csatlakoztatjuk a fürthöz. , ami után a pod hozzáadásra kerül.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Ez a Kubernetes-fürt automatikus skálázása, amely nagyszerűen működik (tapasztalataink szerint). Azonban, mint máshol, itt is van néhány árnyalat...

Amíg növeltük a fürt méretét, minden rendben volt, de mi történik, ha a fürt kezdett szabadulni? A probléma az, hogy a pod-ok migrálása (a gazdagépek felszabadítása érdekében) technikailag nagyon nehéz és költséges az erőforrások szempontjából. A Kubernetes teljesen más megközelítést alkalmaz.

Tekintsünk egy 3 kiszolgálóból álló fürtöt, amely rendelkezik telepítéssel. 6 podja van: most 2 minden szerverhez tartozik. Valamiért le akartuk kapcsolni az egyik szervert. Ehhez a parancsot fogjuk használni kubectl drain, melyik:

  • megtiltja az új podok küldését erre a szerverre;
  • törli a meglévő podokat a szerveren.

Mivel a Kubernetes felelős a hüvelyek számának fenntartásáért (6), egyszerűen újrateremteni fogja más csomópontokon, de nem azon, amelyik le van tiltva, mivel az már meg van jelölve, mint nem elérhető új pod-ok tárolására. Ez a Kubernetes alapvető mechanikája.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Azonban itt is van egy árnyalat. Hasonló helyzetben a StatefulSet esetében (a telepítés helyett) a műveletek eltérőek lesznek. Most már van egy állapottartó alkalmazásunk - például három pod MongoDB-vel, amelyek közül az egyiknek valamilyen probléma van (az adatok megsérültek, vagy egy másik hiba, amely megakadályozza a pod helyes elindulását). És ismét úgy döntünk, hogy letiltunk egy szervert. Mi fog történni?

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

MongoDB tudott meghal, mert határozatképességre van szüksége: egy három telepítésből álló klaszterhez legalább kettőnek működnie kell. Azonban ez nem történik meg - köszönet PodDisruptionBudget. Ez a paraméter határozza meg a minimálisan szükséges működő hüvelyek számát. Tudva, hogy az egyik MongoDB pod már nem működik, és látva, hogy a PodDisruptionBudget be van állítva a MongoDB-hez minAvailable: 2, A Kubernetes nem teszi lehetővé a pod törlését.

A lényeg: ahhoz, hogy a pod-ok mozgatása (és tulajdonképpen az újralétrehozása) megfelelően működjön a fürt felengedésekor, be kell állítani a PodDisruptionBudget-et.

Vízszintes méretezés

Nézzünk egy másik helyzetet. Van egy alkalmazás, amely Telepítésként fut a Kubernetesben. A felhasználói forgalom a podokra érkezik (például három van belőle), és ezekben mérünk egy bizonyos mutatót (mondjuk a CPU terhelést). Amikor a terhelés növekszik, azt ütemezetten rögzítjük, és növeljük a kérések elosztásához szükséges podok számát.

Ma a Kubernetesben ezt nem kell manuálisan megtenni: a hüvelyek számának automatikus növelése/csökkentése van konfigurálva a mért terhelésjelzők értékétől függően.

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

A fő kérdések itt a következők: mit kell pontosan mérni и hogyan kell értelmezni kapott értékek (a hüvelyek számának megváltoztatására vonatkozó döntés meghozatalához). Sokat mérhetsz:

Automatikus skálázás és erőforrás-kezelés a Kubernetesben (áttekintés és videójelentés)

Hogyan kell ezt technikailag megtenni - mérőszámokat gyűjteni stb. — A jelentésben részletesen beszéltem róla Monitoring és Kubernetes. És a fő tanács az optimális paraméterek kiválasztásához kísérlet!

Van USE módszert (Használat telítettsége és hibák), amelynek jelentése a következő. Milyen alapon van értelme skálázni például a php-fpm-et? Az alapján, hogy fogynak a dolgozók, ez az hasznosítás. És ha a munkásoknak vége, és az új kapcsolatokat nem fogadják el, ez már így van telítettség. Mindkét paramétert meg kell mérni, és az értékektől függően skálázást kell végezni.

Ahelyett, hogy egy következtetés

A jelentésnek van egy folytatása: a függőleges skálázásról és a megfelelő erőforrások kiválasztásáról. Erről a következő videókban fogok beszélni a YouTube-unkon - iratkozz fel, hogy ne maradj le!

Videók és diák

Videó az előadásról (44 perc):

A jelentés bemutatása:

PS

További beszámolók a Kubernetesről a blogunkon:

Forrás: will.com

Hozzászólás