Kubernetes : pourquoi est-il si important de configurer la gestion des ressources systĂšme ?

En gĂ©nĂ©ral, il est toujours nĂ©cessaire de fournir un pool de ressources dĂ©diĂ©es Ă  une application pour son bon fonctionnement. Mais que faire si plusieurs applications fonctionnent sur les mĂȘmes ressources ? Comment garantir que chacune d'elles dispose du minimum nĂ©cessaire ? Comment limiter la consommation de ressources ? Comment rĂ©partir efficacement la charge entre les nƓuds ? Comment faire en sorte que le mĂ©canisme de mise Ă  l'Ă©chelle horizontale fonctionne lors d'une augmentation de la charge sur les applications ?

Kubernetes : pourquoi est-il si important de configurer la gestion des ressources systĂšme ?

Il faut d'abord tenir compte des principaux types de ressources disponibles dans le systĂšme — Ă  savoir le temps processeur et la mĂ©moire vive. Dans les manifestes k8s, ces types de ressources sont mesurĂ©s dans les unitĂ©s suivantes :

  • CPU — en cƓurs
  • RAM — en octets

Pour chaque ressource, il est possible de dĂ©finir deux types de demandes — requests et limits. Requests — dĂ©crit les exigences minimales en termes de ressources libre sur le nƓud pour faire fonctionner le conteneur (et le pod en gĂ©nĂ©ral), tandis que limits fixe une limite stricte des ressources disponibles pour le conteneur.

Il est important de comprendre que dans le manifeste, il n'est pas nécessaire de définir explicitement les deux types, et le comportement sera le suivant :

  • Si seul limits est spĂ©cifiĂ© pour la ressource, alors requests pour cette ressource prend automatiquement la valeur de limits (ce qui peut ĂȘtre vĂ©rifiĂ© en appelant describe de l'entitĂ©). Cela signifie que le fonctionnement du conteneur sera limitĂ© au mĂȘme nombre de ressources dont il a besoin pour son lancement.
  • Si seules requests sont spĂ©cifiĂ©es pour la ressource, aucune restriction supĂ©rieure n'est imposĂ©e — c'est-Ă -dire que le conteneur n'est limitĂ© que par les ressources du nƓud lui-mĂȘme.

Il est également possible de gérer les ressources non seulement au niveau du conteneur spécifique, mais aussi au niveau du namespace à l'aide des entités suivantes :

  • LimitRange — dĂ©crit la politique de limitation au niveau du conteneur/pod dans le ns et est nĂ©cessaire pour dĂ©finir les limitations par dĂ©faut sur le conteneur/pod, ainsi que pour prĂ©venir la crĂ©ation de conteneurs/pods manifestement surdimensionnĂ©s (ou inversement), limiter leur quantitĂ© et dĂ©terminer la diffĂ©rence possible entre les limits et requests.
  • ResourceQuotas — dĂ©crit la politique de limitation en gĂ©nĂ©ral pour tous les conteneurs dans le ns et est gĂ©nĂ©ralement utilisĂ©e pour la rĂ©partition des ressources par environnement (utile lorsque les environnements ne sont pas strictement sĂ©parĂ©s au niveau des nƓuds)

Ci-dessous sont des exemples de manifestes oĂč des limites de ressources sont Ă©tablies :

  • Au niveau d'un conteneur spĂ©cifique :

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

    C'est-Ă -dire que, dans ce cas, pour exĂ©cuter un conteneur avec nginx, il faut au minimum avoir 1G de RAM libre et 0,2 CPU sur le nƓud, tout en sachant que le conteneur peut consommer jusqu'Ă  0,2 CPU et toute la RAM disponible sur le nƓud.

  • Au niveau de tout le ns :

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

    C'est-à-dire que la somme de toutes les demandes des conteneurs dans le ns par défaut ne peut pas dépasser 300m pour le CPU et 1G pour la RAM, tandis que la somme de toutes les limites ne peut pas dépasser 700m pour le CPU et 2G pour la RAM.

  • Limites par dĂ©faut pour les conteneurs dans le 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

    C'est-à-dire que dans le namespace par défaut, une demande de 100m pour le CPU et 1G pour la RAM sera établie par défaut pour tous les conteneurs, avec une limite de 1 CPU et 2G. De plus, une restriction sur les valeurs possibles dans la demande/limite pour le CPU (50m < x < 2) et la RAM (500M < x < 4G) est également définie.

  • Limites au niveau des pods dans le ns :

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

    C'est-à-dire que pour chaque pod dans le ns par défaut, une limite de 4 vCPU et 1G sera appliquée.

Maintenant, j'aimerais parler des avantages que nous pouvons tirer de l'établissement de ces limites.

MĂ©canisme d'Ă©quilibrage de charge entre les nƓuds

Comme nous le savons, la rĂ©partition des pods entre les nƓuds est assurĂ©e par un composant de k8s, scheduler, qui fonctionne selon un certain algorithme. Cet algorithme passe par deux Ă©tapes lors du choix du nƓud optimal pour le lancement :

  1. Filtrage
  2. Classement

C'est-Ă -dire qu'en fonction de la politique dĂ©crite, les nƓuds sur lesquels le pod peut ĂȘtre exĂ©cutĂ© sont initialement sĂ©lectionnĂ©s sur la base de plusieurs predicates (y compris la vĂ©rification que la ressource du nƓud est suffisante pour exĂ©cuter le pod — PodFitsResources), puis pour chacun de ces nƓuds, selon priorities des points sont attribuĂ©s (plus les ressources de la node sont libres, plus elle reçoit de points — LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) et se lance sur la node avec le plus de points (si plusieurs nodes remplissent cette condition, l'une d'elles est choisie au hasard).

Il faut comprendre que le scheduler Ă©value les ressources disponibles de la node en se basant sur les donnĂ©es stockĂ©es dans etcd — c'est-Ă -dire sur la somme des ressources demandĂ©es/limites de chaque pod lancĂ© sur cette node, mais pas sur la consommation rĂ©elle des ressources. Ces informations peuvent ĂȘtre obtenues en exĂ©cutant la commande kubectl describe node $NODE, par exemple :

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

Ici, nous voyons tous les pods lancĂ©s sur une node spĂ©cifique ainsi que les ressources requises par chacun d'eux. Voici Ă  quoi ressemblent les logs du scheduler lors du lancement du pod cronjob-cron-events-1573793820-xt6q9 (ces informations apparaĂźtront dans les logs du scheduler si le niveau de log est rĂ©glĂ© sur 10 lors de l'exĂ©cution en dĂ©finissant les arguments de la commande —v=10) :

log

I1115 07:57:21.637791       1 scheduling_queue.go:908] Tentative de planifier le pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9                                                                                                                                           
I1115 07:57:21.637804       1 scheduler.go:453] Tentative de planifier le pod : nxs-stage\/cronjob-cron-events-1573793820-xt6q9                                                                                                                                                    
I1115 07:57:21.638285       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s5 est autorisĂ©e, le nƓud exĂ©cute seulement 16 sur 110 pods.                                                                               
I1115 07:57:21.638300       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s6 est autorisĂ©e, le nƓud exĂ©cute seulement 20 sur 110 pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s3 est autorisĂ©e, le nƓud exĂ©cute seulement 20 sur 110 pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s4 est autorisĂ©e, le nƓud exĂ©cute seulement 17 sur 110 pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s10 est autorisĂ©e, le nƓud exĂ©cute seulement 16 sur 110 pods.                                                                              
I1115 07:57:21.638365       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s12 est autorisĂ©e, le nƓud exĂ©cute seulement 9 sur 110 pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s11 est autorisĂ©e, le nƓud exĂ©cute seulement 11 sur 110 pods.                                                                              
I1115 07:57:21.638385       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s1 est autorisĂ©e, le nƓud exĂ©cute seulement 19 sur 110 pods.                                                                               
I1115 07:57:21.638402       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s2 est autorisĂ©e, le nƓud exĂ©cute seulement 21 sur 110 pods.                                                                               
I1115 07:57:21.638383       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s9 est autorisĂ©e, le nƓud exĂ©cute seulement 16 sur 110 pods.                                                                               
I1115 07:57:21.638335       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s8 est autorisĂ©e, le nƓud exĂ©cute seulement 18 sur 110 pods.                                                                               
I1115 07:57:21.638408       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s13 est autorisĂ©e, le nƓud exĂ©cute seulement 8 sur 110 pods.                                                                               
I1115 07:57:21.638478       1 predicates.go:1369] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s10 est autorisĂ©e, les termes d'anti-affinitĂ© des pods existants sont satisfaits.                                                                         
I1115 07:57:21.638505       1 predicates.go:1369] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s8 est autorisĂ©e, les termes d'anti-affinitĂ© des pods existants sont satisfaits.                                                                          
I1115 07:57:21.638577       1 predicates.go:1369] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s9 est autorisĂ©e, les termes d'anti-affinitĂ© des pods existants sont satisfaits.                                                                          
I1115 07:57:21.638583       1 predicates.go:829] La planification du pod nxs-stage\/cronjob-cron-events-1573793820-xt6q9 sur le nƓud nxs-k8s-s7 est autorisĂ©e, le nƓud exĂ©cute seulement 25 sur 110 pods.                                                                               
I1115 07:57:21.638932       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10 : AllocationRessourceÉquilibrĂ©e, capacitĂ© 39900 millicores 66620178432 octets mĂ©moire, demande totale 2343 millicores 9640186880 octets mĂ©moire, score 9        
I1115 07:57:21.638946       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10 : AllocationRessourceMinimale, capacité 39900 millicores 66620178432 octets mémoire, demande totale 2343 millicores 9640186880 octets mémoire, score 8           
I1115 07:57:21.638961       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9 : AllocationRessourceÉquilibrĂ©e, capacitĂ© 39900 millicores 66620170240 octets mĂ©moire, demande totale 4107 millicores 11307422720 octets mĂ©moire, score 9        
I1115 07:57:21.638971       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8 : AllocationRessourceÉquilibrĂ©e, capacitĂ© 39900 millicores 66620178432 octets mĂ©moire, demande totale 5847 millicores 24333637120 octets mĂ©moire, score 7        
I1115 07:57:21.638975       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9 : AllocationRessourceMinimale, capacité 39900 millicores 66620170240 octets mémoire, demande totale 4107 millicores 11307422720 octets mémoire, score 8           
I1115 07:57:21.638990       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8 : AllocationRessourceMinimale, capacité 39900 millicores 66620178432 octets mémoire, demande totale 5847 millicores 24333637120 octets mémoire, score 7           
I1115 07:57:21.639022       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10 : PrioritéToléranceTaints, Score : (10)                                                                                                        
I1115 07:57:21.639030       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8 : PrioritéToléranceTaints, Score : (10)                                                                                                         
I1115 07:57:21.639034       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9 : PrioritéToléranceTaints, Score : (10)                                                                                                         
I1115 07:57:21.639041       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10 : PrioritĂ©AffinitĂ©NƓud, Score : (0)                                                                                                            
I1115 07:57:21.639053       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8 : PrioritĂ©AffinitĂ©NƓud, Score : (0)                                                                                                             
I1115 07:57:21.639059       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9 : PrioritĂ©AffinitĂ©NƓud, Score : (0)                                                                                                             
I1115 07:57:21.639061       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10 : PrioritéAffinitéInterPod, Score : (0)                                                                                                                   
I1115 07:57:21.639063       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                                   
I1115 07:57:21.639073       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8 : PrioritéAffinitéInterPod, Score : (0)                                                                                                                    
I1115 07:57:21.639077       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                                    
I1115 07:57:21.639085       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9 : PrioritéAffinitéInterPod, Score : (0)                                                                                                                    
I1115 07:57:21.639088       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                                    
I1115 07:57:21.639103       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                         
I1115 07:57:21.639109       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                          
I1115 07:57:21.639114       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9 : PrioritéRépartitionSélecteur, Score : (10)                                                                                                          
I1115 07:57:21.639127       1 generic_scheduler.go:781] HĂŽte nxs-k8s-s10 => Score 100037                                                                                                                                                                            
I1115 07:57:21.639150       1 generic_scheduler.go:781] HĂŽte nxs-k8s-s8 => Score 100034                                                                                                                                                                             
I1115 07:57:21.639154       1 generic_scheduler.go:781] HĂŽte nxs-k8s-s9 => Score 100037                                                                                                                                                                             
I1115 07:57:21.639267       1 scheduler_binder.go:269] AssumerVolumesPod pour le pod "nxs-stage\/cronjob-cron-events-1573793820-xt6q9", nƓud "nxs-k8s-s10"                                                                                                               
I1115 07:57:21.639286       1 scheduler_binder.go:279] AssumerVolumesPod pour le pod "nxs-stage\/cronjob-cron-events-1573793820-xt6q9", nƓud "nxs-k8s-s10" : tous les PVC sont liĂ©s et rien Ă  faire                                                                             
I1115 07:57:21.639333       1 factory.go:733] Tentative de lier cronjob-cron-events-1573793820-xt6q9 Ă  nxs-k8s-s10

Ici, nous voyons qu'initialement, le scheduler effectue une filtration et forme une liste de 3 nƓuds sur lesquels un dĂ©ploiement est possible (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Il procĂšde ensuite au calcul des points selon plusieurs paramĂštres (y compris BalancedResourceAllocation, LeastResourceAllocation) pour chacun de ces nƓuds afin de dĂ©terminer le nƓud le plus appropriĂ©. En fin de compte, le dĂ©ploiement se fait sur le nƓud avec le plus grand nombre de points (ici, deux nƓuds ont le mĂȘme score de 100037, donc un est choisi au hasard — nxs-k8s-s10).

Sortie: si des pods fonctionnent sur un nƓud sans limitations dĂ©finies, cela signifie pour k8s (en termes de consommation de ressources) qu'il est Ă©quivalent Ă  la situation oĂč ces pods n'existent pas sur ce nƓud. Donc, si vous avez, par exemple, un pod avec un processus gourmand (comme wowza) et qu'aucune limite n'est dĂ©finie, il pourrait arriver que ce pod consomme toutes les ressources du nƓud, mais pour k8s, ce nƓud est considĂ©rĂ© comme non chargĂ© et recevra le mĂȘme nombre de points lors du classement (dans les points d'Ă©valuation des ressources disponibles), qu'un nƓud sans pods en fonctionnement, ce qui peut finalement conduire Ă  une rĂ©partition inĂ©gale de la charge entre les nƓuds.

Éviction d'un pod

Comme on le sait, à chaque pod est attribuée l'une des 3 classes QoS :

  1. garanti — attribuĂ©e lorsque, pour chaque conteneur dans le pod, un request et un limit de mĂ©moire et CPU sont dĂ©finis, ces valeurs devant coĂŻncider
  2. instances burstable. — lorsqu'au moins un conteneur dans le pod a un request et un limit, avec request < limit
  3. meilleur effort — lorsque aucun conteneur dans le pod n'est limitĂ© en ressources

Cependant, lorsque le nƓud manque de ressources (disque, mĂ©moire), kubelet commence Ă  classer et Ă  Ă©vincer les pods selon un algorithme dĂ©terminĂ©, tenant compte de la prioritĂ© du pod et de sa classe QoS. Par exemple, en ce qui concerne la RAM, des points sont attribuĂ©s selon la classe QoS selon le principe suivant :

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

Ainsi, avec une priorité identique, kubelet évincera en priorité les pods avec la classe QoS meilleur effort.

Sortie: si vous souhaitez rĂ©duire la probabilitĂ© d'Ă©viction d'un pod important sur un nƓud en cas de manque de ressources, il est nĂ©cessaire, en plus de la prioritĂ©, de s'assurer de dĂ©finir un request/limit pour celui-ci.

Mécanisme de mise à l'échelle horizontale automatique des pods d'application (HPA)

Lorsqu'il s'agit d'augmenter ou de diminuer automatiquement le nombre de pods en fonction de l'utilisation des ressources (systĂšme — CPU/RAM ou utilisateur — rps), une entitĂ© k8s telle que HPA (Horizontal Pod Autoscaler) peut aider dans cette solution. Son algorithme est le suivant :

  1. Les valeurs des ressources observées actuelles (currentMetricValue) sont déterminées
  2. Les valeurs souhaitées pour la ressource (desiredMetricValue) sont déterminées, qui pour les ressources systÚme sont définies à l'aide de la demande
  3. Le nombre actuel de répliques (currentReplicas) est déterminé
  4. Le nombre de répliques souhaité (desiredReplicas) est calculé selon la formule suivante
    desiredReplicas = [ currentReplicas * ( currentMetricValue / desiredMetricValue )]

Cependant, la mise Ă  l'Ă©chelle ne se produira pas lorsque le coefficient (currentMetricValue / desiredMetricValue) est proche de 1 (nous pouvons dĂ©finir la tolĂ©rance nous-mĂȘmes, par dĂ©faut elle est de 0,1).

ConsidĂ©rons le fonctionnement de l'HPA Ă  l'aide de l'application app-test (dĂ©crite comme un dĂ©ploiement), oĂč il est nĂ©cessaire de modifier le nombre de rĂ©pliques en fonction de la consommation du CPU :

  • Manifeste de l'application

    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

    C'est-à-dire que nous voyons que le pod avec l'application est initialement exécuté en deux exemplaires, chacun contenant deux conteneurs nginx et nginx-exporter, pour lesquels est défini requests pour le CPU.

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

    C'est-à-dire que nous avons créé un HPA qui surveillera le déploiement app-test et régulera le nombre de pods avec l'application en fonction de l'indicateur de CPU (nous nous attendons à ce que le pod consomme 30 % de l'CPU qui lui est demandé), tout en maintenant le nombre de répliques dans la plage de 2 à 10.

    Maintenant, considérons le mécanisme de fonctionnement de l'HPA, si une charge est appliquée à l'un des pods :

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

En résumé, nous avons ce qui suit :

  • La valeur dĂ©sirĂ©e (desiredMetricValue) — selon les paramĂštres hpa, est de 30%
  • La valeur actuelle (currentMetricValue) — pour le calcul, le controller-manager Ă©value la consommation moyenne des ressources en %, c'est-Ă -dire qu'il effectue l'opĂ©ration suivante :
    1. Il obtient les valeurs absolues des métriques des pods à partir du serveur de métriques, c'est-à-dire 101m et 4m
    2. Il calcule la valeur moyenne absolue, c'est-Ă -dire (101m + 4m) / 2 = 53m
    3. Il obtient la valeur absolue pour la consommation souhaitĂ©e de ressources (pour cela, il additionne les requĂȘtes de tous les conteneurs) 60m + 30m = 90m
    4. Il calcule le pourcentage moyen de consommation CPU par rapport Ă  la demande du pod, c'est-Ă -dire 53m / 90m * 100% = 59%

Maintenant, nous avons tout ce qu'il faut pour dĂ©terminer si le nombre de rĂ©plicas doit ĂȘtre modifiĂ©, pour cela, nous calculons le ratio :

ratio = 59% / 30% = 1.96

C'est-Ă -dire que le nombre de rĂ©plicas doit ĂȘtre multipliĂ© par ~2, ce qui donne [2 * 1.96] = 4.

Conclusion : Comme on peut le constater, pour que ce mĂ©canisme fonctionne, il est Ă©galement nĂ©cessaire d'avoir des requĂȘtes pour tous les conteneurs dans le pod observĂ©.

MĂ©canisme d'autoscaling horizontal des nƓuds (Cluster Autoscaler)

Pour attĂ©nuer l'impact nĂ©gatif sur le systĂšme lors de pics de charge, le simple fait d'avoir un hpa configurĂ© peut ne pas suffire. Par exemple, selon les paramĂštres dans le hpa, le manager du contrĂŽleur dĂ©cide que le nombre de rĂ©plicas doit ĂȘtre multipliĂ© par 2, cependant, il n'y a pas de ressources libres sur les nƓuds pour exĂ©cuter ce nombre de pods (c'est-Ă -dire que le nƓud ne peut pas fournir les ressources demandĂ©es par les pods). Ces pods passent alors dans un Ă©tat Pending.

Dans ce cas, si le fournisseur dispose d'une IaaS/PaaS appropriĂ©e (par exemple, GKE/GCE, AKS, EKS, etc.), un outil tel que Node Autoscaler. Cela permet de dĂ©finir un nombre maximal et minimal de nƓuds dans le cluster et de rĂ©guler automatiquement le nombre actuel de nƓuds (en appelant l'API du fournisseur cloud pour commander/supprimer un nƓud), lorsque des pĂ©nuries de ressources sont observĂ©es dans le cluster et que les pods ne peuvent pas ĂȘtre programmĂ©s (se trouvent en Ă©tat Pending).

Conclusion : Pour permettre l'autoscaling des nƓuds, il est nĂ©cessaire de dĂ©finir des requĂȘtes dans les conteneurs des pods, afin que k8s puisse Ă©valuer correctement la charge des nƓuds et indiquer qu'il n'y a pas suffisamment de ressources dans le cluster pour dĂ©marrer le prochain pod.

Conclusion

Il convient de noter que la mise en place de limites de ressources pour le conteneur n'est pas une condition nécessaire au démarrage réussi de l'application, mais il est néanmoins préférable de le faire pour les raisons suivantes :

  1. Pour un fonctionnement plus prĂ©cis du scheduler en ce qui concerne la rĂ©partition de la charge entre les nƓuds k8s
  2. Pour réduire la probabilité de l'événement "expulsion du pod"
  3. Pour le fonctionnement de l'autoscaling horizontal des pods de l'application (HPA)
  4. Pour le fonctionnement de l'autoscaling horizontal des nƓuds (Cluster Autoscaling) chez les fournisseurs de cloud

Lisez aussi d'autres articles sur notre blog :

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster