ProHoster > Blogi > antaminen > Kubernetes: miksi järjestelmän resurssienhallinnan määrittäminen on niin tärkeää?
Kubernetes: miksi järjestelmän resurssienhallinnan määrittäminen on niin tärkeää?
Yleensä on aina tarve tarjota erillinen resurssivarasto sovellukselle, jotta se toimii oikein ja vakaasti. Mutta entä jos useita sovelluksia on käynnissä samalla teholla? Kuinka tarjota jokaiselle heistä tarvittavat vähimmäisresurssit? Miten resurssien kulutusta voi rajoittaa? Kuinka jakaa kuorma oikein solmujen välillä? Kuinka varmistaa, että vaakasuuntainen skaalausmekanismi toimii, jos sovelluksen kuormitus kasvaa?
Sinun on aloitettava siitä, mitä pääresursseja järjestelmässä on - tämä on tietysti prosessoriaika ja RAM. K8s-luetteloissa nämä resurssityypit mitataan seuraavilla yksiköillä:
CPU - ytimissä
RAM - tavuina
Lisäksi jokaiselle resurssille on mahdollista asettaa kahdenlaisia vaatimuksia - pyynnöt и rajat. Pyynnöt - kuvaa vähimmäisvaatimukset solmun vapaille resursseille kontin (ja koko paketin) suorittamiseksi, kun taas rajoitukset asettavat kovan rajan säilön käytettävissä oleville resursseille.
On tärkeää ymmärtää, että manifestin ei tarvitse erikseen määritellä molempia tyyppejä, mutta käyttäytyminen on seuraava:
Jos vain resurssin rajat on määritetty erikseen, tämän resurssin pyynnöt saavat automaattisesti arvon, joka on yhtä suuri kuin rajat (voit varmistaa tämän kutsumalla kuvaus entiteettejä). Nuo. itse asiassa säilö rajoittuu samaan määrään resursseja, joita se tarvitsee suorittaakseen.
Jos resurssille on nimenomaisesti määritelty vain pyynnöt, tälle resurssille ei aseteta ylempiä rajoituksia - ts. konttia rajoittavat vain itse solmun resurssit.
On myös mahdollista määrittää resurssien hallinta ei vain tietyn säilön tasolla, vaan myös nimiavaruuden tasolla seuraavien entiteettien avulla:
LimitRange — kuvaa rajoituspolitiikkaa säiliön/palon tasolla ns:nä ja tarvitaan kuvaamaan säiliön/palon oletusrajoja, sekä estämään ilmeisen rasvaisten säiliöiden/palojen luomisen (tai päinvastoin), rajoittamaan niiden määrää ja määrittää mahdolliset erot rajojen ja pyyntöjen arvojen välillä
Resurssikiintiöt — kuvaile rajoituskäytäntöä yleisesti kaikille ns:n säilöille, ja sitä käytetään yleensä resurssien rajaamiseen ympäristöjen välillä (hyödyllinen, kun ympäristöjä ei ole tiukasti rajattu solmutasolla)
Seuraavassa on esimerkkejä luetteloista, jotka asettavat resurssirajoituksia:
Nuo. Tässä tapauksessa nginx-säilöä varten tarvitset vähintään 1 Gt vapaata RAM-muistia ja 0.2 CPU:ta solmussa, kun taas säilö voi kuluttaa enintään 0.2 CPU:ta ja kaikkea solmun käytettävissä olevaa RAM-muistia.
Nuo. Kaikkien pyyntösäilöjen summa oletusarvoissa ns ei saa ylittää 300m CPU:lle ja 1G OP:lle, ja kaikkien rajoitusten summa on 700m CPU:lle ja 2G OP:lle.
Nuo. kaikkien säilöjen oletusnimiavaruudessa pyynnön arvoksi asetetaan 100 m CPU:lle ja 1G OP:lle, raja - 1 CPU ja 2G. Samalla asetetaan raja myös CPU:n (50m < x < 2) ja RAM:n (500M < x < 4G) pyynnön/rajan mahdollisille arvoille.
Nuo. jokaiselle podille oletusarvoisessa ns:ssä on 4 vCPU:n ja 1G:n raja.
Nyt haluaisin kertoa teille, mitä etuja näiden rajoitusten asettaminen voi antaa meille.
Kuormituksen tasausmekanismi solmujen välillä
Kuten tiedät, k8s-komponentti vastaa podien jakautumisesta solmujen kesken, kuten ajastimella, joka toimii tietyn algoritmin mukaan. Tämä algoritmi käy läpi kaksi vaihetta, kun valitaan optimaalinen solmu käynnistettäväksi:
suodatus
Rangeissa
Nuo. kuvatun politiikan mukaisesti valitaan aluksi solmut, joiden pohjalta pod voidaan käynnistää joukon perusteella predikaatit (mukaan lukien sen tarkistaminen, onko solmulla tarpeeksi resursseja podin suorittamiseen - PodFitsResources), ja sitten jokaiselle näistä solmuista painopisteet pisteitä myönnetään (mukaan lukien mitä enemmän vapaita resursseja solmulla on, sitä enemmän pisteitä sille osoitetaan - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) ja pod käynnistetään solmussa, jolla on eniten pisteitä (jos useat solmut täyttävät tämän ehdon kerralla, valitaan satunnainen).
Samanaikaisesti sinun on ymmärrettävä, että aikatauluttaja, arvioidessaan solmun käytettävissä olevia resursseja, ohjaa tietoja, jotka on tallennettu etcd - ts. kunkin tässä solmussa käynnissä olevan podin pyydetyn/raja-resurssin määrälle, mutta ei todelliselle resurssin kulutukselle. Nämä tiedot saadaan komennon lähdöstä kubectl describe node $NODE, esimerkiksi:
Täällä näemme kaikki tietyssä solmussa toimivat podit sekä kunkin podin pyytämät resurssit. Ja tältä aikataululokit näyttävät, kun cronjob-cron-events-1573793820-xt6q9 pod käynnistetään (nämä tiedot näkyvät ajoituslokissa, kun asetat 10. lokitason käynnistyskomennon argumenteissa -v=10):
leveä lokki
I1115 07:57:21.637791 1 scheduling_queue.go:908] About to try and schedule pod nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.637804 1 scheduler.go:453] Attempting to schedule pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.638285 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s5 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638300 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s6 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s3 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s4 is allowed, Node is running only 17 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638365 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s12 is allowed, Node is running only 9 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s11 is allowed, Node is running only 11 out of 110 Pods.
I1115 07:57:21.638385 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s1 is allowed, Node is running only 19 out of 110 Pods.
I1115 07:57:21.638402 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s2 is allowed, Node is running only 21 out of 110 Pods.
I1115 07:57:21.638383 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638335 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, Node is running only 18 out of 110 Pods.
I1115 07:57:21.638408 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s13 is allowed, Node is running only 8 out of 110 Pods.
I1115 07:57:21.638478 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638505 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638577 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638583 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s7 is allowed, Node is running only 25 out of 110 Pods.
I1115 07:57:21.638932 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 9
I1115 07:57:21.638946 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 8
I1115 07:57:21.638961 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 9
I1115 07:57:21.638971 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.638975 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 8
I1115 07:57:21.638990 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.639022 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639030 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639034 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639041 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639053 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639059 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639061 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639063 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639073 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639077 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639085 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639088 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639103 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639109 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639114 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639127 1 generic_scheduler.go:781] Host nxs-k8s-s10 => Score 100037
I1115 07:57:21.639150 1 generic_scheduler.go:781] Host nxs-k8s-s8 => Score 100034
I1115 07:57:21.639154 1 generic_scheduler.go:781] Host nxs-k8s-s9 => Score 100037
I1115 07:57:21.639267 1 scheduler_binder.go:269] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10"
I1115 07:57:21.639286 1 scheduler_binder.go:279] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10": all PVCs bound and nothing to do
I1115 07:57:21.639333 1 factory.go:733] Attempting to bind cronjob-cron-events-1573793820-xt6q9 to nxs-k8s-s10
Tässä näemme, että aluksi ajastin suodattaa ja luo luettelon 3 solmusta, joista se voidaan käynnistää (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Sitten se laskee pisteet useiden parametrien (mukaan lukien BalancedResourceAllocation, LeastResourceAllocation) perusteella jokaiselle näistä solmuista sopivimman solmun määrittämiseksi. Lopulta pod ajoitetaan solmuun, jolla on eniten pisteitä (tässä kahdella solmulla on sama määrä pisteitä 100037, joten valitaan satunnainen - nxs-k8s-s10).
johtopäätös: jos solmu ajaa podeja, joille ei ole asetettu rajoituksia, niin k8s:lle (resurssien kulutuksen näkökulmasta) tämä vastaa ikään kuin tässä solmussa ei olisi sellaisia podeja. Siksi, jos sinulla on ehdollisesti pod, jossa on ahmattiprosessi (esimerkiksi wowza) eikä sille ole asetettu rajoituksia, voi syntyä tilanne, kun tämä pod todella söi kaikki solmun resurssit, mutta k8s:lle tämä solmu katsotaan kuormittamattomaksi ja se saa saman pistemäärän, kun se luokitellaan (täsmälleen käytettävissä olevia resursseja arvioivissa pisteissä) solmuksi, jolla ei ole toimivia podeja, mikä voi lopulta johtaa kuorman epätasaiseen jakautumiseen solmujen välillä.
Podin häätö
Kuten tiedät, jokaiselle podille on määritetty yksi kolmesta QoS-luokasta:
taattu — määritetään, kun jokaiselle podin säilölle on määritetty pyyntö ja raja muistille ja prosessorille, ja näiden arvojen on vastattava
räjähtävä — vähintään yhdellä säiliön säiliöllä on pyyntö ja raja, pyyntö < limit
paras ponnistus — kun yksikään säiliön resurssi ei ole rajoitettu
Samaan aikaan, kun solmu kokee resurssien (levyn, muistin) puutteen, kubelet alkaa luokitella ja häätää podeja tietyn algoritmin mukaan, joka ottaa huomioon podin prioriteetin ja sen QoS-luokan. Esimerkiksi, jos puhumme RAM-muistista, niin QoS-luokan perusteella pisteet myönnetään seuraavan periaatteen mukaisesti:
Nuo. samalla prioriteetilla kubelet häätää ensin solmusta parhaan QoS-luokan podit.
johtopäätös: jos haluat vähentää todennäköisyyttä, että haluttu pod häädetään solmusta siinä tapauksessa, että siinä ei ole resursseja, sinun on priorisoinnin ohella huolehdittava myös pyynnön/rajan asettamisesta sille.
Kun tehtävänä on automaattisesti lisätä ja vähentää podien määrää resurssien käytöstä (järjestelmä - CPU/RAM tai käyttäjä - rps) riippuen, k8s-entiteetti kuten HPA (Horizontal Pod Autoscaler). Sen algoritmi on seuraava:
Havaitun resurssin nykyiset lukemat määritetään (currentMetricValue)
Resurssille määritetään halutut arvot (desiredMetricValue), jotka järjestelmäresursseille asetetaan pyynnöstä
Replikoiden nykyinen määrä määritetään (currentReplicas)
Seuraava kaava laskee halutun määrän kopioita (haluttuja replikoita)
HaluttuReplicas = [ currentReplicas * ( currentMetricValue / HaluttuMetricValue )]
Tässä tapauksessa skaalaus ei tapahdu, kun kerroin (currentMetricValue / HaluttuMetricValue) on lähellä 1 (tässä tapauksessa voimme asettaa sallitun virheen itse; oletuksena se on 0.1).
Katsotaanpa, miten hpa toimii esimerkkinä sovellustestisovelluksesta (kuten Deployment), jossa on tarpeen muuttaa replikoiden määrää prosessorin kulutuksen mukaan:
Nuo. näemme, että sovelluspaketti käynnistetään alun perin kahdessa tapauksessa, joista jokainen sisältää kaksi nginx- ja nginx-vientikonttia, joista jokaiselle on määritetty pyynnöt prosessorille.
Nuo. Loimme hpa:n, joka valvoo Deployment app-testiä ja säätää podien määrää sovelluksella prosessoriindikaattorin perusteella (oletamme, että pod kuluttaa 30 % sen pyytämästä suorittimesta) replikoiden määrän ollessa mukana välillä 2-10.
Katsotaanpa nyt hpa:n toiminnan mekanismia, jos kuormitamme yhtä tulisijoista:
# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
app-test-78559f8f44-pgs58 101m 243Mi
app-test-78559f8f44-cj4jz 4m 240Mi
Yhteensä meillä on seuraavat:
Haluttu arvo (desiredMetricValue) - hpa-asetusten mukaan meillä on 30 %
Current value (currentMetricValue) - laskentaa varten ohjain-manager laskee resurssien kulutuksen keskiarvon prosentteina, ts. ehdollisesti tekee seuraavaa:
Vastaanottaa pod-mittareiden absoluuttiset arvot metriikkapalvelimelta, ts. 101m ja 4m
Järjestelmään kohdistuvan negatiivisen vaikutuksen neutraloimiseksi kuormituspiikkien aikana konfiguroitu hpa ei riitä. Esimerkiksi hpa-ohjainten hallinnan asetusten mukaan se päättää, että replikoiden määrää on lisättävä 2-kertaiseksi, mutta solmuilla ei ole vapaita resursseja suorittaakseen tällaista määrää podeja (eli solmu ei voi tarjota pyydetyt resurssit pyyntöryhmiin) ja nämä ryhmät siirtyvät Odottaa-tilaan.
Tässä tapauksessa, jos tarjoajalla on vastaava IaaS/PaaS (esimerkiksi GKE/GCE, AKS, EKS jne.), työkalu, kuten Node Autoscaler. Sen avulla voit asettaa klusterin solmujen enimmäis- ja vähimmäismäärän ja säätää automaattisesti nykyistä solmumäärää (soittamalla pilvipalvelun sovellusliittymää tilaamaan/poistamaan solmun), kun klusterissa ja podissa on resurssien puute. ei voida ajoittaa (ovat Odottaa-tilassa).
Johtopäätös: Solmujen automaattisen skaalauksen mahdollistamiseksi on tarpeen asettaa pyynnöt pod-säiliöihin, jotta k8s voivat arvioida oikein solmujen kuormituksen ja raportoida vastaavasti, että klusterissa ei ole resursseja seuraavan podin käynnistämiseksi.
Johtopäätös
On huomattava, että säilöresurssirajojen asettaminen ei ole edellytys sovelluksen onnistumiselle, mutta se on silti parempi tehdä se seuraavista syistä:
Aikataulun tarkempaa käyttöä varten k8s-solmujen välisen kuormituksen tasapainotuksen kannalta
Vähentääkseen todennäköisyyttä, että "potin häätö" tapahtuu
Sovellustyynyjen (HPA) vaakasuora automaattiskaalaus toimii