Kubernetes : pourquoi est-il si important de mettre en place une gestion des ressources système ?

En règle générale, il est toujours nécessaire de fournir un pool de ressources dédié à une application pour son fonctionnement correct et stable. Mais que se passe-t-il si plusieurs applications fonctionnent avec la même puissance ? Comment fournir à chacun d’entre eux le minimum de ressources nécessaires ? Comment limiter la consommation des ressources ? Comment répartir correctement la charge entre les nœuds ? Comment garantir le fonctionnement du mécanisme de mise à l’échelle horizontale si la charge de l’application augmente ?

Kubernetes : pourquoi est-il si important de mettre en place une gestion des ressources système ?

Vous devez commencer par les principaux types de ressources qui existent dans le système - il s'agit bien sûr du temps processeur et de la RAM. Dans les manifestes k8, ces types de ressources sont mesurés dans les unités suivantes :

  • CPU - en cœurs
  • RAM - en octets

De plus, pour chaque ressource, il est possible de définir deux types d'exigences : demandes и limites. Requêtes - décrit les exigences minimales en matière de ressources libres d'un nœud pour exécuter un conteneur (et un pod dans son ensemble), tandis que les limites fixent une limite stricte aux ressources disponibles pour le conteneur.

Il est important de comprendre que le manifeste ne doit pas nécessairement définir explicitement les deux types, mais le comportement sera le suivant :

  • Si seules les limites d'une ressource sont explicitement spécifiées, alors les requêtes pour cette ressource prennent automatiquement une valeur égale aux limites (vous pouvez le vérifier en appelant des entités descriptives). Ceux. en fait, le conteneur sera limité à la même quantité de ressources dont il a besoin pour fonctionner.
  • Si seules les demandes sont explicitement spécifiées pour une ressource, alors aucune restriction supérieure n'est définie sur cette ressource - c'est-à-dire le conteneur n'est limité que par les ressources du nœud lui-même.

Il est également possible de configurer la gestion des ressources non seulement au niveau d'un conteneur spécifique, mais également au niveau de l'espace de noms en utilisant les entités suivantes :

  • LimitRange — décrit la politique de restriction au niveau du conteneur/pod en ns et est nécessaire pour décrire les limites par défaut sur le conteneur/pod, ainsi qu'empêcher la création de conteneurs/pods manifestement gros (ou vice versa), limiter leur nombre et déterminer la différence possible dans les valeurs des limites et des demandes
  • Quotas de ressources — décrit la politique de restriction en général pour tous les conteneurs dans ns et est utilisée, en règle générale, pour délimiter les ressources entre les environnements (utile lorsque les environnements ne sont pas strictement délimités au niveau des nœuds)

Voici des exemples de manifestes qui définissent des limites de ressources :

  • Au niveau du conteneur spécifique :

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

    Ceux. dans ce cas, pour exécuter un conteneur avec nginx, vous aurez besoin d'au moins 1 Go de RAM libre et de 0.2 CPU sur le nœud, alors qu'au maximum le conteneur peut consommer 0.2 CPU et toute la RAM disponible sur le nœud.

  • Au niveau entier ns :

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

    Ceux. la somme de tous les conteneurs de requêtes dans le ns par défaut ne peut pas dépasser 300 mo pour le CPU et 1G pour l'OP, et la somme de toutes les limites est de 700 mo pour le CPU et 2G pour l'OP.

  • Limites par défaut pour les conteneurs en 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

    Ceux. dans l'espace de noms par défaut pour tous les conteneurs, la demande sera définie sur 100 m pour le CPU et 1G pour l'OP, limite : 1 CPU et 2G. Parallèlement, une limite est également fixée sur les valeurs possibles en requête/limite pour le CPU (50m < x < 2) et la RAM (500M < x < 4G).

  • Restrictions au niveau du pod :

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

    Ceux. pour chaque pod dans le ns par défaut, il y aura une limite de 4 vCPU et 1G.

Je voudrais maintenant vous dire quels avantages l'imposition de ces restrictions peut nous apporter.

Mécanisme d'équilibrage de charge entre les nœuds

Comme vous le savez, le composant k8s est responsable de la répartition des pods entre les nœuds, tels que ordonnanceur, qui fonctionne selon un algorithme spécifique. Cet algorithme passe par deux étapes lors de la sélection du nœud optimal à lancer :

  1. filtration
  2. Variant

Ceux. selon la politique décrite, les nœuds sont initialement sélectionnés sur lesquels il est possible de lancer un pod en fonction d'un ensemble prédicats (y compris vérifier si le nœud dispose de suffisamment de ressources pour exécuter le pod - PodFitsResources), puis pour chacun de ces nœuds, selon priorités des points sont attribués (y compris, plus un nœud possède de ressources libres, plus il est attribué de points - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) et le pod est lancé sur le nœud avec le plus de points (si plusieurs nœuds satisfont à cette condition à la fois, alors un aléatoire est sélectionné).

Dans le même temps, vous devez comprendre que le planificateur, lorsqu'il évalue les ressources disponibles d'un nœud, est guidé par les données stockées dans etcd - c'est-à-dire pour la quantité de ressource demandée/limitée de chaque pod exécuté sur ce nœud, mais pas pour la consommation réelle de ressources. Ces informations peuvent être obtenues à partir de la sortie de 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 exécutés sur un nœud spécifique, ainsi que les ressources demandées par chaque pod. Et voici à quoi ressemblent les journaux du planificateur lorsque le pod cronjob-cron-events-1573793820-xt6q9 est lancé (ces informations apparaîtront dans le journal du planificateur lorsque vous définirez le 10ème niveau de journalisation dans les arguments de la commande de démarrage -v=10) :

se connecter

I1115 07:57:21.637791       1 scheduling_queue.go:908] About to try and schedule pod nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                           
I1115 07:57:21.637804       1 scheduler.go:453] Attempting to schedule pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                                    
I1115 07:57:21.638285       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s5 is allowed, Node is running only 16 out of 110 Pods.                                                                               
I1115 07:57:21.638300       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s6 is allowed, Node is running only 20 out of 110 Pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s3 is allowed, Node is running only 20 out of 110 Pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s4 is allowed, Node is running only 17 out of 110 Pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, Node is running only 16 out of 110 Pods.                                                                              
I1115 07:57:21.638365       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s12 is allowed, Node is running only 9 out of 110 Pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s11 is allowed, Node is running only 11 out of 110 Pods.                                                                              
I1115 07:57:21.638385       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s1 is allowed, Node is running only 19 out of 110 Pods.                                                                               
I1115 07:57:21.638402       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s2 is allowed, Node is running only 21 out of 110 Pods.                                                                               
I1115 07:57:21.638383       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, Node is running only 16 out of 110 Pods.                                                                               
I1115 07:57:21.638335       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, Node is running only 18 out of 110 Pods.                                                                               
I1115 07:57:21.638408       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s13 is allowed, Node is running only 8 out of 110 Pods.                                                                               
I1115 07:57:21.638478       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, existing pods anti-affinity terms satisfied.                                                                         
I1115 07:57:21.638505       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, existing pods anti-affinity terms satisfied.                                                                          
I1115 07:57:21.638577       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, existing pods anti-affinity terms satisfied.                                                                          
I1115 07:57:21.638583       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s7 is allowed, Node is running only 25 out of 110 Pods.                                                                               
I1115 07:57:21.638932       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 9        
I1115 07:57:21.638946       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 8           
I1115 07:57:21.638961       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 9        
I1115 07:57:21.638971       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7        
I1115 07:57:21.638975       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 8           
I1115 07:57:21.638990       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7           
I1115 07:57:21.639022       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Score: (10)                                                                                                        
I1115 07:57:21.639030       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Score: (10)                                                                                                         
I1115 07:57:21.639034       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Score: (10)                                                                                                         
I1115 07:57:21.639041       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Score: (0)                                                                                                            
I1115 07:57:21.639053       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Score: (0)                                                                                                             
I1115 07:57:21.639059       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Score: (0)                                                                                                             
I1115 07:57:21.639061       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Score: (0)                                                                                                                   
I1115 07:57:21.639063       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)                                                                                                                   
I1115 07:57:21.639073       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Score: (0)                                                                                                                    
I1115 07:57:21.639077       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)                                                                                                                    
I1115 07:57:21.639085       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Score: (0)                                                                                                                    
I1115 07:57:21.639088       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)                                                                                                                    
I1115 07:57:21.639103       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)                                                                                                         
I1115 07:57:21.639109       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)                                                                                                          
I1115 07:57:21.639114       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)                                                                                                          
I1115 07:57:21.639127       1 generic_scheduler.go:781] Host nxs-k8s-s10 => Score 100037                                                                                                                                                                            
I1115 07:57:21.639150       1 generic_scheduler.go:781] Host nxs-k8s-s8 => Score 100034                                                                                                                                                                             
I1115 07:57:21.639154       1 generic_scheduler.go:781] Host nxs-k8s-s9 => Score 100037                                                                                                                                                                             
I1115 07:57:21.639267       1 scheduler_binder.go:269] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10"                                                                                                               
I1115 07:57:21.639286       1 scheduler_binder.go:279] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10": all PVCs bound and nothing to do                                                                             
I1115 07:57:21.639333       1 factory.go:733] Attempting to bind cronjob-cron-events-1573793820-xt6q9 to nxs-k8s-s10

On voit ici qu'au départ le planificateur filtre et génère une liste de 3 nœuds sur lesquels il peut être lancé (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Ensuite, il calcule des scores en fonction de plusieurs paramètres (dont BalancedResourceAllocation, LeastResourceAllocation) pour chacun de ces nœuds afin de déterminer le nœud le plus adapté. En fin de compte, le pod est planifié sur le nœud avec le plus grand nombre de points (ici deux nœuds à la fois ont le même nombre de points 100037, donc un nœud aléatoire est sélectionné - nxs-k8s-s10).

conclusion: si un nœud exécute des pods pour lesquels aucune restriction n'est définie, alors pour les k8 (du point de vue de la consommation des ressources), cela équivaudra à comme s'il n'y avait aucun pod de ce type sur ce nœud. Par conséquent, si vous disposez, conditionnellement, d'un pod avec un processus glouton (par exemple, wowza) et qu'aucune restriction n'est définie pour celui-ci, alors une situation peut survenir lorsque ce pod a réellement mangé toutes les ressources du nœud, mais pour k8s, ce nœud est considéré comme déchargé et il recevra le même nombre de points lors du classement (précisément en points évaluant les ressources disponibles) qu'un nœud qui ne dispose pas de pods fonctionnels, ce qui peut finalement conduire à une répartition inégale de la charge entre les nœuds.

L'expulsion de Pod

Comme vous le savez, chaque pod se voit attribuer l'une des 3 classes QoS :

  1. garanti - est attribué lorsque pour chaque conteneur du pod une demande et une limite sont spécifiées pour la mémoire et le processeur, et ces valeurs doivent correspondre
  2. éclatable — au moins un conteneur dans le pod a une requête et une limite, avec request < limit
  3. meilleur effort - lorsqu'aucun conteneur du pod n'est limité en ressources

Parallèlement, lorsqu'un nœud rencontre un manque de ressources (disque, mémoire), kubelet commence à classer et expulser les pods selon un algorithme spécifique qui prend en compte la priorité du pod et sa classe QoS. Par exemple, si nous parlons de RAM, alors en fonction de la classe QoS, les points sont attribués selon le principe suivant :

  • Sans conditions: -998
  • Meilleur effort: 1000
  • Éclatable: min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Ceux. avec la même priorité, le kubelet expulsera d'abord du nœud les pods avec la classe QoS de meilleur effort.

conclusion: si vous souhaitez réduire la probabilité que le pod souhaité soit expulsé du nœud en cas de manque de ressources sur celui-ci, alors en plus de la priorité, vous devez également prendre soin de définir la demande/limite pour celui-ci.

Mécanisme d'autoscaling horizontal des pods d'application (HPA)

Lorsque la tâche consiste à augmenter et 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 (Autoscaler horizontal de pods). Dont l'algorithme est le suivant :

  1. Les lectures actuelles de la ressource observée sont déterminées (currentMetricValue)
  2. Les valeurs souhaitées pour la ressource sont déterminées (desiredMetricValue), qui pour les ressources système sont définies à l'aide d'une requête
  3. Le nombre actuel de répliques est déterminé (currentReplicas)
  4. La formule suivante calcule le nombre souhaité de répliques (desiredReplicas)
    wantedReplicas = [ currentReplicas * ( currentMetricValue / wantedMetricValue )]

Dans ce cas, la mise à l'échelle n'aura pas lieu lorsque le coefficient (currentMetricValue / wantedMetricValue) est proche de 1 (dans ce cas, nous pouvons définir nous-mêmes l'erreur tolérée ; par défaut elle est de 0.1).

Regardons le fonctionnement de hpa en utilisant l'exemple de l'application app-test (décrite comme Déploiement), où il est nécessaire de modifier le nombre de répliques en fonction de la consommation CPU :

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

    Ceux. nous voyons que le pod d'application est initialement lancé dans deux instances, dont chacune contient deux conteneurs nginx et nginx-exporter, pour chacun desquels un spécifié demandes pour le processeur.

  • 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

    Ceux. Nous avons créé un hpa qui surveillera le test de l'application de déploiement et ajustera le nombre de pods avec l'application en fonction de l'indicateur du processeur (nous nous attendons à ce que le pod consomme 30 % du processeur qu'il demande), le nombre de répliques étant en la plage de 2 à 10.

    Voyons maintenant le mécanisme de fonctionnement du hpa si l'on applique une charge sur l'un des foyers :

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

Au total, nous avons les éléments suivants :

  • La valeur souhaitée (desiredMetricValue) - selon les paramètres hpa, nous avons 30%
  • Valeur actuelle (currentMetricValue) - pour le calcul, le contrôleur-gestionnaire calcule la valeur moyenne de la consommation des ressources en %, c'est-à-dire conditionnellement, il effectue ce qui suit :
    1. Reçoit les valeurs absolues des métriques du pod du serveur de métriques, c'est-à-dire 101m et 4m
    2. Calcule la valeur absolue moyenne, c'est-à-dire (101m + 4m) / 2 = 53m
    3. Obtient la valeur absolue de la consommation de ressources souhaitée (pour cela, les demandes de tous les conteneurs sont résumées) 60m + 30m = 90m
    4. Calcule le pourcentage moyen de consommation CPU par rapport au pod de requête, c'est-à-dire 53m / 90m * 100% = 59%

Nous avons maintenant tout ce qu'il faut pour déterminer s'il faut modifier le nombre de répliques ; pour cela, nous calculons le coefficient :

ratio = 59% / 30% = 1.96

Ceux. le nombre de répliques doit être augmenté d'environ 2 fois et s'élever à [2 * 1.96] = 4.

Conclusion: Comme vous pouvez le constater, pour que ce mécanisme fonctionne, une condition nécessaire est la présence de requêtes pour tous les conteneurs dans le pod observé.

Mécanisme d'autoscaling horizontal des nœuds (Cluster Autoscaler)

Afin de neutraliser l'impact négatif sur le système lors des surtensions de charge, disposer d'un hpa configuré ne suffit pas. Par exemple, selon les paramètres du gestionnaire de contrôleur hpa, il décide que le nombre de répliques doit être augmenté de 2 fois, mais les nœuds ne disposent pas de ressources libres pour exécuter un tel nombre de pods (c'est-à-dire que le nœud ne peut pas fournir le ressources demandées au pod de requêtes) et ces pods passent à l'état En attente.

Dans ce cas, si le fournisseur dispose d'un IaaS/PaaS correspondant (par exemple, GKE/GCE, AKS, EKS, etc.), un outil comme Mise à l'échelle automatique des nœuds. Il vous permet de définir le nombre maximum et minimum de nœuds dans le cluster et d'ajuster automatiquement le nombre actuel de nœuds (en appelant l'API du fournisseur de cloud pour commander/supprimer un nœud) en cas de manque de ressources dans le cluster et les pods. ne peuvent pas être planifiés (sont à l’état En attente).

Conclusion: Pour pouvoir mettre à l'échelle automatiquement les nœuds, il est nécessaire de définir des requêtes dans les conteneurs de pods afin que les k8 puissent évaluer correctement la charge sur les nœuds et signaler par conséquent qu'il n'y a pas de ressources dans le cluster pour lancer le pod suivant.

Conclusion

Il convient de noter que la définition de limites de ressources de conteneur n'est pas une condition nécessaire au bon fonctionnement 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 planificateur en termes d'équilibrage de charge entre les nœuds k8s
  2. Pour réduire la probabilité qu’un événement d’« expulsion de pod » se produise
  3. Pour que la mise à l'échelle automatique horizontale des modules d'application (HPA) fonctionne
  4. Pour la mise à l'échelle automatique horizontale des nœuds (Cluster Autoscaling) pour les fournisseurs de cloud

Lisez également d'autres articles sur notre blog:

Source: habr.com

Ajouter un commentaire