ProHoster > Blog > Administrácia > Vytvorenie ďalšieho plánovača kube s vlastnou sadou pravidiel plánovania
Vytvorenie ďalšieho plánovača kube s vlastnou sadou pravidiel plánovania
Kube-scheduler je integrálnou súčasťou Kubernetes, ktorá je zodpovedná za plánovanie modulov medzi uzlami v súlade so špecifikovanými politikami. Počas prevádzky klastra Kubernetes často nemusíme premýšľať o tom, ktoré politiky sa používajú na plánovanie modulov, pretože sada pravidiel predvoleného plánovača kube je vhodná pre väčšinu každodenných úloh. Sú však situácie, kedy je pre nás dôležité doladiť proces prideľovania modulov a existujú dva spôsoby, ako túto úlohu splniť:
Vytvorte plánovač kube s vlastnou sadou pravidiel
Napíšte si vlastný plánovač a naučte ho pracovať s požiadavkami servera API
V tomto článku popíšem implementáciu prvého bodu na vyriešenie problému nerovnomerného rozvrhnutia ohnísk na jednom z našich projektov.
Krátky úvod do fungovania plánovača kube
Za zmienku stojí najmä skutočnosť, že kube-scheduler nie je zodpovedný za priame plánovanie modulov - je zodpovedný iba za určenie uzla, na ktorý sa modul umiestni. Inými slovami, výsledkom práce plánovača kube je názov uzla, ktorý vráti serveru API na žiadosť o plánovanie, a tam jeho práca končí.
Najprv kube-scheduler zostaví zoznam uzlov, na ktorých je možné naplánovať pod v súlade s politikami predikátov. Ďalej každý uzol z tohto zoznamu získa určitý počet bodov v súlade s politikami priorít. V dôsledku toho sa vyberie uzol s maximálnym počtom bodov. Ak existujú uzly, ktoré majú rovnaké maximálne skóre, vyberie sa náhodne. Zoznam a popis politík predikátov (filtrovanie) a priorít (bodovanie) nájdete v dokumentáciu.
Popis tela problému
Napriek veľkému počtu rôznych klastrov Kubernetes udržiavaných v spoločnosti Nixys sme sa prvýkrát stretli s problémom plánovania modulov len nedávno, keď jeden z našich projektov potreboval spustiť veľké množstvo periodických úloh (~ 100 entít CronJob). Aby sme čo najviac zjednodušili popis problému, uvedieme ako príklad jednu mikroslužbu, v rámci ktorej sa raz za minútu spúšťa úloha cron, čím dochádza k určitej záťaži CPU. Na spustenie úlohy cron boli pridelené tri uzly s absolútne identickými charakteristikami (24 vCPU na každom).
Zároveň nie je možné s presnosťou povedať, ako dlho bude CronJob trvať, kým sa vykoná, pretože objem vstupných údajov sa neustále mení. V priemere počas normálnej prevádzky plánovača kube každý uzol spúšťa 3-4 inštancie úloh, ktoré vytvárajú ~20-30% zaťaženia CPU každého uzla:
Samotný problém je v tom, že niekedy sa pody úloh cron prestali plánovať na jednom z troch uzlov. To znamená, že v určitom čase nebol pre jeden z uzlov naplánovaný ani jeden modul, zatiaľ čo na ostatných dvoch uzloch bežalo 6-8 kópií úlohy, čo vytváralo ~40-60% zaťaženia CPU:
Problém sa opakoval s absolútne náhodnou frekvenciou a príležitostne koreloval s momentom uvedenia novej verzie kódu.
Zvýšením úrovne protokolovania plánovača kube na úroveň 10 (-v=10) sme začali zaznamenávať, koľko bodov každý uzol získal počas procesu hodnotenia. Počas normálnej plánovacej operácie je možné v protokoloch vidieť nasledujúce informácie:
Tie. súdiac podľa informácií získaných z protokolov, každý z uzlov dosiahol rovnaký počet konečných bodov a na plánovanie bol vybraný jeden náhodný. V čase problematického plánovania vyzerali protokoly takto:
Z čoho je vidieť, že jeden z uzlov dosiahol menej konečných bodov ako ostatné, a preto sa plánovalo len pre dva uzly, ktoré dosiahli maximálne skóre. Definitívne sme sa teda presvedčili, že problém spočíva práve v plánovaní strukov.
Ďalší algoritmus na vyriešenie problému bol pre nás zrejmý - analyzovať protokoly, pochopiť, s akou prioritou uzol nezískal body, a v prípade potreby upraviť politiky predvoleného plánovača kube. Tu však čelíme dvom závažným problémom:
Pri maximálnej úrovni ťažby (10) sa odrážajú body získané len za niektoré priority. Vo vyššie uvedenom úryvku protokolov môžete vidieť, že pre všetky priority vyjadrené v protokoloch dosahujú uzly rovnaký počet bodov v normálnom a problémovom plánovaní, ale konečný výsledok v prípade plánovania problémov je odlišný. Môžeme teda dospieť k záveru, že pri niektorých prioritách sa bodovanie vyskytuje „v zákulisí“ a nemáme spôsob, ako pochopiť, za ktorú prioritu uzol nezískal body. Tento problém sme podrobne opísali v otázka Úložisko Kubernetes na Github. V čase písania tohto článku bola od vývojárov prijatá odpoveď, že podpora protokolovania bude pridaná v aktualizáciách Kubernetes v1.15,1.16, 1.17 a XNUMX.
Neexistuje jednoduchý spôsob, ako pochopiť, s ktorou špecifickou sadou politík kube-scheduler momentálne pracuje. Áno, v dokumentáciu tento zoznam je uvedený, ale neobsahuje informácie o tom, aké konkrétne váhy sú priradené každej z prioritných politík. Váhy alebo úpravu pravidiel predvoleného plánovača kube môžete zobraziť iba v zdrojové kódy.
Stojí za zmienku, že raz sa nám podarilo zaznamenať, že uzol nezískal body podľa politiky ImageLocalityPriority, ktorá prideľuje body uzlu, ak už má obraz potrebný na spustenie aplikácie. To znamená, že v čase uvedenia novej verzie aplikácie sa úloha cron podarilo spustiť na dvoch uzloch, pričom sa do nich stiahol nový obrázok z registra dockerov, a teda dva uzly získali vyššie konečné skóre v porovnaní s tretím .
Ako som písal vyššie, v protokoloch nevidíme informácie o vyhodnotení politiky ImageLocalityPriority, takže aby sme skontrolovali náš predpoklad, prehodili sme obrázok s novou verziou aplikácie na tretí uzol, po ktorom plánovanie fungovalo správne. . Práve kvôli politike ImageLocalityPriority bol problém s plánovaním pozorovaný pomerne zriedkavo, častejšie bol spojený s niečím iným. Vzhľadom na to, že sme nemohli úplne odladiť každú z politík v zozname priorít predvoleného plánovača kube, potrebovali sme flexibilnú správu politík plánovania pod.
Vyhlásenie o probléme
Chceli sme, aby riešenie problému bolo čo najkonkrétnejšie, to znamená, že hlavné entity Kubernetes (tu máme na mysli predvolený plánovač kube) by mali zostať nezmenené. Nechceli sme riešiť problém na jednom mieste a vytvárať ho na inom. Dospeli sme teda k dvom možnostiam riešenia problému, ktoré boli oznámené v úvode článku - vytvorenie dodatočného plánovača alebo napísanie vlastného. Hlavnou požiadavkou na plánovanie úloh cronu je rovnomerné rozloženie záťaže medzi tri uzly. Táto požiadavka môže byť splnená existujúcimi politikami plánovača kube, takže na vyriešenie nášho problému nemá zmysel písať svoj vlastný plánovač.
Pokyny na vytvorenie a nasadenie dodatočného plánovača kube sú popísané v dokumentáciu. Zdalo sa nám však, že entita Deployment nestačí na zabezpečenie odolnosti voči poruchám pri prevádzke takej kritickej služby, akou je kube-scheduler, preto sme sa rozhodli nasadiť nový kube-scheduler ako Static Pod, ktorý bude monitorovaný priamo od Kubeleta. Preto máme pre nový plánovač kube nasledujúce požiadavky:
Služba musí byť nasadená ako statický modul na všetkých hlavných serveroch klastra
V prípade, že aktívny modul s plánovačom kube nie je k dispozícii, musí byť poskytnutá odolnosť voči chybám
Hlavnou prioritou pri plánovaní by mal byť počet dostupných zdrojov v uzle (LeastRequestedPriority)
Implementačné riešenia
Okamžite stojí za zmienku, že všetku prácu vykonáme v Kubernetes v1.14.7, pretože Toto je verzia, ktorá bola použitá v projekte. Začnime napísaním manifestu pre náš nový plánovač kube. Vezmime si za základ predvolený manifest (/etc/kubernetes/manifests/kube-scheduler.yaml) a privedieme ho do nasledujúcej podoby:
Zmenil sa názov modulu a kontajnera na kube-scheduler-cron
Špecifikovalo použitie portov 10151 a 10159, ako bola definovaná možnosť hostNetwork: true a nemôžeme použiť rovnaké porty ako predvolený plánovač kube (10251 a 10259)
Pomocou parametra --config sme špecifikovali konfiguračný súbor, pomocou ktorého sa má služba spustiť
Konfigurované pripojenie konfiguračného súboru (scheduler-custom.conf) a súboru politiky plánovania (scheduler-custom-policy-config.json) z hostiteľa
Nezabudnite, že náš plánovač kube bude potrebovať práva podobné predvolenému. Upravte jeho rolu klastra:
Teraz si povedzme, čo by mal obsahovať konfiguračný súbor a súbor politiky plánovania:
Konfiguračný súbor (scheduler-custom.conf)
Ak chcete získať predvolenú konfiguráciu plánovača kube, musíte použiť parameter --write-config-to z dokumentáciu. Výslednú konfiguráciu umiestnime do súboru /etc/kubernetes/scheduler-custom.conf a zredukujeme ju do nasledujúcej podoby:
Nastavili sme schedulerName na názov našej služby kube-scheduler-cron.
V parametri lockObjectName musíte tiež nastaviť názov našej služby a uistiť sa, že parameter leaderElect nastavte na hodnotu true (ak máte jeden hlavný uzol, môžete ho nastaviť na hodnotu false).
V parametri zadáte cestu k súboru s popisom politík plánovania algorithmSource.
Stojí za to sa bližšie pozrieť na druhý bod, kde upravujeme parametre pre kľúč leaderElection. Na zabezpečenie odolnosti voči chybám sme povolili (leaderElect) proces výberu vedúceho (hlavného) medzi modulmi nášho plánovača kube pomocou jediného koncového bodu (resourceLock) s názvom kube-scheduler-cron (lockObjectName) v priestore názvov systému kube (lockObjectNamespace). Ako Kubernetes zabezpečuje vysokú dostupnosť hlavných komponentov (vrátane plánovača kube) nájdete v článok.
Súbor zásad plánovania (scheduler-custom-policy-config.json)
Ako som už písal, môžeme zistiť, s ktorými konkrétnymi politikami predvolený plánovač kube pracuje, iba analýzou jeho kódu. To znamená, že nemôžeme získať súbor so zásadami plánovania pre predvolený plánovač kube rovnakým spôsobom ako konfiguračný súbor. Popíšme si zásady plánovania, ktoré nás zaujímajú v súbore /etc/kubernetes/scheduler-custom-policy-config.json takto:
Preto kube-scheduler najprv zostaví zoznam uzlov, do ktorých možno naplánovať pod podľa zásady GeneralPredicates (ktorá zahŕňa sadu zásad PodFitsResources, PodFitsHostPorts, HostName a MatchNodeSelector). Potom sa každý uzol vyhodnotí v súlade so súborom politík v poli priorít. Na splnenie podmienok našej úlohy sme považovali takýto súbor politík za optimálne riešenie. Dovoľte mi pripomenúť, že súbor pravidiel s ich podrobným popisom je dostupný v dokumentáciu. Na splnenie svojej úlohy môžete jednoducho zmeniť množinu použitých politík a priradiť im vhodné váhy.
Manifest nového plánovača kube, ktorý sme vytvorili na začiatku kapitoly, nazvime kube-scheduler-custom.yaml a umiestnime ho do nasledujúcej cesty /etc/kubernetes/manifests na tri hlavné uzly. Ak je všetko vykonané správne, Kubelet spustí modul na každom uzle a v protokoloch nášho nového plánovača kube uvidíme informáciu, že náš súbor zásad bol úspešne použitý:
Creating scheduler from configuration: {{ } [{GeneralPredicates <nil>}] [{ServiceSpreadingPriority 1 <nil>} {EqualPriority 1 <nil>} {LeastRequestedPriority 1 <nil>} {NodePreferAvoidPodsPriority 10000 <nil>} {NodeAffinityPriority 1 <nil>}] [] 10 false}
Registering predicate: GeneralPredicates
Predicate type GeneralPredicates already registered, reusing.
Registering priority: ServiceSpreadingPriority
Priority type ServiceSpreadingPriority already registered, reusing.
Registering priority: EqualPriority
Priority type EqualPriority already registered, reusing.
Registering priority: LeastRequestedPriority
Priority type LeastRequestedPriority already registered, reusing.
Registering priority: NodePreferAvoidPodsPriority
Priority type NodePreferAvoidPodsPriority already registered, reusing.
Registering priority: NodeAffinityPriority
Priority type NodeAffinityPriority already registered, reusing.
Creating scheduler with fit predicates 'map[GeneralPredicates:{}]' and priority functions 'map[EqualPriority:{} LeastRequestedPriority:{} NodeAffinityPriority:{} NodePreferAvoidPodsPriority:{} ServiceSpreadingPriority:{}]'
Teraz už zostáva len uviesť v špecifikácii nášho CronJob, že všetky požiadavky na plánovanie jeho modulov by mal spracovať náš nový plánovač kube:
Nakoniec sme dostali ďalší plánovač kube s jedinečnou sadou plánovacích politík, ktorých prácu monitoruje priamo kubelet. Okrem toho sme nastavili voľbu nového lídra medzi modulmi nášho kube-plánovača pre prípad, že by starý líder z nejakého dôvodu nebol dostupný.
Bežné aplikácie a služby sa naďalej plánujú prostredníctvom predvoleného plánovača kube a všetky úlohy cron boli úplne prenesené do nového. Zaťaženie vytvorené úlohami cron je teraz rovnomerne rozdelené medzi všetky uzly. Vzhľadom na to, že väčšina úloh cron sa vykonáva na rovnakých uzloch ako hlavné aplikácie projektu, výrazne sa tým znížilo riziko pohybu modulov v dôsledku nedostatku zdrojov. Po zavedení dodatočného plánovača kube už nevznikali problémy s nerovnomerným plánovaním úloh cron.