Kubernetes: perché è così importante configurare la gestione delle risorse di sistema?

In genere, c'è sempre la necessità di fornire un pool dedicato di risorse a un'applicazione per il suo corretto e stabile funzionamento. Ma cosa succede se sulla stessa infrastruttura operano più applicazioni contemporaneamente? Come garantire risorse minime necessarie a ciascuna di esse? Quali modalità esistono per limitare il consumo di risorse? Come distribuire adeguatamente il carico tra i nodi? Come garantire il funzionamento del meccanismo di scalabilità orizzontale in caso di aumento del carico sulle applicazioni?

Kubernetes: perché è così importante configurare la gestione delle risorse di sistema?

Bisogna innanzitutto chiarire quali sono i principali tipi di risorse esistenti nel sistema: si tratta ovviamente del tempo di CPU e della memoria RAM. Nei manifesti k8s, questi tipi di risorse vengono misurati nelle seguenti unità:

  • CPU — in core
  • RAM — in byte

Inoltre, per ciascuna risorsa è possibile impostare due tipi di requisiti — requests e limits. Requests — descrive i requisiti minimi per le risorse libere del nodo per avviare un contenitore (e del pod in generale), mentre limits stabilisce un limite rigido alle risorse disponibili per il contenitore.

È importante capire che nel manifesto non è necessario definire esplicitamente entrambi i tipi, e il comportamento sarà il seguente:

  • Se è stato specificato solo il limite per una risorsa, le richieste per questa risorsa assumono automaticamente il valore del limite (può essere verificato richiamando la descrizione dell'entità). Cioè, in effetti, l'operatività del contenitore sarà limitata alla stessa quantità di risorse che richiede per il suo avvio.
  • Se per la risorsa è stata specificata solo la richiesta, non vengono impostati limiti superiori su questa risorsa: cioè, il contenitore è limitato solo dalle risorse del nodo stesso.

Esiste anche la possibilità di gestire le risorse non solo a livello di singolo contenitore, ma anche a livello di namespace utilizzando le seguenti entità:

  • LimitRange — descrive la politica di limitazione a livello di contenitore/pod nel ns e serve per definire i limiti predefiniti per il contenitore/pod, oltre a prevenire la creazione di contenitori/pod decisamente sovradimensionati (o viceversa), limitando il loro numero e determinando la possibile differenza di valori tra limits e requests.
  • ResourceQuotas — descrivono la politica di limitazione in generale per tutti i container in ns e viene utilizzata, di norma, per distinguere le risorse tra gli ambienti (utile quando gli ambienti non sono severamente separati a livello di nodi)

Di seguito sono riportati esempi di manifesti in cui vengono stabiliti limiti sulle risorse:

  • A livello di un singolo container:

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

    Cioè, in questo caso, per avviare un container con nginx sono necessari almeno 1G di RAM e 0.2 CPU disponibili nel nodo, mentre al massimo il container può utilizzare 0.2 CPU e tutta la RAM disponibile nel nodo.

  • A livello di un intero ns:

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

    Cioè, la somma di tutte le richieste dei container nel ns predefinito non può superare 300m per la CPU e 1G per la RAM, mentre la somma di tutti i limiti non può superare 700m per la CPU e 2G per la RAM.

  • Limitazioni predefinite per i container in 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

    Cioè, nel namespace predefinito, per tutti i container verrà impostato un limite di richiesta di 100m per la CPU e 1G per la RAM, e un limite di 1 CPU e 2G. Inoltre, è stato stabilito un limite sui valori possibili in richiesta / limite per la CPU (50m < x < 2) e RAM (500M < x < 4G).

  • Limitazioni a livello di pod ns:

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

    Cioè, per ogni pod nel ns predefinito sarà impostato un limite di 4 vCPU e 1G.

Ora vorremmo parlare dei vantaggi che l'impostazione di queste limitazioni può offrirci.

Meccanismo di bilanciamento del carico tra i nodi

Come è noto, la distribuzione dei pod tra i nodi è gestita da un componente k8s chiamato scheduler, che funziona secondo un determinato algoritmo. Questo algoritmo, nella scelta del nodo ottimale per l'avvio, passa attraverso due fasi:

  1. Filtraggio
  2. Ranking

Cioè, secondo la politica descritta, inizialmente vengono selezionati i nodi sui quali è possibile avviare il pod basandosi su un insieme di predicates (incluso il controllo se il nodo dispone di risorse sufficienti per avviare il pod — PodFitsResources), e poi, per ciascuno di questi nodi, secondo priorities vengono accumulati punti (incluso, più risorse libere ha il nodo, più punti vengono assegnati - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) e viene attivato sul nodo con il maggior numero di punti (se più nodi soddisfano questa condizione, viene scelto casualmente uno di essi).

Va compreso che il scheduler, nella valutazione delle risorse disponibili del nodo, si basa sui dati memorizzati in etcd - cioè sulla somma delle risorse richieste/limitate di ogni pod in esecuzione su questo nodo, ma non sul consumo effettivo delle risorse. Queste informazioni possono essere ottenute dall'output del comando kubectl describe node $NODE, per esempio:

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

Qui vediamo tutti i pod in esecuzione su un nodo specifico, così come le risorse richieste da ciascun pod. Ecco come appaiono i log del scheduler durante l'avvio del pod cronjob-cron-events-1573793820-xt6q9 (queste informazioni nei log del scheduler appariranno impostando il livello di registrazione al 10° livello negli argomenti del comando di avvio -v=10):

log

I1115 07:57:21.637791       1 scheduling_queue.go:908] In procinto di provare a programmare il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                           
I1115 07:57:21.637804       1 scheduler.go:453] Tentativo di programmare il pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                                    
I1115 07:57:21.638285       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s5 consentito, il nodo sta eseguendo solo 16 su 110 pod.                                                                               
I1115 07:57:21.638300       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s6 consentito, il nodo sta eseguendo solo 20 su 110 pod.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s3 consentito, il nodo sta eseguendo solo 20 su 110 pod.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s4 consentito, il nodo sta eseguendo solo 17 su 110 pod.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s10 consentito, il nodo sta eseguendo solo 16 su 110 pod.                                                                              
I1115 07:57:21.638365       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s12 consentito, il nodo sta eseguendo solo 9 su 110 pod.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s11 consentito, il nodo sta eseguendo solo 11 su 110 pod.                                                                              
I1115 07:57:21.638385       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s1 consentito, il nodo sta eseguendo solo 19 su 110 pod.                                                                               
I1115 07:57:21.638402       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s2 consentito, il nodo sta eseguendo solo 21 su 110 pod.                                                                               
I1115 07:57:21.638383       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s9 consentito, il nodo sta eseguendo solo 16 su 110 pod.                                                                               
I1115 07:57:21.638335       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s8 consentito, il nodo sta eseguendo solo 18 su 110 pod.                                                                               
I1115 07:57:21.638408       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s13 consentito, il nodo sta eseguendo solo 8 su 110 pod.                                                                               
I1115 07:57:21.638478       1 predicates.go:1369] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s10 consentito, le condizioni di anti-affinità dei pod esistenti sono soddisfatte.                                                                         
I1115 07:57:21.638505       1 predicates.go:1369] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s8 consentito, le condizioni di anti-affinità dei pod esistenti sono soddisfatte.                                                                          
I1115 07:57:21.638577       1 predicates.go:1369] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s9 consentito, le condizioni di anti-affinità dei pod esistenti sono soddisfatte.                                                                          
I1115 07:57:21.638583       1 predicates.go:829] Programma il pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 sul nodo nxs-k8s-s7 consentito, il nodo sta eseguendo solo 25 su 110 pod.                                                                               
I1115 07:57:21.638932       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: Bilanciamento allocazione risorse, capacità 39900 millicores 66620178432 byte di memoria, richiesta totale 2343 millicores 9640186880 byte di memoria, punteggio 9        
I1115 07:57:21.638946       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: Allocazione risorse minima, capacità 39900 millicores 66620178432 byte di memoria, richiesta totale 2343 millicores 9640186880 byte di memoria, punteggio 8           
I1115 07:57:21.638961       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: Bilanciamento allocazione risorse, capacità 39900 millicores 66620170240 byte di memoria, richiesta totale 4107 millicores 11307422720 byte di memoria, punteggio 9        
I1115 07:57:21.638971       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: Bilanciamento allocazione risorse, capacità 39900 millicores 66620178432 byte di memoria, richiesta totale 5847 millicores 24333637120 byte di memoria, punteggio 7        
I1115 07:57:21.638975       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: Allocazione risorse minima, capacità 39900 millicores 66620170240 byte di memoria, richiesta totale 4107 millicores 11307422720 byte di memoria, punteggio 8           
I1115 07:57:21.638990       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: Allocazione risorse minima, capacità 39900 millicores 66620178432 byte di memoria, richiesta totale 5847 millicores 24333637120 byte di memoria, punteggio 7           
I1115 07:57:21.639022       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: Priorità di tolleranza di taint, Punteggio: (10)                                                                                                        
I1115 07:57:21.639030       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: Priorità di tolleranza di taint, Punteggio: (10)                                                                                                         
I1115 07:57:21.639034       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: Priorità di tolleranza di taint, Punteggio: (10)                                                                                                         
I1115 07:57:21.639041       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: Priorità di affinità nodo, Punteggio: (0)                                                                                                            
I1115 07:57:21.639053       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: Priorità di affinità nodo, Punteggio: (0)                                                                                                             
I1115 07:57:21.639059       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: Priorità di affinità nodo, Punteggio: (0)                                                                                                             
I1115 07:57:21.639061       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: Priorità di affinità inter-pod, Punteggio: (0)                                                                                                                   
I1115 07:57:21.639063       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                                   
I1115 07:57:21.639073       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: Priorità di affinità inter-pod, Punteggio: (0)                                                                                                                    
I1115 07:57:21.639077       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                                    
I1115 07:57:21.639085       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: Priorità di affinità inter-pod, Punteggio: (0)                                                                                                                    
I1115 07:57:21.639088       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                                    
I1115 07:57:21.639103       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                         
I1115 07:57:21.639109       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                          
I1115 07:57:21.639114       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: Priorità di distribuzione selettiva, Punteggio: (10)                                                                                                          
I1115 07:57:21.639127       1 generic_scheduler.go:781] Host nxs-k8s-s10 -> Punteggio 100037                                                                                                                                                                            
I1115 07:57:21.639150       1 generic_scheduler.go:781] Host nxs-k8s-s8 -> Punteggio 100034                                                                                                                                                                             
I1115 07:57:21.639154       1 generic_scheduler.go:781] Host nxs-k8s-s9 -> Punteggio 100037                                                                                                                                                                             
I1115 07:57:21.639267       1 scheduler_binder.go:269] AssumePodVolumes per pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nodo "nxs-k8s-s10"                                                                                                               
I1115 07:57:21.639286       1 scheduler_binder.go:279] AssumePodVolumes per pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", nodo "nxs-k8s-s10": tutte le PVC sono legate e non c'è nulla da fare                                                                             
I1115 07:57:21.639333       1 factory.go:733] Tentativo di legare cronjob-cron-events-1573793820-xt6q9 a nxs-k8s-s10

Qui vediamo che inizialmente il scheduler esegue un filtraggio e crea un elenco di 3 nodi sui quali è possibile avviare (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Successivamente, calcola i punteggi in base a diversi parametri (inclusi BalancedResourceAllocation, LeastResourceAllocation) per ciascuno di questi nodi al fine di determinare il nodo più adatto. Alla fine, si pianifica sul nodo con il punteggio più alto (qui due nodi hanno lo stesso punteggio di 100037, quindi viene scelta una di esse a caso — nxs-k8s-s10).

Conclusione: se nel nodo sono in esecuzione pod per i quali non sono state impostate restrizioni, per k8s (dal punto di vista del consumo delle risorse) questo equivarrà a considerare che su questo nodo tali pod siano assenti. Pertanto, se avete, per esempio, un pod con un processo molto esigente (ad esempio, wowza) e per esso non sono state definite restrizioni, può verificarsi una situazione in cui questo pod consuma tutte le risorse del nodo, ma per k8s questo nodo risulta non sovraccarico e riceverà lo stesso punteggio in fase di classificazione (a livello di punteggi di risorse disponibili) di un nodo su cui non ci sono pod attivi, il che può portare a una distribuzione incoerente del carico tra i nodi.

Espulsione del pod

Come è noto, a ogni pod viene assegnata una delle 3 classi QoS:

  1. garantita — viene assegnata quando per ogni contenitore nel pod sono stati definiti request e limit per la memoria e la CPU, e questi valori devono coincidere
  2. burstable — almeno un contenitore nel pod ha request e limit, con request < limit
  3. best effort — quando nessun contenitore nel pod è limitato nelle risorse

Pertanto, quando nel nodo si verifica una carenza di risorse (disco, memoria), kubelet inizia a classificare ed espellere i pod secondo un determinato algoritmo, che tiene conto della priorità del pod e della sua classe QoS. Ad esempio, per quanto riguarda la RAM, i punteggi vengono assegnati sulla base della classe QoS secondo il seguente principio:

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

Cioè, a parità di priorità, kubelet espellerà in prima battuta i pod con classe QoS best effort.

Conclusione: se desiderate ridurre la probabilità di espulsione di un pod necessario dal nodo in caso di carenza di risorse, dovete anche assicurarvi di definire request/limit per esso, oltre alla priorità.

Meccanismo di autoscaling orizzontale dei pod dell'applicazione (HPA)

Quando si tratta di aumentare e diminuire automaticamente il numero di pod in base all'utilizzo delle risorse (sistema — CPU/RAM o utente — rps), può aiutare un'entità k8s come HPA (Horizontal Pod Autoscaler). Il cui algoritmo è il seguente:

  1. Si determinano le letture attuali della risorsa monitorata (currentMetricValue)
  2. Si definiscono i valori desiderati per la risorsa (desiredMetricValue), che per le risorse di sistema vengono impostati tramite request
  3. Si determina l'attuale numero di repliche (currentReplicas)
  4. Si calcola il numero desiderato di repliche (desiredReplicas) con la seguente formula
    desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]

In questo caso non ci sarà scaling quando il rapporto (currentMetricValue / desiredMetricValue) è vicino a 1 (e la tolleranza può essere impostata da noi, per impostazione predefinita è 0.1).

Esaminiamo il funzionamento dell'HPA con l'esempio dell'applicazione app-test (descritta come Deployment), dove è necessario modificare il numero di repliche in base al consumo della CPU:

  • Manifesto dell'applicazione

    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

    Cioè, vediamo che il pod con l'applicazione viene inizialmente avviato in due esemplari, ognuno dei quali contiene due contenitori nginx e nginx-exporter, per ciascuno dei quali è stato impostato requests per la CPU.

  • Manifesto 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

    Cioè, abbiamo creato un HPA che monitorerà il Deployment app-test e regolerà il numero di pod con l'applicazione in base all'indicatore della CPU (ci aspettiamo che il pod dovrebbe consumare il 30% della CPU richiesta), con il numero di repliche che rimane tra 2 e 10.

    Ora consideriamo il meccanismo di funzionamento dell'HPA se si applica un carico a uno dei pod:

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

In sintesi, abbiamo il seguente:

  • Il valore desiderato (desiredMetricValue) — secondo le impostazioni di hpa è pari al 30%
  • Il valore attuale (currentMetricValue) — per il calcolo, il controller-manager calcola la media del consumo delle risorse in %, ossia sostanzialmente esegue quanto segue:
    1. Ottiene i valori assoluti delle metriche dei pod dal metric-server, ossia 101m e 4m
    2. Calcola il valore assoluto medio, cioè (101m + 4m) / 2 = 53m
    3. Ottiene il valore assoluto per il consumo desiderato delle risorse (per questo si sommano le richieste di tutti i container) 60m + 30m = 90m
    4. Calcola la percentuale media di utilizzo della CPU rispetto alle richieste del pod, cioè 53m / 90m * 100% = 59%

Ora abbiamo tutto il necessario per determinare se è necessario modificare il numero di repliche, per questo calcoliamo il rapporto:

ratio = 59% / 30% = 1.96

Cioè, il numero di repliche deve essere aumentato di circa 2 volte e quindi essere [2 * 1.96] = 4.

Conclusione: Come si può notare, affinché questo meccanismo funzioni, una condizione necessaria è anche la presenza di richieste per tutti i container nel pod osservato.

Meccanismo di autoscaling orizzontale dei nodi (Cluster Autoscaler)

Per ridurre l'impatto negativo sul sistema durante i picchi di carico, avere un hpa configurato può essere insufficiente. Ad esempio, secondo le impostazioni dell'hpa, il controller manager decide che il numero di repliche deve aumentare di 2 volte, ma nei nodi non ci sono risorse disponibili per avviare un tale numero di pod (cioè il nodo non può fornire le risorse richieste dalle richieste del pod) e questi pod passano allo stato Pending.

In questo caso, se il provider ha un corrispondente IaaS/PaaS (ad esempio, GKE/GCE, AKS, EKS, ecc.), uno strumento come Node Autoscalerci può aiutare. Permette di impostare un numero massimo e minimo di nodi nel cluster e regolare automaticamente il numero attuale di nodi (richiedendo all'API del provider cloud l'ordine/rimozione di un nodo), quando si osserva una mancanza di risorse nel cluster e i pod non possono essere pianificati (si trovano in stato Pending).

Conclusione: Per avere la possibilità di autoscaling dei nodi, è necessario impostare le richieste nei container dei pod, affinché k8s possa valutare correttamente il carico dei nodi e segnalare di conseguenza che non ci sono risorse nel cluster per avviare un ulteriore pod.

Conclusione

È importante notare che impostare limiti sulle risorse del contenitore non è un prerequisito per l'avvio riuscito dell'applicazione, tuttavia è consigliabile farlo per i seguenti motivi:

  1. Per un funzionamento più preciso dello scheduler nella distribuzione del carico tra i nodi k8s
  2. Per ridurre la probabilità che si verifichi un evento di 'espulsione del pod'
  3. Per il funzionamento dell'auto-scaling orizzontale dei pod dell'applicazione (HPA)
  4. Per il funzionamento dell'auto-scaling orizzontale dei nodi (Cluster Autoscaling) da parte dei fornitori di cloud

Leggi anche altri articoli nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster