Kube-scheduler on Kubernetesin olennainen osa, joka vastaa podien ajoittamisesta solmujen välillä määritettyjen käytäntöjen mukaisesti. Usein Kubernetes-klusterin toiminnan aikana meidän ei tarvitse miettiä, mitä käytäntöjä podien ajoittamiseen käytetään, koska oletuskube-aikataulun käytäntösarja sopii useimpiin päivittäisiin tehtäviin. On kuitenkin tilanteita, joissa meidän on tärkeää hienosäätää podien allokointiprosessia, ja tämä tehtävä voidaan suorittaa kahdella tavalla:
Luo kube-aikataulu mukautetuilla säännöillä
Kirjoita oma aikataulusi ja opeta se toimimaan API-palvelinpyyntöjen kanssa
Tässä artikkelissa kuvailen ensimmäisen kohdan toteuttamista tulisijojen epätasaisen ajoituksen ongelman ratkaisemiseksi yhdessä projektissamme.
Lyhyt esittely kube-schedulerin toiminnasta
Erityisesti kannattaa huomioida se tosiasia, että kube-scheduler ei ole vastuussa podien suorasta ajoittamisesta - se on vastuussa vain sen solmun määrittämisestä, johon pod sijoitetaan. Toisin sanoen kube-schedulerin työn tulos on solmun nimi, jonka se palauttaa API-palvelimelle ajoituspyyntöä varten, ja siihen sen työ päättyy.
Ensin kube-scheduler kokoaa listan solmuista, joihin pod voidaan ajoittaa predikaattikäytäntöjen mukaisesti. Seuraavaksi jokainen tämän luettelon solmu saa tietyn määrän pisteitä prioriteettikäytäntöjen mukaisesti. Tämän seurauksena valitaan solmu, jolla on suurin määrä pisteitä. Jos solmuilla on sama maksimipistemäärä, valitaan satunnainen. Luettelo ja kuvaus predikaatti- (suodatus) ja prioriteettikäytännöistä (pisteytys) on osoitteessa dokumentointi.
Kuvaus ongelmarungosta
Huolimatta siitä, että Nixysillä ylläpidetään suurta määrää erilaisia Kubernetes-klustereita, törmäsimme ensimmäisen kerran podien ajoitusongelmaan vasta äskettäin, kun yhdessä projekteistamme piti suorittaa suuri määrä säännöllisiä tehtäviä (~100 CronJob-kokonaisuutta). Yksinkertaistaaksemme ongelman kuvausta mahdollisimman paljon, otamme esimerkkinä yhden mikropalvelun, jossa cron-tehtävä käynnistetään kerran minuutissa, mikä kuormittaa prosessoria. Cron-tehtävän suorittamiseksi allokoitiin kolme solmua, joilla oli täysin identtiset ominaisuudet (24 vCPU:ta kussakin).
Samanaikaisesti on mahdotonta sanoa tarkasti, kuinka kauan CronJob kestää, koska syöttötietojen määrä muuttuu jatkuvasti. Kube-schedulerin normaalin toiminnan aikana jokainen solmu suorittaa keskimäärin 3-4 työinstanssia, jotka kuormittavat ~20-30% kunkin solmun suorittimen kuormituksesta:
Ongelma itsessään on, että joskus cron-tehtäväpodit lakkasivat olemasta ajoitettuja yhteen kolmesta solmusta. Eli jossain vaiheessa yhdelle solmulle ei suunniteltu ainuttakaan podia, kun taas kahdessa muussa solmussa oli käynnissä 6-8 kopiota tehtävästä, mikä tuotti ~40-60% suorittimen kuormituksesta:
Ongelma toistui täysin satunnaisesti ja toisinaan korreloi koodin uuden version julkaisun kanssa.
Nostamalla kube-schedulerin lokitason tasolle 10 (-v=10), aloimme tallentaa, kuinka monta pistettä kukin solmu sai arviointiprosessin aikana. Normaalin suunnittelutoiminnan aikana lokeissa voitiin nähdä seuraavat tiedot:
Nuo. lokeista saaduista tiedoista päätellen kukin solmu sai yhtä paljon lopullisia pisteitä ja satunnainen valittiin suunnitteluun. Ongelmallisen suunnittelun aikana lokit näyttivät tältä:
Mistä voidaan nähdä, että yksi solmuista sai vähemmän loppupisteitä kuin muut, ja siksi suunnittelua tehtiin vain kahdelle maksimipistemäärän saaneelle solmulle. Näin ollen olimme ehdottomasti vakuuttuneita siitä, että ongelma piilee nimenomaan podien ajoituksessa.
Lisäalgoritmi ongelman ratkaisemiseksi oli meille ilmeinen - analysoi lokit, ymmärrä, millä prioriteetilla solmu ei saanut pisteitä ja säädä tarvittaessa oletuskube-ajoitusohjelman käytäntöjä. Tässä kohtaamme kuitenkin kaksi merkittävää vaikeutta:
Suurimmalla kirjaustasolla (10) vain joistakin prioriteeteista saadut pisteet näkyvät. Yllä olevasta lokiotteesta näkyy, että kaikista lokeissa näkyvistä prioriteeteista solmut saavat saman pistemäärän normaalissa ja ongelma-ajoituksessa, mutta lopputulos ongelmasuunnittelun tapauksessa on erilainen. Siten voimme päätellä, että joidenkin prioriteettien kohdalla pisteytys tapahtuu "kulissien takana", eikä meillä ole mitään keinoa ymmärtää, mistä prioriteetista solmu ei saanut pisteitä. Kuvasimme tätä ongelmaa yksityiskohtaisesti artikkelissa kysymys Kubernetes-arkisto Githubissa. Tätä kirjoittaessa kehittäjiltä saatiin vastaus, että Kubernetes v1.15,1.16, 1.17 ja XNUMX päivityksiin lisätään lokituki.
Ei ole helppoa tapaa ymmärtää, mitä tiettyä käytäntösarjaa kube-scheduler parhaillaan käyttää. Kyllä, sisään dokumentointi tämä luettelo on lueteltu, mutta se ei sisällä tietoja siitä, mitä painoarvoja kullekin prioriteettikäytännölle on määritetty. Voit nähdä painot tai muokata oletuskube-aikataulun käytäntöjä vain kohteessa lähdekoodeja.
On syytä huomata, että kun pystyimme kirjaamaan, että solmu ei saanut pisteitä ImageLocalityPriority-käytännön mukaisesti, joka antaa pisteitä solmulle, jos sillä on jo sovelluksen suorittamiseen tarvittava kuva. Eli kun sovelluksen uusi versio julkaistiin, cron-tehtävä onnistui ajamaan kahdessa solmussa, lataamalla niille uuden kuvan Docker-rekisteristä, ja siten kaksi solmua sai korkeamman lopullisen pistemäärän verrattuna kolmanteen. .
Kuten ylempänä kirjoitin, emme näe lokeissa tietoa ImageLocalityPriority-käytännön arvioinnista, joten olettamuksemme tarkistamiseksi upposimme kuvan sovelluksen uudella versiolla kolmanteen solmuun, jonka jälkeen ajoitus toimi oikein . Juuri ImageLocalityPriority-käytännön vuoksi ajoitusongelma havaittiin melko harvoin, useammin se liittyi johonkin muuhun. Koska emme pystyneet täysin virheenkorjaamaan kaikkia oletuskube-ajoitusohjelman prioriteettiluettelon käytäntöjä, tarvitsimme pod-ajoituskäytäntöjen joustavaa hallintaa.
Ongelma
Halusimme ongelman ratkaisun olevan mahdollisimman tarkka eli Kubernetesin (tässä tarkoitamme oletuskube-ajoitusohjelma) pääolioiden tulisi pysyä ennallaan. Emme halunneet ratkaista ongelmaa yhdessä paikassa ja luoda sitä toisessa. Näin ollen päädyimme kahteen vaihtoehtoon ongelman ratkaisemiseksi, joista ilmoitettiin artikkelin johdannossa - lisäaikataulun luominen tai oman kirjoittaminen. Päävaatimus cron-tehtävien ajoituksessa on jakaa kuorma tasaisesti kolmen solmun välillä. Tämä vaatimus voidaan täyttää olemassa olevilla kube-scheduler-käytännöillä, joten ongelmamme ratkaisemiseksi ei ole mitään järkeä kirjoittaa omaa ajastinta.
Ohjeet ylimääräisen kube-aikataulun luomiseen ja käyttöönottoon on kuvattu dokumentointi. Meistä kuitenkin vaikutti siltä, että Deployment-entiteetti ei riittänyt varmistamaan vikasietoisuutta niin kriittisen palvelun kuin kube-scheduler toiminnassa, joten päätimme ottaa käyttöön uuden kube-schedulerin Static Podina, jota valvottaisiin suoraan. kirjoittanut Kubelet. Näin ollen meillä on seuraavat vaatimukset uudelle kube-aikataululle:
Palvelu on otettava käyttöön staattisena kotelona kaikissa klusterin pääkoneissa
Vikasietoisuus on annettava, jos aktiivinen pod kube-schedulerilla ei ole käytettävissä
Suunnittelun pääprioriteetti tulee olla solmun käytettävissä olevien resurssien määrä (LeastRequestedPriority)
Toteutusratkaisut
Heti kannattaa huomioida, että teemme kaikki työt Kubernetes v1.14.7:ssä, koska Tämä on versio, jota käytettiin projektissa. Aloitetaan kirjoittamalla manifesti uudelle kube-schedulerillemme. Otetaan oletusluettelo (/etc/kubernetes/manifests/kube-scheduler.yaml) pohjaksi ja tuodaan se seuraavaan muotoon:
Podin ja säilön nimi muutettiin muotoon kube-scheduler-cron
Määritettiin porttien 10151 ja 10159 käyttö vaihtoehdoksi hostNetwork: true emmekä voi käyttää samoja portteja kuin oletuskube-aikataulu (10251 ja 10259)
Määritimme parametrin --config avulla määritystiedoston, jolla palvelu käynnistetään
Konfigurointitiedoston (scheduler-custom.conf) ja ajoituskäytäntötiedoston (scheduler-custom-policy-config.json) liittäminen isännästä
Älä unohda, että kube-scheduler tarvitsee oletusarvon kaltaiset oikeudet. Muokkaa sen klusterin roolia:
Puhutaan nyt siitä, mitä määritystiedoston ja ajoituskäytäntötiedoston pitäisi sisältää:
Asetustiedosto (scheduler-custom.conf)
Käytä parametria saadaksesi oletusarvoisen kube-scheduler-kokoonpanon --write-config-to ja dokumentointi. Sijoitamme tuloksena saadut asetukset tiedostoon /etc/kubernetes/scheduler-custom.conf ja muokkaamme sen seuraavaan muotoon:
Parametrissa lockObjectName sinun on myös asetettava palvelumme nimi ja varmistettava, että parametri leaderElect aseta arvoksi tosi (jos sinulla on yksi pääsolmu, voit asettaa sen arvoon false).
Määritti polun tiedostoon, jossa on parametrin aikataulutuskäytäntöjen kuvaus algorithmSource.
On syytä tarkastella lähemmin toista kohtaa, jossa muokkaamme avaimen parametreja leaderElection. Vikasietokyvyn varmistamiseksi olemme ottaneet käyttöön (leaderElect) prosessi, jossa valitaan johtaja (master) kube-schedulerimme podien välillä käyttämällä niille yhtä päätepistettä (resourceLock) nimeltä kube-scheduler-cron (lockObjectName) kube-järjestelmän nimiavaruudessa (lockObjectNamespace). Miten Kubernetes varmistaa pääkomponenttien (mukaan lukien kube-scheduler) korkean käytettävyyden, löydät osoitteesta статье.
Ajoituskäytäntötiedosto (scheduler-custom-policy-config.json)
Kuten aiemmin kirjoitin, voimme selvittää, millä erityisillä käytännöillä oletuskube-scheduler toimii, vain analysoimalla sen koodia. Toisin sanoen emme voi saada tiedostoa, jossa on ajoituskäytännöt oletuskube-aikataululle, samalla tavalla kuin asetustiedostoa. Kuvataan aikataulutuskäytännöt, joista olemme kiinnostuneita /etc/kubernetes/scheduler-custom-policy-config.json-tiedostossa seuraavasti:
Siten kube-scheduler kokoaa ensin luettelon solmuista, joihin pod voidaan ajoittaa GeneralPredicates-käytännön mukaisesti (joka sisältää joukon PodFitsResources-, PodFitsHostPorts-, HostName- ja MatchNodeSelector-käytäntöjä). Ja sitten jokainen solmu arvioidaan prioriteettitaulukon käytäntöjen joukon mukaisesti. Täyttääksemme tehtävämme ehdot katsoimme, että tällainen toimintalinja olisi optimaalinen ratkaisu. Haluan muistuttaa, että joukko käytäntöjä ja niiden yksityiskohtaiset kuvaukset ovat saatavilla dokumentointi. Suorittaaksesi tehtäväsi, voit yksinkertaisesti muuttaa käytettyjä käytäntöjä ja määrittää niille sopivat painotukset.
Kutsutaan luvun alussa luomamme uuden kube-schedulerin manifestiksi kube-scheduler-custom.yaml ja sijoitetaan se seuraavaan polkuun /etc/kubernetes/manifests kolmeen pääsolmuun. Jos kaikki on tehty oikein, Kubelet käynnistää podin jokaisessa solmussa, ja uuden kube-schedulerimme lokeissa näemme tiedon siitä, että käytäntötiedostomme on otettu käyttöö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:{}]'
Nyt ei jää muuta kuin ilmoittaa CronJobimme spesifikaatioissa, että uuden kube-schedulerimme tulee käsitellä kaikki podien ajoituspyynnöt:
Lopulta saimme ylimääräisen kube-schedulerin ainutlaatuisella aikataulutuskäytännöllä, jonka toimintaa valvoo suoraan kubelet. Lisäksi olemme asettaneet uuden johtajan valinnan kube-schedulerimme väliin siltä varalta, että vanha johtaja ei jostain syystä ole tavoitettavissa.
Säännölliset sovellukset ja palvelut ajoitetaan edelleen oletuskube-aikataulun kautta, ja kaikki cron-tehtävät on siirretty kokonaan uuteen. cron-tehtävien luoma kuorma on nyt jakautunut tasaisesti kaikille solmuille. Ottaen huomioon, että suurin osa cron-tehtävistä suoritetaan samoissa solmuissa kuin projektin pääsovellukset, tämä on vähentänyt merkittävästi podien liikkumisen riskiä resurssien puutteen vuoksi. Lisäkube-aikataulun käyttöönoton jälkeen cron-tehtävien epätasaisen ajoituksen ongelmia ei enää ilmennyt.