Kubernetes: kodėl taip svarbu nustatyti sistemos išteklių valdymą?
Paprastai, norint, kad programa veiktų teisingai ir stabiliai, visada reikia suteikti tam skirtą išteklių telkinį. Bet ką daryti, jei kelios programos veikia tuo pačiu maitinimu? Kaip kiekvieną iš jų aprūpinti minimaliais reikiamais ištekliais? Kaip galite apriboti išteklių naudojimą? Kaip teisingai paskirstyti apkrovą tarp mazgų? Kaip užtikrinti, kad horizontalaus mastelio keitimo mechanizmas veiktų, jei padidėtų programų apkrova?
Pradėti reikia nuo to, kokie pagrindiniai ištekliai egzistuoja sistemoje – tai, žinoma, yra procesoriaus laikas ir RAM. K8s manifestuose šie išteklių tipai matuojami šiais vienetais:
CPU – branduoliuose
RAM – baitais
Be to, kiekvienam ištekliui galima nustatyti dviejų tipų reikalavimus - prašymai и ribos. Užklausos – aprašomi minimalūs reikalavimai laisviems mazgo ištekliams, kad būtų galima paleisti sudėtinį rodinį (ir grupę kaip visumą), o apribojimai nustato griežtą sudėtinio rodinio išteklių ribą.
Svarbu suprasti, kad manifeste nebūtina aiškiai apibrėžti abiejų tipų, tačiau elgsena bus tokia:
Jei aiškiai nurodytos tik ištekliaus ribos, šio resurso užklausos automatiškai įgauna reikšmę, lygią riboms (tai galite patikrinti iškvietę aprašomuosius objektus). Tie. Tiesą sakant, konteineris bus apribotas iki tiek išteklių, kiek reikia jam paleisti.
Jei ištekliui yra aiškiai nurodytos tik užklausos, tai šiam ištekliui nenustatyti jokie viršutiniai apribojimai – t.y. konteinerį riboja tik paties mazgo ištekliai.
Taip pat galima konfigūruoti išteklių valdymą ne tik konkretaus konteinerio, bet ir vardų erdvės lygiu, naudojant šiuos objektus:
LimitRange — aprašo apribojimų politiką konteinerio / ankšties lygyje ns ir reikalinga norint apibūdinti numatytuosius konteinerio / ankšties apribojimus, taip pat užkirsti kelią akivaizdžiai riebalų talpyklų / ankščių susidarymui (arba atvirkščiai), apriboti jų skaičių ir nustatyti galimą ribų ir užklausų verčių skirtumą
Išteklių kvotos — apskritai apibūdinkite apribojimų politiką, taikomą visiems ns konteineriams ir paprastai naudojama siekiant atskirti išteklius tarp aplinkų (naudinga, kai aplinka nėra griežtai atribota mazgo lygiu)
Toliau pateikiami apraiškų, nustatančių išteklių apribojimus, pavyzdžiai:
Tie. Šiuo atveju, norint paleisti konteinerį su nginx, mazge reikės mažiausiai 1 G laisvos RAM ir 0.2 procesoriaus, o talpykla gali sunaudoti daugiausia 0.2 procesoriaus ir visos turimos mazgo RAM.
Tie. numatytoje visų konteinerių vardų srityje užklausa bus nustatyta į 100 m CPU ir 1G OP, riba – 1 CPU ir 2G. Tuo pačiu metu taip pat nustatomas galimų procesoriaus (50 m < x < 2) ir RAM (500 M < x < 4G) užklausos / limito verčių limitas.
Tie. kiekvienam podeliui numatytajame ns bus 4 vCPU ir 1G limitas.
Dabar norėčiau jums pasakyti, kokių pranašumų mums gali suteikti šie apribojimai.
Apkrovos balansavimo mechanizmas tarp mazgų
Kaip žinote, k8s komponentas yra atsakingas už ankščių paskirstymą tarp mazgų, tokių kaip Planuotojas, kuris veikia pagal konkretų algoritmą. Šis algoritmas pasirenka du etapus, kai pasirenkamas optimalus paleidimo mazgas:
filtravimas
Range
Tie. pagal aprašytą politiką iš pradžių parenkami mazgai, kuriais remiantis galima paleisti podą pagal rinkinį predikatai (įskaitant patikrinimą, ar mazgas turi pakankamai išteklių podiui paleisti – PodFitsResources), o tada kiekvienam iš šių mazgų, atsižvelgiant į prioritetai suteikiami taškai (įskaitant, kuo daugiau mazgas turi laisvų išteklių, tuo daugiau taškų jam priskiriama – LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) ir podas paleidžiamas daugiausiai taškų surinkusiame mazge (jei keli mazgai iš karto atitinka šią sąlygą, tada pasirenkamas atsitiktinis).
Tuo pačiu reikia suprasti, kad planuotojas, vertindamas turimus mazgo resursus, vadovaujasi duomenimis, kurie saugomi etcd – t.y. už prašomą / ribinį kiekvieno šiame mazge veikiančio bloko išteklių kiekį, bet ne už faktinį išteklių suvartojimą. Šią informaciją galima gauti iš komandos išvesties kubectl describe node $NODE, pavyzdžiui:
Čia matome visus konkrečiame mazge veikiančius blokus, taip pat išteklius, kurių reikalauja kiekvienas blokas. Štai kaip atrodo planuotojo žurnalai, kai paleidžiamas cronjob-cron-events-1573793820-xt6q9 pod (ši informacija bus rodoma planavimo žurnale, kai paleidimo komandos argumentuose bus nustatytas 10 registravimo lygis -v=10 ):
žurnalas
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
Čia matome, kad iš pradžių planuoklis filtruoja ir sugeneruoja 3 mazgų, kuriuose jį galima paleisti, sąrašą (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Tada jis apskaičiuoja balus pagal kelis parametrus (įskaitant BalancedResourceAllocation, LeastResourceAllocation) kiekvienam iš šių mazgų, kad nustatytų tinkamiausią mazgą. Galiausiai blokas suplanuojamas mazge, kuriame yra didžiausias taškų skaičius (čia du mazgai vienu metu turi tą patį taškų skaičių 100037, todėl pasirenkamas atsitiktinis - nxs-k8s-s10).
Produkcija: jei mazgas valdo blokus, kuriems nenustatyti jokie apribojimai, tai k8s (išteklių suvartojimo požiūriu) tai bus tolygu, tarsi šiame mazge tokių blokų iš viso nebūtų. Todėl, jei sąlyginai turite podą su gluttonišku procesu (pavyzdžiui, wowza) ir jam nenustatyti jokie apribojimai, gali susidaryti situacija, kai šis podas iš tikrųjų suvalgė visus mazgo išteklius, o k8s šis mazgas yra laikomas neapkrautu ir jam bus suteiktas tiek pat balų, kai reitinguojamas (tiksliai taškais, įvertinus turimus išteklius) kaip mazgas, neturintis veikiančių podų, o tai galiausiai gali lemti netolygų apkrovos paskirstymą tarp mazgų.
Podo iškeldinimas
Kaip žinote, kiekvienam podui priskiriama viena iš 3 QoS klasių:
garantuotas – priskiriamas, kai kiekvienam podelyje esančiam konteineriui yra nurodyta užklausa ir limitas atminčiai ir procesoriui, ir šios reikšmės turi sutapti
sprogstamasis — bent vienas konteineris talpykloje turi užklausą ir limitą, o užklausa < limit
geriausios pastangos — kai nėra nei vieno konteinerio talpyklos ištekliai riboti
Tuo pačiu metu, kai mazgas patiria išteklių (disko, atminties) trūkumą, kubelet pradeda reitinguoti ir iškeldinti blokus pagal konkretų algoritmą, kuriame atsižvelgiama į bloko prioritetą ir jo QoS klasę. Pavyzdžiui, jei kalbame apie RAM, tada pagal QoS klasę taškai suteikiami tokiu principu:
Garantija: -998 val
BestEffort: 1000
Plyštantis: min(maks.(2, 1000 - (1000 * atminties užklausos baitai) / mašinos atminties talpa baitai), 999)
Tie. su tuo pačiu prioritetu, kubeletas pirmiausia iškels iš mazgo geriausios kokybės QoS klasės blokus.
Produkcija: jei norite sumažinti tikimybę, kad norimas podas bus iškeldintas iš mazgo, jei jame trūksta resursų, tuomet kartu su prioritetu turite pasirūpinti ir užklausos/limicijos nustatymu.
Kai užduotis yra automatiškai padidinti ir sumažinti podų skaičių, priklausomai nuo išteklių naudojimo (sistema - CPU/RAM arba vartotojas - rps), toks k8s subjektas kaip HPA (Horizontal Pod Autoscaler). Kurio algoritmas yra toks:
Nustatomos norimos išteklių reikšmės (desiredMetricValue), kurios sistemos ištekliams nustatomos naudojant užklausą
Nustatytas dabartinis kopijų skaičius (currentReplicas)
Ši formulė apskaičiuoja pageidaujamą kopijų skaičių (norimas replikos)
wishReplicas = [ currentReplicas * ( currentMetricValue / wishMetricValue )]
Šiuo atveju mastelio keitimas neįvyks, kai koeficientas (currentMetricValue / wishMetricValue) yra artimas 1 (šiuo atveju leistiną klaidą galime nustatyti patys; pagal nutylėjimą ji yra 0.1).
Pažiūrėkime, kaip veikia hpa, naudodami programos testavimo programos pavyzdį (apibūdintą kaip diegimas), kur reikia pakeisti kopijų skaičių, atsižvelgiant į procesoriaus suvartojimą:
Tie. matome, kad programų rinkinys iš pradžių paleidžiamas dviem atvejais, kurių kiekviename yra du nginx ir nginx-eksportuotojo konteineriai, kiekvienam iš kurių nurodytas prašymai CPU.
Tie. Sukūrėme hpa, kuri stebės diegimo programos testą ir reguliuos blokų skaičių su programa pagal procesoriaus indikatorių (tikimės, kad podas sunaudos 30% jo reikalaujamo procesoriaus), o kopijų skaičius bus 2-10 diapazone.
Dabar pažiūrėkime į hpa veikimo mechanizmą, jei vieną iš židinių apkrausime:
# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
app-test-78559f8f44-pgs58 101m 243Mi
app-test-78559f8f44-cj4jz 4m 240Mi
Iš viso turime:
Norima reikšmė (desiredMetricValue) – pagal hpa nustatymus turime 30 proc.
Dabartinė vertė (currentMetricValue) - skaičiavimui, valdiklis-vadybininkas apskaičiuoja vidutinę išteklių suvartojimo reikšmę %, t.y. sąlygiškai atlieka šiuos veiksmus:
Gauna absoliučias pod metrikos reikšmes iš metrikos serverio, t.y. 101m ir 4m
Norint neutralizuoti neigiamą poveikį sistemai apkrovos šuolių metu, neužtenka sukonfigūruoto hpa. Pavyzdžiui, pagal nustatymus hpa valdiklio tvarkyklėje jis nusprendžia, kad reikia padidinti replikų skaičių 2 kartus, tačiau mazgai neturi laisvų resursų tokiam podų skaičiui paleisti (t.y. mazgas negali pateikti užklausų išteklių į užklausų grupę) ir šios grupės persijungia į būseną Laukiama.
Tokiu atveju, jei teikėjas turi atitinkamą IaaS / PaaS (pavyzdžiui, GKE / GCE, AKS, EKS ir kt.), įrankis, pvz., Node Autoscaler. Tai leidžia nustatyti maksimalų ir mažiausią mazgų skaičių klasteryje ir automatiškai koreguoti esamą mazgų skaičių (paskambinus debesies paslaugų teikėjo API, kad užsakytumėte / pašalintumėte mazgą), kai klasteryje ir blokuose trūksta išteklių. negali būti suplanuoti (būsenoje Laukiama).
Išvada: Kad būtų galima automatizuoti mazgų skalę, reikia nustatyti užklausas pod konteineriuose, kad k8s galėtų teisingai įvertinti mazgų apkrovą ir atitinkamai pranešti, kad klasteryje nėra resursų paleisti kitą podą.
išvada
Reikėtų pažymėti, kad konteinerio išteklių apribojimų nustatymas nėra būtinas reikalavimas, kad programa veiktų sėkmingai, tačiau vis tiek geriau tai padaryti dėl šių priežasčių:
Tikslesniam planuoklio veikimui apkrovos balansavimo tarp k8s mazgų atžvilgiu
Sumažinti „ankšties iškeldinimo“ įvykio tikimybę
Kad veiktų horizontalus automatinis aplikacijų keitimas (HPA).
Horizontaliam automatiniam mazgų mastelio keitimui (Cluster Autoscaling) debesų paslaugų teikėjams
Taip pat skaitykite kitus mūsų tinklaraščio straipsnius: