ProHoster > блог > Администрација > Kubernetes: зошто е толку важно да се постави системско управување со ресурсите?
Kubernetes: зошто е толку важно да се постави системско управување со ресурсите?
Како по правило, секогаш постои потреба да се обезбеди посветен фонд на ресурси на апликацијата за нејзино правилно и стабилно функционирање. Но, што ако неколку апликации работат на иста моќност? Како да се обезбеди секој од нив со минимум потребни ресурси? Како можете да ја ограничите потрошувачката на ресурси? Како правилно да се дистрибуира товарот помеѓу јазлите? Како да се осигурате дека механизмот за хоризонтално скалирање работи ако се зголеми оптоварувањето на апликацијата?
Треба да започнете со кои главни видови ресурси постојат во системот - ова, се разбира, е време на процесорот и RAM меморија. Во манифестациите на k8, овие типови на ресурси се мерат во следните единици:
Процесорот - во јадра
RAM меморија - во бајти
Покрај тоа, за секој ресурс е можно да се постават два вида барања - барања и граници. Барања - ги опишува минималните барања за слободни ресурси на јазолот за водење на контејнер (и pod како целина), додека лимитите поставуваат цврсто ограничување на ресурсите достапни на контејнерот.
Важно е да се разбере дека манифестот не мора експлицитно да ги дефинира двата типа, но однесувањето ќе биде како што следува:
Ако само границите на ресурсот се експлицитно наведени, тогаш барањата за овој ресурс автоматски земаат вредност еднаква на лимитите (може да го потврдите ова со повикување на ентитети за опишување). Оние. всушност, контејнерот ќе биде ограничен на истиот износ на ресурси што ги бара за да работи.
Ако само барањата се експлицитно наведени за некој ресурс, тогаш не се поставени горните ограничувања на овој ресурс - т.е. контејнерот е ограничен само од ресурсите на самиот јазол.
Исто така, можно е да се конфигурира управувањето со ресурсите не само на ниво на одреден контејнер, туку и на ниво на именски простор користејќи ги следните ентитети:
Ограничен опсег — ја опишува политиката за ограничување на ниво на контејнер/под во ns и е потребна со цел да се опишат стандардните граници на контејнерот/под, како и да се спречи создавање на очигледно масни контејнери/мешули (или обратно), да се ограничи нивниот број и да ја одредите можната разлика во вредностите во границите и барањата
РесурсиКвоти - опишете ја политиката за ограничување општо за сите контејнери во ns и се користи, како по правило, за разграничување на ресурсите меѓу околините (корисно кога околините не се строго разграничени на ниво на јазол)
Следниве се примери на манифестации кои поставуваат ограничувања на ресурсите:
Оние. во овој случај, за да водите контејнер со nginx, ќе ви требаат најмалку 1G слободна RAM и 0.2 процесор на јазолот, додека најмногу контејнерот може да троши 0.2 процесор и целата достапна RAM меморија на јазолот.
Оние. збирот на сите контејнери за барања во стандардните ns не може да надмине 300 m за процесорот и 1G за OP, а збирот на сите ограничувања е 700 m за процесорот и 2G за OP.
Оние. во стандардниот именски простор за сите контејнери, барањето ќе биде поставено на 100 m за CPU и 1G за OP, ограничување - 1 CPU и 2G. Во исто време, се поставува и ограничување на можните вредности во барање/лимит за процесорот (50m < x < 2) и RAM (500M < x < 4G).
Оние. за секој pod во стандардните ns ќе има ограничување од 4 vCPU и 1G.
Сега би сакал да ви кажам какви предности може да ни даде поставувањето на овие ограничувања.
Механизам за балансирање на оптоварување помеѓу јазли
Како што знаете, компонентата k8s е одговорна за распределбата на мешунките меѓу јазлите, како на пр распоредувач, кој работи според специфичен алгоритам. Овој алгоритам поминува низ две фази при изборот на оптимален јазол за лансирање:
филтрирање
Опсегнување
Оние. според опишаната политика, првично се избираат јазли на кои е можно да се стартува pod врз основа на множество предикати (вклучувајќи проверка дали јазолот има доволно ресурси за да го стартува подот - PodFitsResources), а потоа за секој од овие јазли, според приоритети се доделуваат поени (вклучувајќи, колку повеќе слободни ресурси има еден јазол, толку повеќе поени му се доделуваат - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) и подлогата се активира на јазолот со најмногу поени (ако неколку јазли ја задоволуваат оваа состојба одеднаш, тогаш се избира по случаен избор) .
Во исто време, треба да разберете дека распоредувачот, кога ги проценува достапните ресурси на јазолот, се води од податоците што се зачувани во etcd - т.е. за износот на бараниот/ограничен ресурс на секој под кој работи на овој јазол, но не и за вистинската потрошувачка на ресурси. Оваа информација може да се добие од излезот на командата kubectl describe node $NODE, на пример:
Овде ги гледаме сите подлоги кои работат на одреден јазол, како и ресурсите што ги бара секој под. И еве како изгледаат дневниците на распоредувачот кога ќе се стартува cronjob-cron-events-1573793820-xt6q9 pod (оваа информација ќе се појави во дневникот на распоредувачот кога 10-тото ниво на евиденција е поставено во аргументите на командата за стартување -v=10 ):
дневник
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
Овде гледаме дека на почетокот распоредувачот филтрира и генерира листа од 3 јазли на кои може да се стартува (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Потоа ги пресметува оценките врз основа на неколку параметри (вклучувајќи BalancedResourceAllocation, LeastResourceAllocation) за секој од овие јазли со цел да го одреди најсоодветниот јазол. На крајот на краиштата, подлогата е закажана на јазолот со најголем број поени (овде два јазли одеднаш имаат ист број на точки 100037, така што се избира случаен избор - nxs-k8s-s10).
Излез: ако некој јазол работи подови за кои не се поставени ограничувања, тогаш за k8s (од гледна точка на потрошувачката на ресурси) ова ќе биде еквивалентно на тоа како воопшто да нема такви места на овој јазол. Затоа, ако, условно, имате подлога со лаком процес (на пример, wowza) и не се поставени ограничувања за тоа, тогаш може да се појави ситуација кога овој под всушност ги изел сите ресурси на јазолот, но за k8s овој јазол се смета за растоварен и ќе му бидат доделени ист број на поени при рангирањето (точно во поени за проценување на расположливите ресурси) како јазол кој нема работни подлоги, што на крајот може да доведе до нерамномерна распределба на оптоварувањето помеѓу јазлите.
Иселување на Под
Како што знаете, на секој дел му е доделена една од 3-те QoS класи:
гарантирано - се доделува кога за секој контејнер во подлогата се наведени барање и ограничување за меморијата и процесорот, и овие вредности мора да се совпаѓаат
пукна — барем еден контејнер во подлогата има барање и ограничување, со барање < ограничување
најдобриот напор - кога ниту еден контејнер во подлогата не е ограничен со ресурси
Во исто време, кога еден јазол ќе доживее недостаток на ресурси (диск, меморија), kubelet почнува да ги рангира и исфрла подлогите според специфичен алгоритам кој го зема предвид приоритетот на подот и неговата QoS класа. На пример, ако зборуваме за RAM меморија, тогаш врз основа на класата QoS, поени се доделуваат според следниот принцип:
Оние. со истиот приоритет, кубелетот прво ќе ги исфрли подовите со најдобар напор QoS класата од јазолот.
Излез: ако сакате да ја намалите веројатноста саканиот pod да биде исфрлен од јазолот во случај на недостаток на ресурси на него, тогаш заедно со приоритетот, треба да се грижите и за поставување на барањето/лимитот за него.
Механизам за хоризонтално автоматско скалирање на апликативни подлоги (HPA)
Кога задачата е автоматски да се зголеми и намали бројот на места во зависност од употребата на ресурсите (систем - CPU/RAM или корисник - rps), таков ентитет k8s како ХХН (Horizontal Pod Autoscaler). Чиј алгоритам е како што следува:
Се одредуваат тековните читања на набљудуваниот ресурс (currentMetricValue)
Се одредуваат саканите вредности за ресурсот (desiredMetricValue), кои за системските ресурси се поставуваат со барање
Се одредува тековниот број на реплики (currentReplicas)
Следната формула го пресметува саканиот број на реплики (посакувани реплики)
саканиРеплики = [тековни реплики * (тековнаМетрична вредност / посакуванаМетрична вредност)]
Во овој случај, скалирањето нема да се случи кога коефициентот (currentMetricValue / саканиотMetricValue) е блиску до 1 (во овој случај, ние самите можеме да ја поставиме дозволената грешка; стандардно е 0.1).
Ајде да погледнеме како работи hpa користејќи го примерот на апликацијата за тестирање на апликации (опишана како Deployment), каде што е неопходно да се промени бројот на реплики во зависност од потрошувачката на процесорот:
Оние. гледаме дека подлогата за апликација првично е стартувана во два примери, од кои секоја содржи два контејнери nginx и nginx-извозник, за секој од нив одредено барања за процесорот.
Оние. Создадовме hpa што ќе го следи тестот за апликација за распоредување и ќе го регулира бројот на подови со апликацијата врз основа на индикаторот на процесорот (очекуваме дека подот треба да троши 30% проценти од процесорот што го бара), при што бројот на реплики е во опсег од 2-10.
Сега, да го погледнеме механизмот на работа на hpa ако нанесеме оптоварување на едно од огништата:
# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
app-test-78559f8f44-pgs58 101m 243Mi
app-test-78559f8f44-cj4jz 4m 240Mi
Севкупно го имаме следново:
Посакуваната вредност (desiredMetricValue) - според поставките на hpa, имаме 30%
Тековна вредност (currentMetricValue) - за пресметка, контролор-менаџер ја пресметува просечната вредност на потрошувачката на ресурси во %, т.е. условно го прави следново:
Добива апсолутни вредности на метрика на pod од метричкиот сервер, т.е. 101м и 4м
Ја пресметува просечната апсолутна вредност, т.е. (101м + 4м) / 2 = 53м
Ја добива апсолутната вредност за саканата потрошувачка на ресурси (за ова се сумираат барањата на сите контејнери) 60m + 30m = 90m
Го пресметува просечниот процент на потрошувачка на процесорот во однос на подлогата за барање, т.е. 53m / 90m * 100% = 59%
Сега имаме сè што ни треба за да утврдиме дали треба да го промениме бројот на реплики за да го направиме ова, го пресметуваме коефициентот:
ratio = 59% / 30% = 1.96
Оние. бројот на реплики треба да се зголеми за ~ 2 пати и да изнесува [2 * 1.96] = 4.
Заклучок: Како што можете да видите, за да може овој механизам да работи, неопходен услов е присуството на барања за сите контејнери во набљудуваната подлога.
Механизам за хоризонтално автоматско скалирање на јазли (Cluster Autoscaler)
За да се неутрализира негативното влијание врз системот за време на пренапони на оптоварување, не е доволно да се има конфигурирана hpa. На пример, според поставките во менаџерот на hpa контролер, тој одлучува дека бројот на реплики треба да се зголеми за 2 пати, но јазлите немаат бесплатни ресурси за да работат таков број на подови (т.е. јазолот не може да обезбеди бараните ресурси во подлогата за барања) и овие подлоги се префрлаат во состојба на чекање.
Во овој случај, ако давателот има соодветен IaaS/PaaS (на пример, GKE/GCE, AKS, EKS, итн.), алатка како Автоматско мерење на јазли. Ви овозможува да го поставите максималниот и минималниот број на јазли во кластерот и автоматски да го приспособите тековниот број на јазли (со повикување на API на провајдерот на облакот за да нарачате/отстранете јазол) кога има недостаток на ресурси во кластерот и местата не може да се закаже (се во состојба на чекање).
Заклучок: За да може автоматски да се скалира јазлите, неопходно е да се постават барања во контејнерите на подлогата за k8s да можат правилно да го проценат оптоварувањето на јазлите и соодветно да пријават дека нема ресурси во кластерот за лансирање на следниот pod.
Заклучок
Треба да се забележи дека поставувањето ограничувања на ресурсите на контејнерот не е услов за апликацијата да работи успешно, но сепак е подобро да се направи тоа од следниве причини:
За попрецизно работење на распоредувачот во однос на балансирање на оптоварувањето помеѓу k8s јазлите
За да се намали веројатноста за настанување на „иселување на подлогата“.
За да функционира хоризонталното автоматско скалирање на подлогите за апликации (HPA).
За хоризонтално автоматско скалирање на јазли (Cluster Autoscaling) за провајдери на облак