In der Regel besteht immer die Notwendigkeit, einem bestimmten Anwendung einen dedizierten Pool von Ressourcen zur VerfĂŒgung zu stellen, um deren korrekte und stabile Funktion zu gewĂ€hrleisten. Aber was passiert, wenn mehrere Anwendungen auf denselben Ressourcen laufen? Wie stellt man sicher, dass jede von ihnen mit den minimal notwendigen Ressourcen ausgestattet ist? Wie kann man den Ressourcenverbrauch einschrĂ€nken? Wie verteilt man die Last sinnvoll zwischen den Knoten? Wie gewĂ€hrleistet man die FunktionsfĂ€higkeit des horizontalen Skalierungssystems bei steigender Last auf den Anwendungen?

Beginnen wir mit den grundlegenden Ressourcentypen im System â hierzu zĂ€hlen natĂŒrlich die CPU-Zeit und der Arbeitsspeicher. In den k8s-Mannifests werden diese Ressourcentypen in folgenden Einheiten gemessen:
- CPU â in Kernen
- RAM â in Bytes
FĂŒr jede Ressource gibt es zudem die Möglichkeit, zwei Arten von Anforderungen zu definieren â requests und Limits. Requests â beschreibt die minimalen Anforderungen an die verfĂŒgbaren Ressourcen des Knotens fĂŒr den Start des Containers (und des Pods insgesamt), wĂ€hrend Limits eine feste Obergrenze fĂŒr die verfĂŒgbare Ressourcenmenge des Containers festlegt.
Es ist wichtig zu verstehen, dass im Manifest nicht unbedingt beide Typen eindeutig definiert werden mĂŒssen; das Verhalten wird wie folgt sein:
- Wenn nur die Limits der Ressource explizit angegeben sind, nimmt die Requests fĂŒr diese Ressource automatisch den Wert der Limits an (dies kann ĂŒberprĂŒft werden, indem die EntitĂ€t beschreiben wird). Das heiĂt, die tatsĂ€chliche Nutzung des Containers wird durch die Menge der Ressourcen begrenzt, die er fĂŒr seinen Start benötigt.
- Wenn fĂŒr die Ressource nur die Requests explizit angegeben sind, werden keine oberen Begrenzungen fĂŒr diese Ressource festgelegt â das heiĂt, der Container ist nur durch die Ressourcen des Knotens begrenzt.
Es besteht auch die Möglichkeit, das Ressourcenmanagement nicht nur auf der Ebene eines bestimmten Containers, sondern auch auf der Ebene des Namespace mit Hilfe der folgenden EntitÀten zu konfigurieren:
- LimitRange â beschreibt die Begrenzungspolitik auf der Container-/Pod-Ebene im Namespace und ist notwendig, um die Standardgrenzen fĂŒr Container/Pods sowie zur Verhinderung der Erstellung ĂŒbermĂ€Ăig groĂer Container/Pods (oder umgekehrt) zu beschreiben, die Anzahl zu begrenzen und mögliche Unterschiede in den Werten von Limits und Requests zu definieren.
- ResourceQuotas â beschreibt die EinschrĂ€nkungspolitik fĂŒr alle Container im Namespace und dient in der Regel der Ressourcenzuweisung innerhalb von Umgebungen (nĂŒtzlich, wenn die Umgebungen auf Knotenebene nicht strikt getrennt sind).
Nachfolgend sind Beispiele fĂŒr Manifestdateien aufgefĂŒhrt, in denen Ressourcengrenzen festgelegt werden:
Auf der Ebene eines bestimmten Containers:
containers: - name: app-nginx image: nginx resources: requests: memory: 1Gi limits: cpu: 200mDas bedeutet, dass beim Starten eines Containers mit nginx mindestens 1 GB RAM und 0,2 CPU-KapazitĂ€t auf dem Knoten verfĂŒgbar sein mĂŒssen; der Container kann maximal 0,2 CPU und den gesamten verfĂŒgbaren RAM auf dem Knoten nutzen.
Auf der Ebene des gesamten Namespace:
apiVersion: v1 kind: ResourceQuota metadata: name: nxs-test spec: hard: requests.cpu: 300m requests.memory: 1Gi limits.cpu: 700m limits.memory: 2GiDas heiĂt, die Summe aller Anforderungen der Container im Standard-Namespace darf 300m fĂŒr CPU und 1G fĂŒr RAM nicht ĂŒberschreiten, die Summe aller Limits betrĂ€gt 700m fĂŒr CPU und 2G fĂŒr RAM.
StandardbeschrĂ€nkungen fĂŒr Container im Namespace:
apiVersion: v1 kind: LimitRange metadata: name: nxs-limit-per-container spec: limits: - type: Container defaultRequest: cpu: 100m memory: 1Gi default: cpu: 1 memory: 2Gi min: cpu: 50m memory: 500Mi max: cpu: 2 memory: 4GiDas bedeutet, dass im Standard-Namespace fĂŒr alle Container standardmĂ€Ăig eine Anforderung von 100m fĂŒr die CPU und 1G fĂŒr den RAM sowie ein Limit von 1 CPU und 2G festgelegt werden. Zudem gibt es BeschrĂ€nkungen fĂŒr mögliche Werte in den Anforderungs- und Grenzwerten fĂŒr CPU (50m < x < 2) und RAM (500M < x < 4G).
EinschrÀnkungen auf Pod-Ebene ns:
apiVersion: v1 kind: LimitRange metadata: name: nxs-limit-pod spec: limits: - type: Pod max: cpu: 4 memory: 1GiDas bedeutet, dass fĂŒr jeden Pod im Standard-Namespace eine EinschrĂ€nkung von 4 vCPU und 1G festgelegt wird.
Nun möchte ich erlĂ€utern, welche Vorteile die Festlegung dieser Grenzen fĂŒr uns haben kann.
Der Mechanismus zur Lastverteilung zwischen den Knoten
Wie bekannt ist, ist fĂŒr die Verteilung der Pods auf die Knoten ein K8s-Komponent zustĂ€ndig, der Scheduler, der nach einem bestimmten Algorithmus arbeitet. Dieser Algorithmus durchlĂ€uft bei der Auswahl des optimalen Knotens zur AusfĂŒhrung zwei Phasen:
- Filterung
- Rankings
Das bedeutet, dass gemÀà der beschriebenen Politik zunĂ€chst Knoten ausgewĂ€hlt werden, auf denen die AusfĂŒhrung des Pods möglich ist, basierend auf einem Satz von PrĂ€dikaten (darunter wird auch geprĂŒft, ob der Knoten genĂŒgend Ressourcen fĂŒr die AusfĂŒhrung des Pods hat â PodFitsResources), und dann werden fĂŒr jeden dieser Knoten gemÀà den PrioritĂ€ten Es werden Punkte vergeben (unter anderem, je mehr freie Ressourcen ein Knoten hat, desto mehr Punkte werden ihm zugewiesen â LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) und erfolgt der Start auf dem Knoten mit der höchsten Punktzahl (sofern mehrere Knoten diese Bedingung erfĂŒllen, wird einer von ihnen zufĂ€llig ausgewĂ€hlt).
Es ist wichtig zu verstehen, dass der Scheduler bei der Bewertung der verfĂŒgbaren Ressourcen des Knotens auf die in etcd gespeicherten Daten zurĂŒckgreift â d.h. auf die Summe von requested/limit der Ressourcen jedes Pods, der auf diesem Knoten lĂ€uft, jedoch nicht auf den tatsĂ€chlichen Ressourcenverbrauch. Diese Informationen können durch den Befehl abgerufen werden: kubectl describe node $NODE, zum Beispiel:
# kubectl describe nodes nxs-k8s-s1
..
Non-terminated Pods: (9 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
ingress-nginx nginx-ingress-controller-754b85bf44-qkt2t 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system kube-flannel-26bl4 150m (0%) 300m (1%) 64M (0%) 500M (1%) 233d
kube-system kube-proxy-exporter-cb629 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system kube-proxy-x9fsc 0 (0%) 0 (0%) 0 (0%) 0 (0%) 233d
kube-system nginx-proxy-k8s-worker-s1 25m (0%) 300m (1%) 32M (0%) 512M (1%) 233d
nxs-monitoring alertmanager-main-1 100m (0%) 100m (0%) 425Mi (1%) 25Mi (0%) 233d
nxs-logging filebeat-lmsmp 100m (0%) 0 (0%) 100Mi (0%) 200Mi (0%) 233d
nxs-monitoring node-exporter-v4gdq 112m (0%) 122m (0%) 200Mi (0%) 220Mi (0%) 233d
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 487m (3%) 822m (5%)
memory 15856217600 (2%) 749976320 (3%)
ephemeral-storage 0 (0%) 0 (0%)Hier sehen wir alle Pods, die auf einem bestimmten Knoten ausgefĂŒhrt werden, sowie die Ressourcen, die jeder Pod anfordert. So sehen die Logs des Schedulers beim Starten des Pods cronjob-cron-events-1573793820-xt6q9 aus (diese Informationen erscheinen im Log des Schedulers, wenn das Logging-Niveau auf 10 in den Startparametern gesetzt wird âv=10):
Log
I1115 07:57:21.637791 1 scheduling_queue.go:908] Versuch, Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 zu planen
I1115 07:57:21.637804 1 scheduler.go:453] Versuch, Pod zu planen: nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.638285 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s5 ist erlaubt, Node lÀuft nur 16 von 110 Pods.
I1115 07:57:21.638300 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s6 ist erlaubt, Node lÀuft nur 20 von 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s3 ist erlaubt, Node lÀuft nur 20 von 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s4 ist erlaubt, Node lÀuft nur 17 von 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s10 ist erlaubt, Node lÀuft nur 16 von 110 Pods.
I1115 07:57:21.638365 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s12 ist erlaubt, Node lÀuft nur 9 von 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s11 ist erlaubt, Node lÀuft nur 11 von 110 Pods.
I1115 07:57:21.638385 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s1 ist erlaubt, Node lÀuft nur 19 von 110 Pods.
I1115 07:57:21.638402 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s2 ist erlaubt, Node lÀuft nur 21 von 110 Pods.
I1115 07:57:21.638383 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s9 ist erlaubt, Node lÀuft nur 16 von 110 Pods.
I1115 07:57:21.638335 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s8 ist erlaubt, Node lÀuft nur 18 von 110 Pods.
I1115 07:57:21.638408 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s13 ist erlaubt, Node lÀuft nur 8 von 110 Pods.
I1115 07:57:21.638478 1 predicates.go:1369] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s10 ist erlaubt, die Antipod-affinitĂ€tsbedingungen sind erfĂŒllt.
I1115 07:57:21.638505 1 predicates.go:1369] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s8 ist erlaubt, die Antipod-affinitĂ€tsbedingungen sind erfĂŒllt.
I1115 07:57:21.638577 1 predicates.go:1369] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s9 ist erlaubt, die Antipod-affinitĂ€tsbedingungen sind erfĂŒllt.
I1115 07:57:21.638583 1 predicates.go:829] Planung des Pods nxs-stage/cronjob-cron-events-1573793820-xt6q9 auf Node nxs-k8s-s7 ist erlaubt, Node lÀuft nur 25 von 110 Pods.
I1115 07:57:21.638932 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, KapazitÀt 39900 millicores 66620178432 Speicherbytes, Gesamtanforderung 2343 millicores 9640186880 Speicherbytes, Punktestand 9
I1115 07:57:21.638946 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, KapazitÀt 39900 millicores 66620178432 Speicherbytes, Gesamtanforderung 2343 millicores 9640186880 Speicherbytes, Punktestand 8
I1115 07:57:21.638961 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, KapazitÀt 39900 millicores 66620170240 Speicherbytes, Gesamtanforderung 4107 millicores 11307422720 Speicherbytes, Punktestand 9
I1115 07:57:21.638971 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, KapazitÀt 39900 millicores 66620178432 Speicherbytes, Gesamtanforderung 5847 millicores 24333637120 Speicherbytes, Punktestand 7
I1115 07:57:21.638975 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, KapazitÀt 39900 millicores 66620170240 Speicherbytes, Gesamtanforderung 4107 millicores 11307422720 Speicherbytes, Punktestand 8
I1115 07:57:21.638990 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, KapazitÀt 39900 millicores 66620178432 Speicherbytes, Gesamtanforderung 5847 millicores 24333637120 Speicherbytes, Punktestand 7
I1115 07:57:21.639022 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Punktestand: (10)
I1115 07:57:21.639030 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Punktestand: (10)
I1115 07:57:21.639034 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Punktestand: (10)
I1115 07:57:21.639041 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Punktestand: (0)
I1115 07:57:21.639053 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Punktestand: (0)
I1115 07:57:21.639059 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Punktestand: (0)
I1115 07:57:21.639061 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Punktestand: (0)
I1115 07:57:21.639063 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639073 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Punktestand: (0)
I1115 07:57:21.639077 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639085 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Punktestand: (0)
I1115 07:57:21.639088 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639103 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639109 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639114 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Punktestand: (10)
I1115 07:57:21.639127 1 generic_scheduler.go:781] Host nxs-k8s-s10 => Punktestand 100037
I1115 07:57:21.639150 1 generic_scheduler.go:781] Host nxs-k8s-s8 => Punktestand 100034
I1115 07:57:21.639154 1 generic_scheduler.go:781] Host nxs-k8s-s9 => Punktestand 100037
I1115 07:57:21.639267 1 scheduler_binder.go:269] AssumePodVolumes fĂŒr Pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", Node "nxs-k8s-s10"
I1115 07:57:21.639286 1 scheduler_binder.go:279] AssumePodVolumes fĂŒr Pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", Node "nxs-k8s-s10": Alle PVCs gebunden und nichts zu tun
I1115 07:57:21.639333 1 factory.go:733] Versuch, cronjob-cron-events-1573793820-xt6q9 an nxs-k8s-s10 zu bindenHier sehen wir, dass der Scheduler zunĂ€chst die Nodes filtert und eine Liste von drei Nodes erstellt, auf denen eine AusfĂŒhrung möglich ist (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). AnschlieĂend werden die Punkte nach verschiedenen Parametern (einschlieĂlich BalancedResourceAllocation, LeastResourceAllocation) fĂŒr jede dieser Nodes gezĂ€hlt, um den am besten geeigneten Knoten zu bestimmen. Letztendlich wird der Plan auf dem Knoten mit der höchsten Punktzahl erstellt (hier haben gleich zwei Nodes die gleiche Punktzahl von 100037, daher wird zufĂ€llig einer von ihnen ausgewĂ€hlt â nxs-k8s-s10).
Fazit: Wenn auf einem Knoten Pods laufen, fĂŒr die keine EinschrĂ€nkungen festgelegt sind, entspricht das aus Sicht der Ressourcennutzung in K8s dem Zustand, als wĂ€ren solche Pods auf diesem Knoten ĂŒberhaupt nicht vorhanden. Daher kann es, wenn Sie zum Beispiel einen Pod mit einer ressourcenintensiven Anwendung (wie wowza) haben und fĂŒr diesen keine EinschrĂ€nkungen definiert sind, zu einer Situation kommen, in der dieser Pod tatsĂ€chlich alle Ressourcen des Knotens verbraucht hat, aber K8s diesen Knoten als nicht ĂŒberlastet betrachtet. In der Folge erhĂ€lt dieser Knoten die gleiche Punktzahl bei der Bewertung (insbesondere in Bezug auf die verfĂŒgbaren Ressourcen) wie ein Knoten, auf dem keine aktiven Pods laufen, was letztlich zu einer ungleichmĂ€Ăigen Lastverteilung zwischen den Knoten fĂŒhren kann.
Pod kĂŒndigen
Wie bekannt ist, wird jedem Pod eine der 3 QoS-Klassen zugewiesen:
- guaranteed â wird zugewiesen, wenn fĂŒr jeden Container im Pod sowohl Memory- als auch CPU-Requests und -Limits festgelegt sind, wobei diese Werte ĂŒbereinstimmen mĂŒssen
- burstable â wenn mindestens ein Container im Pod Requests und Limits hat, wobei der Request < Limit ist
- best effort â wenn kein Container im Pod hinsichtlich der Ressourcen eingeschrĂ€nkt ist
Wenn auf einem Node Ressourcenmangel (Speicherplatz, RAM) festgestellt wird, beginnt der Kubelet, Pods nach einem bestimmten Algorithmus zu priorisieren und auszusiedeln, der die PrioritĂ€t des Pods und seine QoS-Klasse berĂŒcksichtigt. Zum Beispiel, wenn es um RAM geht, werden basierend auf der QoS-Klasse Punkte nach folgendem Prinzip vergeben:
- Guaranteed: -998
- BestEffort: 1000
- Burstable: min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)
Das bedeutet, dass der Kubelet bei gleichrangiger PrioritÀt zuerst die Pods mit der QoS-Klasse "Best Effort" aus dem Node ausgliedert.
Fazit: Wenn Sie die Wahrscheinlichkeit verringern möchten, dass ein benötigter Pod bei Ressourcenmangel vom Node entfernt wird, sollten Sie neben der PrioritĂ€t auch dafĂŒr sorgen, dass request/limit gesetzt werden.
Der Mechanismus zur horizontalen Skalierung von Anwendungs-Pods (HPA)
Wenn die Aufgabe darin besteht, die Anzahl der Pods automatisch zu erhöhen oder zu verringern, abhÀngig von der Ressourcennutzung (systemisch - CPU/RAM oder benutzerspezifisch - rps), kann eine EntitÀt wie HPA (Horizontal Pod Autoscaler) bei der Lösung helfen. Sein Algorithmus funktioniert wie folgt:
- Die aktuellen Werte der beobachteten Ressource (currentMetricValue) werden ermittelt.
- Die gewĂŒnschten Werte fĂŒr die Ressource (desiredMetricValue) werden fĂŒr Systemressourcen ĂŒber eine Anfrage festgelegt.
- Es wird die aktuelle Anzahl von Replikaten (currentReplicas) bestimmt.
- Die gewĂŒnschte Anzahl der Replikate (desiredReplicas) wird nach folgender Formel berechnet:
desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]
Es findet keine Skalierung statt, wenn der Faktor (currentMetricValue / desiredMetricValue) nahe bei 1 liegt (die zulĂ€ssige Abweichung kann von uns selbst festgelegt werden, sie betrĂ€gt standardmĂ€Ăig 0,1).
Betrachten wir die Funktionsweise von hpa am Beispiel der Anwendung app-test (beschrieben als Deployment), bei der die Anzahl der Replikate je nach CPU-Verbrauch geÀndert werden muss:
Manifest der Anwendung
kind: Deployment apiVersion: apps/v1beta2 metadata: name: app-test spec: selector: matchLabels: app: app-test replicas: 2 template: metadata: labels: app: app-test spec: containers: - name: nginx image: registry.nixys.ru/generic-images/nginx imagePullPolicy: Always resources: requests: cpu: 60m ports: - name: http containerPort: 80 - name: nginx-exporter image: nginx/nginx-prometheus-exporter resources: requests: cpu: 30m ports: - name: nginx-exporter containerPort: 9113 args: - -nginx.scrape-uri - http://127.0.0.1:80/nginx-statusDas bedeutet, wir sehen, dass die Anwendung ursprĂŒnglich mit zwei Instanzen gestartet wird, von denen jede zwei Container fĂŒr nginx und nginx-exporter enthĂ€lt, fĂŒr die jeweils festgelegt ist, requests fĂŒr die CPU.
HPA-Manifest
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: app-test-hpa spec: maxReplicas: 10 minReplicas: 2 scaleTargetRef: apiVersion: extensions/v1beta1 kind: Deployment name: app-test metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 30Das bedeutet, wir haben eine HPA erstellt, die den Deployment app-test ĂŒberwacht und die Anzahl der Pods mit der Anwendung basierend auf dem CPU-Wert reguliert (wir erwarten, dass der Pod 30 % der ihm zugewiesenen CPU benötigt), wobei die Anzahl der Replikate zwischen 2 und 10 liegt.
Nun betrachten wir den Mechanismus der HPA, wenn wir eine Last auf einen der Pods anwenden:
# kubectl top pod NAME CPU(cores) MEMORY(bytes) app-test-78559f8f44-pgs58 101m 243Mi app-test-78559f8f44-cj4jz 4m 240Mi
Insgesamt haben wir Folgendes:
- WĂŒnschenswerter Wert (desiredMetricValue) â gemÀà den HPA-Einstellungen betrĂ€gt dieser 30 %
- Aktueller Wert (currentMetricValue) â zur Berechnung ermittelt der controller-manager den Durchschnittswert des Ressourcenverbrauchs in %, d.h., vereinfacht gesagt, er macht Folgendes:
- Er erhÀlt die absoluten Werte der Metriken der Pods vom Metric-Server, d.h. 101m und 4m.
- Er berechnet den durchschnittlichen absoluten Wert, d.h. (101m + 4m) / 2 = 53m.
- Er erhĂ€lt den Absolutwert fĂŒr den gewĂŒnschten Ressourcenverbrauch (dabei werden die Requests aller Container summiert) 60m + 30m = 90m.
- Berechnet den durchschnittlichen Prozentsatz der CPU-Nutzung im VerhÀltnis zu den Pod-Requests, d.h. 53m / 90m * 100% = 59%.
Jetzt haben wir alles Notwendige, um zu bestimmen, ob die Anzahl der Replikate geÀndert werden muss; dazu berechnen wir den Faktor:
ratio = 59% / 30% = 1,96.
Das heiĂt, die Anzahl der Replikate sollte ungefĂ€hr verdoppelt werden und betrĂ€gt [2 * 1,96] = 4.
Fazit: Wie man sehen kann, ist eine Voraussetzung fĂŒr das Funktionieren dieses Mechanismus, dass Anfragen fĂŒr alle Container im beobachteten Pod vorhanden sind.
Mechanismus der horizontalen automatischen Skalierung von Knoten (Cluster Autoscaler).
Um die negativen Auswirkungen auf das System bei Lastspitzen zu minimieren, reicht die Einrichtung eines HPA möglicherweise nicht aus. Beispielsweise entscheidet der HPA-Controller-Manager gemÀà den Einstellungen, dass die Anzahl der Replikate verdoppelt werden muss, jedoch sind auf den Knoten keine freien Ressourcen verfĂŒgbar, um so viele Pods zu starten (d.h. der Knoten kann die angeforderten Ressourcen der Pod-Requests nicht bereitstellen), und diese Pods wechseln in den Status Pending.
In diesem Fall, wenn der Anbieter ĂŒber eine entsprechende IaaS/PaaS (z.B. GKE/GCE, AKS, EKS usw.) verfĂŒgt, kann uns ein solches Werkzeug wie Node Autoscalerhelfen. Er ermöglicht es, die maximale und minimale Anzahl von Knoten im Cluster festzulegen und die aktuelle Anzahl der Knoten automatisch zu regulieren (indem er ĂŒber die API des Cloud-Anbieters Knoten anfordert oder entfernt), wenn ein Ressourcenmangel im Cluster festgestellt wird und die Pods nicht geplant werden können (im Status Pending sind).
Fazit: FĂŒr die Möglichkeit der automatischen Skalierung der Knoten mĂŒssen in den Containern der Pods Requests definiert werden, damit k8s die Auslastung der Knoten korrekt bewerten und entsprechend melden kann, dass fĂŒr den Start eines weiteren Pods im Cluster nicht genĂŒgend Ressourcen vorhanden sind.
Fazit
Es ist zu beachten, dass die Festlegung von RessourcenbeschrĂ€nkungen fĂŒr den Container keine zwingende Voraussetzung fĂŒr den erfolgreichen Start der Anwendung ist, jedoch aus folgenden GrĂŒnden dennoch ratsam ist:
- Um eine genauere Arbeitsweise des Schedulers hinsichtlich der Lastverteilung zwischen den Knoten k8s zu ermöglichen
- Um die Wahrscheinlichkeit eines âPod-Evictionâ-Ereignisses zu reduzieren
- Um das horizontale Autoscaling der Anwendungs-Pods (HPA) zu ermöglichen
- FĂŒr das horizontale automatisierte Skalieren von Knoten (Cluster-Autoscaling) bei Cloud-Anbietern
Lesen Sie auch andere Artikel in unserem Blog:
Quelle: habr.com
