ProHoster > Bloc > Administració > Kubernetes: per què és tan important configurar la gestió de recursos del sistema?
Kubernetes: per què és tan important configurar la gestió de recursos del sistema?
Per regla general, sempre hi ha la necessitat de proporcionar un conjunt de recursos dedicats a una aplicació per al seu funcionament correcte i estable. Però, què passa si s'executen diverses aplicacions amb la mateixa potència? Com dotar a cadascun d'ells dels recursos mínims necessaris? Com es pot limitar el consum de recursos? Com distribuir correctament la càrrega entre els nodes? Com assegurar-se que el mecanisme d'escala horitzontal funciona si la càrrega de l'aplicació augmenta?
Heu de començar amb quins tipus principals de recursos existeixen al sistema: això, per descomptat, és el temps del processador i la memòria RAM. En els manifests k8s, aquests tipus de recursos es mesuren en les unitats següents:
CPU - en nuclis
RAM - en bytes
A més, per a cada recurs es poden establir dos tipus de requisits: peticions и límits. Sol·licituds: descriu els requisits mínims per als recursos lliures d'un node per executar un contenidor (i un pod en conjunt), mentre que els límits estableixen un límit dur als recursos disponibles per al contenidor.
És important entendre que el manifest no ha de definir explícitament els dos tipus, però el comportament serà el següent:
Si només s'especifiquen explícitament els límits d'un recurs, aleshores les sol·licituds d'aquest recurs prenen automàticament un valor igual als límits (podeu verificar-ho trucant a les entitats descriu). Aquells. de fet, el contenidor estarà limitat a la mateixa quantitat de recursos que necessita per funcionar.
Si només s'especifiquen de manera explícita les sol·licituds per a un recurs, no s'estableixen restriccions superiors en aquest recurs, és a dir. el contenidor només està limitat pels recursos del propi node.
També és possible configurar la gestió de recursos no només a nivell d'un contenidor específic, sinó també a nivell d'espai de noms mitjançant les entitats següents:
Interval límit — descriu la política de restricció a nivell de contenidor/pod en ns i és necessària per descriure els límits predeterminats del contenidor/pod, així com evitar la creació de contenidors/pods òbviament grassos (o viceversa), limitar-ne el nombre i determinar la possible diferència en els valors en límits i peticions
Quotes de recursos — descriure la política de restricció en general per a tots els contenidors en ns i s'utilitza, per regla general, per delimitar recursos entre entorns (útil quan els entorns no estan estrictament delimitats a nivell de node)
Els següents són exemples de manifests que estableixen límits de recursos:
Aquells. en aquest cas, per executar un contenidor amb nginx, necessitareu almenys 1 G de RAM lliure i 0.2 CPU al node, mentre que com a màxim el contenidor pot consumir 0.2 CPU i tota la RAM disponible al node.
Aquells. la suma de tots els contenidors de sol·licitud en els ns predeterminats no pot superar els 300 m per a la CPU i 1 G per a l'OP, i la suma de tots els límits és de 700 m per a la CPU i 2G per a l'OP.
Aquells. a l'espai de noms predeterminat per a tots els contenidors, la sol·licitud s'establirà en 100 m per a CPU i 1G per a OP, límit: 1 CPU i 2G. Al mateix temps, també s'estableix un límit als valors possibles en petició/límit per a CPU (50m < x < 2) i RAM (500M < x < 4G).
Aquells. per a cada pod del ns predeterminat hi haurà un límit de 4 vCPU i 1G.
Ara m'agradaria dir-vos quins avantatges ens pot donar establir aquestes restriccions.
Mecanisme d'equilibri de càrrega entre nodes
Com sabeu, el component k8s és responsable de la distribució de pods entre nodes, com ara planificador, que funciona segons un algorisme específic. Aquest algorisme passa per dues etapes a l'hora de seleccionar el node òptim per llançar:
filtració
Avançant
Aquells. d'acord amb la política descrita, inicialment es seleccionen nodes sobre els quals és possible llançar un pod basat en un conjunt predicats (inclosa la comprovació de si el node té prou recursos per executar el pod - PodFitsResources), i després per a cadascun d'aquests nodes, segons prioritats s'atorguen punts (incloent-hi, com més recursos lliures tingui un node, més punts s'assigna - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) i el pod es llança al node amb més punts (si diversos nodes compleixen aquesta condició alhora, aleshores se n'ha seleccionat un a l'atzar).
Al mateix temps, cal entendre que el planificador, quan avalua els recursos disponibles d'un node, es guia per les dades que s'emmagatzemen a etcd, és a dir. per a la quantitat del recurs sol·licitat/limitat de cada pod que s'executa en aquest node, però no pel consum real de recursos. Aquesta informació es pot obtenir de la sortida de l'ordre kubectl describe node $NODE, per exemple:
Aquí veiem tots els pods que s'executen en un node específic, així com els recursos que cada pod sol·licita. I aquí és com es veuen els registres del planificador quan s'inicia el pod cronjob-cron-events-1573793820-xt6q9 (aquesta informació apareixerà al registre del planificador quan establiu el desè nivell de registre als arguments de l'ordre d'inici -v=10):
registre
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
Aquí veiem que inicialment el planificador filtra i genera una llista de 3 nodes sobre els quals es pot llançar (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). A continuació, calcula puntuacions en funció de diversos paràmetres (incloent BalancedResourceAllocation, LeastResourceAllocation) per a cadascun d'aquests nodes per tal de determinar el node més adequat. En última instància, el pod es programa al node amb el nombre més alt de punts (aquí dos nodes alhora tenen el mateix nombre de punts 100037, de manera que se'n selecciona un a l'atzar: nxs-k8s-s10).
Sortida: si un node executa pods per als quals no s'estableixen restriccions, llavors per a k8s (des del punt de vista del consum de recursos) serà equivalent a com si no hi hagués cap pods en aquest node. Per tant, si, condicionalment, teniu un pod amb un procés glotós (per exemple, wowza) i no hi ha cap restricció establerta, aleshores es pot produir una situació quan aquest pod realment es menja tots els recursos del node, però per a k8s aquest node es considera descarregat i se li atorgarà el mateix nombre de punts a l'hora de classificar-se (precisament en punts que avaluen els recursos disponibles) que un node que no té pods de treball, cosa que en última instància pot provocar una distribució desigual de la càrrega entre nodes.
Desnonament de Pod
Com ja sabeu, a cada pod se li assigna una de les 3 classes de QoS:
garantida — s'assigna quan per a cada contenidor del pod s'especifiquen una sol·licitud i un límit per a la memòria i la CPU, i aquests valors han de coincidir
esclatable — Almenys un contenidor del pod té una sol·licitud i un límit, amb petició < límit
el millor esforç — quan ni un sol contenidor de la beina té recursos limitats
Al mateix temps, quan un node experimenta una manca de recursos (disc, memòria), kubelet comença a classificar i desallotjar els pods segons un algorisme específic que té en compte la prioritat del pod i la seva classe de QoS. Per exemple, si parlem de RAM, basant-nos en la classe QoS, els punts s'atorguen segons el principi següent:
Aquells. amb la mateixa prioritat, el kubelet desallotjarà primer les beines amb la millor classe de QoS del node.
Sortida: si voleu reduir la probabilitat que el pod desitjat sigui desallotjat del node en cas de falta de recursos en ell, aleshores, juntament amb la prioritat, també us heu de preocupar d'establir-ne la sol·licitud/límit.
Mecanisme per a l'escalat automàtic horitzontal de pods d'aplicació (HPA)
Quan la tasca és augmentar i disminuir automàticament el nombre de pods en funció de l'ús dels recursos (sistema - CPU/RAM o usuari - rps), una entitat k8s com ara HPA (Horizontal Pod Autoscaler). L'algorisme del qual és el següent:
Es determinen les lectures actuals del recurs observat (currentMetricValue)
Es determinen els valors desitjats per al recurs (desiredMetricValue), que per als recursos del sistema s'estableixen mitjançant la sol·licitud
Es determina el nombre actual de rèpliques (currentReplicas)
La fórmula següent calcula el nombre desitjat de rèpliques (desiredReplicas)
desitjatReplicas = [currentReplicas * ( currentMetricValue / wishMetricValue )]
En aquest cas, l'escala no es produirà quan el coeficient (currentMetricValue / wishMetricValue) sigui proper a 1 (en aquest cas, podem establir l'error admissible nosaltres mateixos; per defecte és 0.1).
Vegem com funciona hpa utilitzant l'exemple de l'aplicació app-test (descrita com a Deployment), on cal canviar el nombre de rèpliques en funció del consum de CPU:
Aquells. veiem que el pod d'aplicació es llança inicialment en dos casos, cadascun dels quals conté dos contenidors nginx i nginx-exporter, per a cadascun dels quals peticions per a la CPU.
Aquells. Hem creat un hpa que supervisarà la prova de l'aplicació de desplegament i ajustarà el nombre de pods amb l'aplicació en funció de l'indicador de la CPU (esperem que el pod consumeixi el 30% de la CPU que sol·licita), amb el nombre de rèpliques en el rang de 2-10.
Ara, mirem el mecanisme de funcionament de hpa si apliquem una càrrega a una de les llars:
# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
app-test-78559f8f44-pgs58 101m 243Mi
app-test-78559f8f44-cj4jz 4m 240Mi
En total tenim el següent:
El valor desitjat (desiredMetricValue): segons la configuració de hpa, tenim un 30%
Valor actual (currentMetricValue): per al càlcul, el gestor-controlador calcula el valor mitjà del consum de recursos en %, és a dir. condicionalment fa el següent:
Rep valors absoluts de mètriques pod del servidor de mètriques, és a dir. 101 m i 4 m
Calcula el valor absolut mitjà, és a dir. (101m + 4m) / 2 = 53m
Obté el valor absolut del consum de recursos desitjat (per a això, es resumeixen les peticions de tots els contenidors) 60m + 30m = 90m
Calcula el percentatge mitjà de consum de CPU en relació al pod de sol·licitud, és a dir. 53 m/90 m * 100 % = 59 %
Ara tenim tot el que necessitem per determinar si hem de canviar el nombre de rèpliques; per fer-ho, calculem el coeficient:
ratio = 59% / 30% = 1.96
Aquells. el nombre de rèpliques s'hauria d'augmentar en ~ 2 vegades i ascendir a [2 * 1.96] = 4.
Conclusió: Com podeu veure, perquè aquest mecanisme funcioni, una condició necessària és la presència de sol·licituds per a tots els contenidors del pod observat.
Mecanisme per a l'escalat automàtic horitzontal dels nodes (Cluster Autoscaler)
Per neutralitzar l'impacte negatiu en el sistema durant les pujades de càrrega, no n'hi ha prou amb un hpa configurat. Per exemple, segons la configuració del gestor del controlador hpa, decideix que el nombre de rèpliques s'ha d'augmentar 2 vegades, però els nodes no tenen recursos gratuïts per executar aquest nombre de pods (és a dir, el node no pot proporcionar el recursos sol·licitats al pod de sol·licituds) i aquests pods canvien a l'estat Pendent.
En aquest cas, si el proveïdor té un IaaS/PaaS corresponent (per exemple, GKE/GCE, AKS, EKS, etc.), una eina com ara Node Autoscaler. Us permet establir el nombre màxim i mínim de nodes al clúster i ajustar automàticament el nombre actual de nodes (trucant a l'API del proveïdor de núvol per demanar/eliminar un node) quan hi ha una manca de recursos al clúster i als pods. no es poden programar (estan a l'estat Pendent).
Conclusió: Per poder escalar els nodes automàticament, cal establir sol·licituds als contenidors de pods perquè els k8s puguin avaluar correctament la càrrega dels nodes i, en conseqüència, informar que no hi ha recursos al clúster per llançar el següent pod.
Conclusió
Cal tenir en compte que establir límits de recursos del contenidor no és un requisit perquè l'aplicació s'executi correctament, però encara és millor fer-ho per les raons següents:
Per a un funcionament més precís del planificador pel que fa a l'equilibri de càrrega entre els nodes k8s
Per reduir la probabilitat que es produeixi un esdeveniment de "desnonament de la beina".
Perquè funcioni l'escalat automàtic horitzontal dels pods d'aplicacions (HPA).
Per a l'escalat automàtic horitzontal de nodes (Cluster Autoscaling) per a proveïdors de núvol