Kubernetes: de ce este atât de important să configurăm gestionarea resurselor sistemului?

De obicei, apare întotdeauna necesitatea de a asigura un grup dedicat de resurse pentru o anumită aplicație, pentru a funcționa corect și stabil. Dar ce se întâmplă dacă mai multe aplicații rulează pe aceleași resurse? Cum putem asigura resursele minim necesare pentru fiecare dintre ele? Cum putem limita consumul de resurse? Cum putem distribui corect încărcătura între noduri? Cum putem asigura funcționarea mecanismului de scalare orizontală în cazul creșterii încărcăturii pe aplicații?

Kubernetes: de ce este atât de important să configurăm gestionarea resurselor sistemului?

Trebuie să începem cu tipurile principale de resurse care există în sistem — acestea sunt, desigur, timpul CPU și memoria RAM. În manifestele k8s, aceste tipuri de resurse sunt măsurate în următoarele unități:

  • CPU — în nuclee
  • RAM — în octeți

Pentru fiecare resursă există posibilitatea de a defini două tipuri de cerințe — requests. și limits. Requests — descrie cerințele minime pentru resursele disponibile ale nodului pentru a porni un container (și podul în general), în timp ce limits stabilește o limită strictă a resurselor disponibile pentru container.

Este important de înțeles că în manifest nu este obligatoriu să definim în mod explicit ambele tipuri, comportamentul va fi următorul:

  • Dacă este specificat explicit doar limits pentru o resursă, atunci requests pentru acea resursă va prelua automat valoarea egală cu limits (acest lucru se poate verifica prin apelarea descrierii entității). Adică, în fapt, funcționarea containerului va fi limitată la aceeași cantitate de resurse pe care le solicită pentru a porni.
  • Dacă pentru o resursă este specificat explicit doar requests, nu se impun restricții suplimentare pe acea resursă — adică, containerul este limitat doar de resursele nodului însuși.

De asemenea, există posibilitatea de a configura gestionarea resurselor nu doar la nivelul unui container specific, ci și la nivelul namespace-ului folosind următoarele entități:

  • LimitRange — descrie politica de limitare la nivelul containerului/podului în ns și este necesară pentru a descrie limitările implicite pentru container/pod, precum și pentru a preveni crearea containerelor/podurilor excesiv de mari (sau invers), limitându-le numărul și stabilind o diferență posibilă între valori în limits și requests.
  • ResourceQuotas — descrie politica de restricție în general pentru toate containerele din ns și este folosită, în mod obișnuit, pentru a delimita resursele pe medii (utilă atunci când mediile nu sunt strict separate la nivel de noduri)

Mai jos sunt exemple de manifeste în care sunt stabilite limitele pentru resurse:

  • La nivelul unui container specific:

    containers:
    - name: app-nginx
      image: nginx
      resources:
        requests:
          memory: 1Gi
        limits:
          cpu: 200m

    Adică, în acest caz, pentru a lansa un container cu nginx, este necesară o capacitate liberă de cel puțin 1G RAM și 0.2 CPU pe nod, iar maximul pe care îl poate utiliza containerul este 0.2 CPU și întreaga memorie disponibilă pe nod.

  • La nivelul întregului ns:

    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: nxs-test
    spec:
      hard:
        requests.cpu: 300m
        requests.memory: 1Gi
        limits.cpu: 700m
        limits.memory: 2Gi

    Adică, suma tuturor cererilor containerelor din ns-ul implicit nu poate depăși 300m pentru CPU și 1G pentru RAM, iar suma tuturor limitelor nu poate depăși 700m pentru CPU și 2G pentru RAM.

  • Limitele implicite pentru containere în ns:

    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: 4Gi

    Adică, în namespace-ul implicit, pentru toate containerele va fi setată în mod implicit o cerere de 100m pentru CPU și 1G pentru RAM, limita fiind de 1 CPU și 2G. De asemenea, sunt stabilite limitele pentru valorile posibile în cerere/limită pentru CPU (50m < x < 2) și RAM (500M < x < 4G).

  • Limitele la nivelul podurilor din ns:

    apiVersion: v1
    kind: LimitRange
    metadata:
     name: nxs-limit-pod
    spec:
     limits:
     - type: Pod
       max:
         cpu: 4
         memory: 1Gi

    Adică, pentru fiecare pod din ns-ul implicit va fi stabilită o limită de 4 vCPU și 1G.

Acum aș dori să discut despre avantajele pe care ni le poate aduce stabilirea acestor limite.

Mecanismul de echilibrare a încărcăturii între noduri

După cum se știe, distribuția podurilor pe noduri este gestionată de un component k8s, numit scheduler, care funcționează pe baza unui algoritm specific. Acest algoritm, în procesul de alegere a nodului optim pentru lansare, parcurge două etape:

  1. Filtrare
  2. Clasificare

Adică, conform politicii descrise, inițial sunt selectate nodurile pe care este posibil să se lanseze poduri pe baza unui set de predicates (inclusiv verificând dacă nodul are suficiente resurse pentru a lansa podul — PodFitsResources), iar apoi, pentru fiecare dintre aceste noduri, conform priorities se acordă puncte (inclusiv, cu cât nodul are mai multe resurse libere, cu atât mai multe puncte i se alocă — LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) și se rulează pe nodul cu cel mai mare număr de puncte (dacă mai multe noduri îndeplinesc această condiție, se alege unul aleatoriu dintre ele).

Trebuie să înțelegem că scheduler-ul, în evaluarea resurselor disponibile ale nodului, se bazează pe datele stocate în etcd — adică pe suma resurselor solicitate/limitate ale fiecărui pod, care rulează pe acest nod, dar nu pe consumul efectiv al resurselor. Această informație poate fi obținută din ieșirea comenzii kubectl describe node $NODE, de exemplu:

# 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%)

Aici vedem toate podurile rulate pe un anumit nod, precum și resursele solicitate de fiecare dintre ele. Iată cum arată log-urile scheduler-ului în timpul rulării pod-ului cronjob-cron-events-1573793820-xt6q9 (această informație va apărea în log-ul scheduler-ului la setarea nivelului 10 de logare în argumentele comenzii de execuție —v=10):

log

I1115 07:57:21.637791 1 scheduling_queue.go:908] Se pregătește să încerce să programeze podul nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.637804 1 scheduler.go:453] Încercând să programeze podul: nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.638285 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s5 este permisă, nodul rulează doar 16 din 110 poduri.
I1115 07:57:21.638300 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s6 este permisă, nodul rulează doar 20 din 110 poduri.
I1115 07:57:21.638322 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s3 este permisă, nodul rulează doar 20 din 110 poduri.
I1115 07:57:21.638322 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s4 este permisă, nodul rulează doar 17 din 110 poduri.
I1115 07:57:21.638334 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s10 este permisă, nodul rulează doar 16 din 110 poduri.
I1115 07:57:21.638365 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s12 este permisă, nodul rulează doar 9 din 110 poduri.
I1115 07:57:21.638334 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s11 este permisă, nodul rulează doar 11 din 110 poduri.
I1115 07:57:21.638385 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s1 este permisă, nodul rulează doar 19 din 110 poduri.
I1115 07:57:21.638402 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s2 este permisă, nodul rulează doar 21 din 110 poduri.
I1115 07:57:21.638383 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s9 este permisă, nodul rulează doar 16 din 110 poduri.
I1115 07:57:21.638335 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s8 este permisă, nodul rulează doar 18 din 110 poduri.
I1115 07:57:21.638408 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s13 este permisă, nodul rulează doar 8 din 110 poduri.
I1115 07:57:21.638478 1 predicates.go:1369] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s10 este permisă, condițiile anti-afinitate pentru podurile existente sunt satisfăcute.
I1115 07:57:21.638505 1 predicates.go:1369] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s8 este permisă, condițiile anti-afinitate pentru podurile existente sunt satisfăcute.
I1115 07:57:21.638577 1 predicates.go:1369] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s9 este permisă, condițiile anti-afinitate pentru podurile existente sunt satisfăcute.
I1115 07:57:21.638583 1 predicates.go:829] Programarea podului nxs-stage/cronjob-cron-events-1573793820-xt6q9 pe nodul nxs-k8s-s7 este permisă, nodul rulează doar 25 din 110 poduri.
I1115 07:57:21.638932 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacitate 39900 milicore 66620178432 octeți de memorie, cerere totală 2343 milicore 9640186880 octeți de memorie, scor 9
I1115 07:57:21.638946 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacitate 39900 milicore 66620178432 octeți de memorie, cerere totală 2343 milicore 9640186880 octeți de memorie, scor 8
I1115 07:57:21.638961 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacitate 39900 milicore 66620170240 octeți de memorie, cerere totală 4107 milicore 11307422720 octeți de memorie, scor 9
I1115 07:57:21.638971 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacitate 39900 milicore 66620178432 octeți de memorie, cerere totală 5847 milicore 24333637120 octeți de memorie, scor 7
I1115 07:57:21.638975 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacitate 39900 milicore 66620170240 octeți de memorie, cerere totală 4107 milicore 11307422720 octeți de memorie, scor 8
I1115 07:57:21.638990 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacitate 39900 milicore 66620178432 octeți de memorie, cerere totală 5847 milicore 24333637120 octeți de memorie, scor 7
I1115 07:57:21.639022 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Scor: (10)
I1115 07:57:21.639030 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Scor: (10)
I1115 07:57:21.639034 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Scor: (10)
I1115 07:57:21.639041 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Scor: (0)
I1115 07:57:21.639053 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Scor: (0)
I1115 07:57:21.639059 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Scor: (0)
I1115 07:57:21.639061 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Scor: (0)
I1115 07:57:21.639063 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639073 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Scor: (0)
I1115 07:57:21.639077 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639085 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Scor: (0)
I1115 07:57:21.639088 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639103 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639109 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639114 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Scor: (10)
I1115 07:57:21.639127 1 generic_scheduler.go:781] Host nxs-k8s-s10 -> Scor 100037
I1115 07:57:21.639150 1 generic_scheduler.go:781] Host nxs-k8s-s8 -> Scor 100034
I1115 07:57:21.639154 1 generic_scheduler.go:781] Host nxs-k8s-s9 -> Scor 100037
I1115 07:57:21.639267 1 scheduler_binder.go:269] AssumePodVolumes pentru podul "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nodul "nxs-k8s-s10"
I1115 07:57:21.639286 1 scheduler_binder.go:279] AssumePodVolumes pentru podul "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nodul "nxs-k8s-s10": toate PVC-urile sunt legate și nimic de făcut
I1115 07:57:21.639333 1 factory.go:733] Încercând să leagă cronjob-cron-events-1573793820-xt6q9 de nxs-k8s-s10

Aici vedem că inițial scheduler-ul realizează filtrarea și formează o listă de 3 noduri pe care poate fi efectuată lansarea (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Apoi, calculează punctele pe baza mai multor parametri (inclusiv BalancedResourceAllocation, LeastResourceAllocation) pentru fiecare dintre aceste noduri, cu scopul de a determina nodul cel mai potrivit. În cele din urmă, se plasează pe nodul cu cel mai mare număr de puncte (aici, imediat două noduri au același număr de puncte 100037, așa că se alege aleatoriu dintre ele — nxs-k8s-s10).

Ieșire: dacă pe nod rulează poduri pentru care nu sunt stabilite limite, pentru k8s (din perspectiva consumului de resurse) aceasta va fi echivalent cu faptul că pe acest nod astfel de poduri nu există deloc. Prin urmare, dacă aveți, să spunem, un pod cu un proces într-un consum mare de resurse (de exemplu, wowza) și pentru acesta nu sunt stabilite limite, ar putea apărea o situație în care acest pod să consume toate resursele nodului, dar pentru k8s acest nod este considerat neîncărcat și va primi același număr de puncte la clasificare (exact în punctele de evaluare a resurselor disponibile) ca și nodul pe care nu există poduri active, ceea ce în final poate duce la o distribuție inegală a sarcinii între noduri.

Evacuarea podului

După cum se știe — fiecărui pod îi este atribuit una dintre cele 3 clase QoS:

  1. garantat — se atribuie atunci când pentru fiecare container din pod sunt stabilite request și limit pentru memorie și CPU, iar aceste valori trebuie să fie egale
  2. burstable — cel puțin un container din pod are request și limit, în timp ce request < limit
  3. cea mai bună efort — atunci când niciun container din pod nu este restricționat în ceea ce privește resursele

În acest context, atunci când pe nod se observă o lipsă de resurse (discuri, memorie), kubelet începe să clasificareze și să evacueze pod-urile conform unui algoritm specific, care ia în considerare prioritatea pod-ului și clasa sa QoS. De exemplu, dacă este vorba despre RAM, atunci baza punctelor se calculează după următorul principiu:

  • Guaranteed: -998
  • BestEffort: 1000
  • Burstable: min(max(2, 1000 — (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Adică, cu aceeași prioritate, kubelet va evacua în primul rând podurile cu clasa QoS cea mai bună efort.

Ieșire: dacă doriți să diminuați probabilitatea evacuării pod-ului necesar de pe nod în cazul lipsei de resurse pe acesta, trebuie să vă asigurați, pe lângă prioritate, și că aveți stabilite request/limit pentru acesta.

Mecanismul de scalare orizontală a podurilor aplicației (HPA)

Când este necesar să se crească și să se reducă automat numărul de pod-uri în funcție de utilizarea resurselor (sistemice — CPU/RAM sau utilizator — rps), o entitate k8s utilă ar putea fi HPA (Horizontal Pod Autoscaler). Algoritmul său este următorul:

  1. Se determină valorile actuale ale resursei observate (currentMetricValue)
  2. Se determină valorile dorite pentru resursă (desiredMetricValue), care pentru resursele sistemice sunt stabilite prin request
  3. Se determină numărul actual de replici (currentReplicas)
  4. Numărul dorit de replici (desiredReplicas) se calculează după următoarea formulă
    desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]

În acest caz, scalarea nu va avea loc atunci când raportul (currentMetricValue / desiredMetricValue) este aproape de 1 (iar toleranța pe care o putem stabili noi poate fi folosită, implicit fiind egală cu 0.1).

Să luăm în considerare funcționarea hpa prin exemplul aplicației app-test (descrisă ca Deployment), unde este necesară modificarea numărului de replici în funcție de consumul de CPU:

  • Manifestul aplicației

    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-status

    Adică, observăm că podul cu aplicația este inițial lansat în două exemplare, fiecare dintre acestea conținând două containere nginx și nginx-exporter, pentru fiecare fiind stabilit requests. pentru CPU.

  • Manifestul HPA

    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: 30

    Adică, am creat un hpa care va monitoriza Deployment app-test și va regla numărul de pod-uri cu aplicația pe baza indicatorului cpu (așteptăm ca pod-ul să consume 30% din CPU-ul solicitat), în timp ce numărul de replici se află în intervalul 2-10.

    Acum, să examinăm mecanismul funcționării hpa, dacă se aplică o sarcină pe unul dintre poduri:

     # kubectl top pod
     NAME                                                   CPU(cores)   MEMORY(bytes)
     app-test-78559f8f44-pgs58            101m         243Mi
     app-test-78559f8f44-cj4jz            4m           240Mi

Astfel, avem următoarele:

  • Valoarea dorită (desiredMetricValue) — conform setărilor hpa, este de 30%
  • Valoarea actuală (currentMetricValue) — pentru calcul, controller-managerul determină media consumului de resurse în %, ceea ce înseamnă că efectuează următoarele operațiuni:
    1. Obține valorile absolute ale metodelor de la serverul metric, adică 101m și 4m
    2. Calculează media valorilor absolute, adică (101m + 4m) / 2 = 53m
    3. Obține valoarea absolută pentru consumul dorit de resurse (pentru aceasta se adună request-urile tuturor containerelor) 60m + 30m = 90m
    4. Calculează procentul mediu de consum CPU în raport cu request-ul podului, adică 53m / 90m * 100% = 59%

Acum avem tot ce este necesar pentru a determina dacă trebuie să modificăm numărul de replici, pentru aceasta calculăm coeficientul:

ratio = 59% / 30% = 1.96

Adică numărul de replici ar trebui să fie crescut de aproximativ 2 ori, ceea ce înseamnă [2 * 1.96] = 4.

Concluzie: Așa cum se poate observa, pentru ca acest mecanism să funcționeze, este necesar ca requests să fie specifice pentru toate containerele din podul monitorizat.

Mecanismul de scalare orizontală a nodurilor (Cluster Autoscaler)

Pentru a diminua impactul negativ asupra sistemului în cazul vârfurilor de încărcare, simpla existență a unui hpa configurat poate fi insuficientă. De exemplu, conform setărilor din hpa, managerul controller determină că numărul de replici trebuie să fie dublat, însă pe noduri nu sunt resurse libere pentru a rula un astfel de număr de poduri (adică nodul nu poate oferi resursele solicitate de poduri requests) și aceste poduri trec în starea Pending.

În acest caz, dacă furnizorul dispune de un IaaS/PaaS corespunzător (de exemplu, GKE/GCE, AKS, EKS etc.), un instrument comme ar fi Node Autoscaler. Acesta permite setarea unui număr maxim și minim de noduri în cluster și reglează automat numărul curent de noduri (prin apeluri API către furnizorul de cloud pentru a comanda/șterge un nod), atunci când se observă o lipsă de resurse în cluster și podurile nu pot fi planificate (se află în starea Pending).

Concluzie: Pentru a permite scalarea automată a nodurilor, este necesar să specificați requests în containerele podurilor, astfel încât k8s să poată evalua corect încărcarea nodurilor și, în consecință, să informeze că nu sunt resurse disponibile în cluster pentru a lansa un nou pod.

Concluzie

Este important de menționat că stabilirea limitelor de resurse pentru un container nu este o condiție obligatorie pentru lansarea cu succes a unei aplicații, dar este totuși recomandată din următoarele motive:

  1. Pentru a îmbunătăți funcționarea scheduler-ului în ceea ce privește echilibrarea sarcinii între nodurile k8s
  2. Pentru a reduce probabilitatea apariției evenimentului „evacuare pod”
  3. Pentru a permite scalarea orizontală automată a podurilor aplicației (HPA)
  4. Pentru a permite scalarea orizontală automată a nodurilor (Cluster Autoscaling) de către furnizorii de cloud

Citiți și alte articole de pe blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster