ProHoster > Blog > podávání > Vytvoření dalšího plánovače kube s vlastní sadou pravidel plánování
Vytvoření dalšího plánovače kube s vlastní sadou pravidel plánování
Kube-scheduler je nedílnou součástí Kubernetes, která je zodpovědná za plánování podů napříč uzly v souladu se stanovenými zásadami. Během provozu clusteru Kubernetes často nemusíme přemýšlet o tom, které zásady se používají k plánování podů, protože sada zásad výchozího plánovače kube je vhodná pro většinu každodenních úkolů. Existují však situace, kdy je pro nás důležité doladit proces přidělování podů, a existují dva způsoby, jak tento úkol splnit:
Vytvořte plánovač kube s vlastní sadou pravidel
Napište si svůj vlastní plánovač a naučte ho pracovat s požadavky API serveru
V tomto článku popíšu implementaci prvního bodu k vyřešení problému nerovnoměrného rozvržení topenišť na jednom z našich projektů.
Krátký úvod k tomu, jak kube-scheduler funguje
Za zmínku stojí zejména skutečnost, že kube-scheduler není zodpovědný za přímé plánování podů - je zodpovědný pouze za určení uzlu, na který se pod umístí. Jinými slovy, výsledkem práce kube-scheduler je název uzlu, který vrací na API server pro žádost o plánování, a tam jeho práce končí.
Nejprve kube-scheduler sestaví seznam uzlů, na kterých lze naplánovat pod v souladu se zásadami predikátů. Dále každý uzel z tohoto seznamu obdrží určitý počet bodů v souladu se zásadami priorit. V důsledku toho je vybrán uzel s maximálním počtem bodů. Pokud existují uzly, které mají stejné maximální skóre, vybere se náhodně. Seznam a popis politik predikátů (filtrování) a priorit (bodování) naleznete v dokumentace.
Popis těla problému
Navzdory velkému množství různých clusterů Kubernetes spravovaných v Nixys jsme se poprvé setkali s problémem plánování podů teprve nedávno, když jeden z našich projektů potřeboval spouštět velké množství periodických úloh (~100 entit CronJob). Abychom popis problému co nejvíce zjednodušili, uvedeme si jako příklad jednu mikroslužbu, v rámci které se jednou za minutu spouští úloha cron, čímž dochází k určité zátěži CPU. Pro spuštění úlohy cron byly přiděleny tři uzly s naprosto identickými charakteristikami (24 vCPU na každém).
Zároveň nelze s přesností říci, jak dlouho bude CronJob trvat, než se vykoná, protože objem vstupních dat se neustále mění. V průměru během normálního provozu kube-scheduler každý uzel spouští 3-4 instance úloh, které vytvářejí ~20-30 % zátěže CPU každého uzlu:
Samotný problém je v tom, že někdy cron task pody přestaly být plánovány na jednom ze tří uzlů. To znamená, že v určitém okamžiku nebyl pro jeden z uzlů plánován ani jeden modul, zatímco na ostatních dvou uzlech běželo 6–8 kopií úlohy, což vytvářelo ~40–60 % zatížení CPU:
Problém se opakoval s naprosto náhodnou frekvencí a občas koreloval s okamžikem, kdy byla vydána nová verze kódu.
Zvýšením úrovně protokolování plánovače kube na úroveň 10 (-v=10) jsme začali zaznamenávat, kolik bodů získal každý uzel během procesu hodnocení. Během normálního plánování lze v protokolech vidět následující informace:
Tito. soudě podle informací získaných z protokolů získal každý z uzlů stejný počet konečných bodů a pro plánování byl vybrán jeden náhodný. V době problematického plánování vypadaly protokoly takto:
Z čehož je vidět, že jeden z uzlů získal méně konečných bodů než ostatní, a proto bylo plánování provedeno pouze pro dva uzly, které dosáhly maximálního skóre. Definitivně jsme se tedy přesvědčili, že problém spočívá právě v naplánování lusků.
Další algoritmus pro řešení problému byl pro nás zřejmý - analyzovat protokoly, pochopit, jakou prioritou uzel nezískal body, a v případě potřeby upravit zásady výchozího plánovače kube. Zde však čelíme dvěma závažným problémům:
Při maximální úrovni protokolování (10) se odrážejí body získané pouze za některé priority. Ve výše uvedeném úryvku protokolů můžete vidět, že pro všechny priority reflektované v protokolech získávají uzly stejný počet bodů v normálním a problémovém plánování, ale konečný výsledek v případě plánování problémů je jiný. Můžeme tedy dojít k závěru, že u některých priorit se bodování vyskytuje „za scénou“ a my nemáme způsob, jak pochopit, za jakou prioritu uzel body nezískal. Tento problém jsme podrobně popsali v otázka Repozitář Kubernetes na Github. V době psaní tohoto článku byla od vývojářů obdržena odpověď, že podpora protokolování bude přidána v aktualizacích Kubernetes v1.15,1.16, 1.17 a XNUMX.
Neexistuje snadný způsob, jak pochopit, se kterou konkrétní sadou zásad kube-scheduler aktuálně pracuje. Ano, v dokumentace tento seznam je uveden, ale neobsahuje informace o tom, jaké konkrétní váhy jsou přiřazeny každé z prioritních politik. Váhy nebo úpravy zásad výchozího plánovače kube můžete zobrazit pouze v zdrojové kódy.
Stojí za zmínku, že jakmile jsme byli schopni zaznamenat, že uzel nezískal body podle zásady ImageLocalityPriority, která přiděluje body uzlu, pokud již má obrázek nezbytný ke spuštění aplikace. To znamená, že v době, kdy byla spuštěna nová verze aplikace, se úloha cron podařilo spustit na dvou uzlech a stáhnout do nich nový obraz z registru dockerů, takže dva uzly získaly vyšší konečné skóre ve srovnání s třetím uzlem. .
Jak jsem psal výše, v protokolech nevidíme informace o vyhodnocení zásady ImageLocalityPriority, takže abychom ověřili náš předpoklad, vyhodili jsme obrázek s novou verzí aplikace na třetí uzel, po kterém plánování fungovalo správně . Právě kvůli politice ImageLocalityPriority byl problém s plánováním pozorován poměrně zřídka, častěji byl spojen s něčím jiným. Vzhledem k tomu, že jsme nemohli plně odladit každou ze zásad v seznamu priorit výchozího plánovače kube, potřebovali jsme flexibilní správu zásad plánování pod.
Formulace problému
Chtěli jsme, aby řešení problému bylo co nejkonkrétnější, to znamená, že hlavní entity Kubernetes (zde máme na mysli výchozí kube-scheduler) by měly zůstat nezměněny. Nechtěli jsme řešit problém na jednom místě a vytvářet ho na jiném. Tím jsme se dostali ke dvěma možnostem řešení problému, které byly avizovány v úvodu článku – vytvoření dalšího plánovače nebo napsání vlastního. Hlavním požadavkem pro plánování úloh cronu je rozložení zátěže rovnoměrně mezi tři uzly. Tento požadavek může být splněn stávajícími zásadami plánovače kube, takže pro vyřešení našeho problému nemá smysl psát svůj vlastní plánovač.
Pokyny pro vytvoření a nasazení dalšího plánovače kube jsou popsány v dokumentace. Zdálo se nám však, že entita Deployment nestačí zajistit odolnost proti chybám při provozu tak kritické služby, jako je kube-scheduler, a proto jsme se rozhodli nasadit nový kube-scheduler jako Static Pod, který by byl přímo monitorován od Kubeleta. Na nový plánovač kube tedy máme následující požadavky:
Služba musí být nasazena jako statický modul na všech hlavních clusterech
V případě, že aktivní modul s plánovačem kube není k dispozici, musí být poskytnuta tolerance chyb
Hlavní prioritou při plánování by měl být počet dostupných zdrojů na uzlu (LeastRequestedPriority)
Implementace řešení
Okamžitě stojí za zmínku, že veškerou práci budeme provádět v Kubernetes v1.14.7, protože Toto je verze, která byla použita v projektu. Začněme tím, že napíšeme manifest pro náš nový plánovač kube. Vezměme výchozí manifest (/etc/kubernetes/manifests/kube-scheduler.yaml) jako základ a převedeme jej do následující podoby:
Změněn název podu a kontejneru na kube-scheduler-cron
Specifikováno použití portů 10151 a 10159, protože byla definována možnost hostNetwork: true a nemůžeme použít stejné porty jako výchozí plánovač kube (10251 a 10259)
Pomocí parametru --config jsme specifikovali konfigurační soubor, se kterým má být služba spuštěna
Konfigurované připojení konfiguračního souboru (scheduler-custom.conf) a souboru zásad plánování (scheduler-custom-policy-config.json) z hostitele
Nezapomeňte, že náš plánovač kube bude potřebovat práva podobná výchozímu. Upravit jeho roli clusteru:
Nyní si promluvme o tom, co by mělo obsahovat konfigurační soubor a soubor zásad plánování:
Konfigurační soubor (scheduler-custom.conf)
Chcete-li získat výchozí konfiguraci plánovače kube, musíte použít parametr --write-config-to z dokumentace. Výslednou konfiguraci umístíme do souboru /etc/kubernetes/scheduler-custom.conf a zmenšíme do následující podoby:
Nastavili jsme schedulerName na název naší služby kube-scheduler-cron.
V parametru lockObjectName musíte také nastavit název naší služby a ujistit se, že parametr leaderElect nastavte na hodnotu true (pokud máte jeden hlavní uzel, můžete jej nastavit na hodnotu false).
V parametru byla zadána cesta k souboru s popisem zásad plánování algorithmSource.
Stojí za to se blíže podívat na druhý bod, kde upravujeme parametry klíče leaderElection. Abychom zajistili odolnost proti chybám, povolili jsme (leaderElect) proces výběru vedoucího (hlavního) mezi pody našeho plánovače kube pomocí jediného koncového bodu (resourceLock) s názvem kube-scheduler-cron (lockObjectName) ve jmenném prostoru systému kube (lockObjectNamespace). Jak Kubernetes zajišťuje vysokou dostupnost hlavních komponent (včetně kube-scheduleru) najdete v článek.
Soubor zásad plánování (scheduler-custom-policy-config.json)
Jak jsem psal dříve, můžeme zjistit, se kterými konkrétními politikami výchozí kube-scheduler pracuje, pouze analýzou jeho kódu. To znamená, že nemůžeme získat soubor se zásadami plánování pro výchozí plánovač kube stejným způsobem jako konfigurační soubor. Popišme zásady plánování, které nás zajímají, v souboru /etc/kubernetes/scheduler-custom-policy-config.json takto:
Proto kube-scheduler nejprve sestaví seznam uzlů, do kterých lze pod naplánovat podle zásady GeneralPredicates (která zahrnuje sadu zásad PodFitsResources, PodFitsHostPorts, HostName a MatchNodeSelector). A pak je každý uzel vyhodnocen v souladu se sadou zásad v poli priorit. Abychom splnili podmínky našeho úkolu, považovali jsme takový soubor politik za optimální řešení. Dovolte mi připomenout, že sada zásad s jejich podrobným popisem je k dispozici v dokumentace. Chcete-li svůj úkol splnit, můžete jednoduše změnit sadu použitých zásad a přiřadit jim odpovídající váhy.
Nazvěme manifest nového kube-scheduler, který jsme vytvořili na začátku kapitoly, kube-scheduler-custom.yaml a umístěte jej do následující cesty /etc/kubernetes/manifests na tři hlavní uzly. Pokud je vše provedeno správně, Kubelet spustí modul na každém uzlu a v protokolech našeho nového plánovače kube uvidíme informaci, že náš soubor zásad byl úspěšně aplikován:
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:{}]'
Nyní zbývá pouze uvést ve specifikaci našeho CronJob, že všechny požadavky na plánování jeho modulů by měly být zpracovány naším novým plánovačem kube:
Nakonec jsme získali další plánovač kube s unikátní sadou plánovacích politik, jejichž práci sleduje přímo kubelet. Kromě toho jsme nastavili volbu nového vůdce mezi moduly našeho kube-plánovače pro případ, že by starý vůdce z nějakého důvodu přestal být dostupný.
Běžné aplikace a služby jsou nadále plánovány prostřednictvím výchozího plánovače kube a všechny úlohy cron byly zcela převedeny do nového. Zátěž vytvořená úlohami cron je nyní rovnoměrně rozložena mezi všechny uzly. Vzhledem k tomu, že většina úloh cron se provádí na stejných uzlech jako hlavní aplikace projektu, výrazně se tím snížilo riziko přesunu modulů kvůli nedostatku zdrojů. Po zavedení dodatečného plánovače kube již nevznikaly problémy s nerovnoměrným plánováním úloh cron.