Comment accéder aux ressources d'un pod Kubernetes

Comment accéder aux ressources d'un pod KubernetesLa Récompense par Tohad

Lors de la première utilisation de Kubernetes, on oublie généralement de configurer les ressources des conteneurs. À ce stade, il suffit de s'assurer que l'image Docker fonctionne et peut être déployée dans le cluster Kubernetes.

Mais par la suite, l'application doit être déployée dans le cluster de production avec d'autres applications. Pour cela, il est nécessaire d'allouer des ressources au conteneur et de s'assurer qu'elles sont suffisantes pour le lancement et le fonctionnement de l'application, sans causer de problèmes aux autres applications en cours d'exécution.

Commande Kubernetes aaS de Mail.ru J'ai traduit un article sur les ressources des conteneurs (CPU & MEM), les demandes et les limites de ressources. Vous apprendrez quels avantages ces réglages apportent et ce qu'il adviendra si vous ne les configurez pas.

Ressources de calcul

Nous avons deux types de ressources avec les unités suivantes :

  • Processeur central (CPU) — cœurs;
  • Mémoire (MEM) — octets.

Les ressources sont spécifiées pour chaque conteneur. Dans le fichier YAML suivant pour le Pod, vous verrez la section des ressources, qui contient les ressources demandées et limites :

  • Ressources demandées pour le Pod = somme des ressources demandées de tous les conteneurs ;
  • Ressources limites du Pod = somme des ressources limites de tous les conteneurs.

apiVersion: v1
kind: Pod
metadata:
  name: backend-pod-name
  labels:
    application: backend
spec:
  containers:
    - name: main-container
      image: my-backend
      tag: v1
      ports:
      - containerPort: 8080
      resources:
        requests:
          cpu: 0.2 # CPU DEMANDÉ : 200m cœurs
          memory: "1Gi" # MÉMOIRE DEMANDÉE : 1Gi
        limits:
          cpu: 1 # UTILISATION MAXIMALE DU CPU : 1 cœur
          memory: "1Gi" # UTILISATION MAXIMALE DE LA MÉMOIRE : 1Gi
    - name: other-container
      image: other-app
      tag: v1
      ports:
      - containerPort: 8000
      resources:
        requests:
          cpu: "200m" # CPU DEMANDÉ : 200m cœurs
          memory: "0.5Gi" # MÉMOIRE DEMANDÉE : 0.5Gi
        limits:
          cpu: 1 # UTILISATION MAXIMALE DU CPU : 1 cœur
          memory: "1Gi" # UTILISATION MAXIMALE DE LA MÉMOIRE : 1Gi

Exemple de ressources demandées et limites

Champ resources.requested de la spécification du Pod — un des éléments utilisés pour rechercher le nœud adéquat. Cela permet déjà de planifier le déploiement du Pod. Comment trouve-t-on un nœud approprié ?

Kubernetes se compose de plusieurs composants, y compris un nœud principal ou nœud maître (Kubernetes Control Plane). Dans le nœud maître, plusieurs processus sont en cours : kube-apiserver, kube-controller-manager et kube-scheduler.

Le processus kube-scheduler est responsable de l'examen des nouveaux modules créés et de la recherche des nœuds de travail possibles qui répondent à toutes les demandes des modules, y compris en termes de ressources demandées. La liste des nœuds trouvés par kube-scheduler est classée. Le Pod est planifié sur le nœud avec les meilleures notes.

Comment accéder aux ressources d'un pod KubernetesOù sera placé le Pod violet ?

L'image montre que le kube-scheduler doit planifier un nouveau Pod violet. Le cluster Kubernetes contient deux nœuds : A et B. Comme vous pouvez le constater, le kube-scheduler ne peut pas planifier le Pod sur le nœud A — les ressources disponibles (non requises) ne correspondent pas aux demandes du Pod violet. Ainsi, la mémoire demandée par le Pod violet est de 1 Go, ce qui ne peut pas tenir sur le nœud A, car la mémoire disponible est de 0,5 Go. Mais le nœud B a suffisamment de ressources. En fin de compte, le kube-scheduler décide que la destination du Pod violet est le nœud B.

Maintenant, nous savons comment les ressources demandées influencent le choix du nœud pour exécuter un Pod. Mais comment les ressources limites influencent-elles ?

Les ressources limites sont la frontière que le CPU/MEM ne peut pas franchir. Cependant, la ressource CPU est flexible, donc les conteneurs atteignant les valeurs limites du CPU ne provoqueront pas l'arrêt du Pod. Au lieu de cela, un throttling CPU sera appliqué. Si la limite d'utilisation de la MEM est atteinte, le conteneur sera arrêté en raison de l'OOM-Killer et redémarré, si cela est autorisé par la configuration RestartPolicy.

Ressources demandées et limites en détail

Comment accéder aux ressources d'un pod KubernetesRelation des ressources entre Docker et Kubernetes

La meilleure façon d'expliquer comment fonctionnent les ressources demandées et limites est de représenter la relation entre Kubernetes et Docker. Dans l'image ci-dessus, vous pouvez voir comment les champs Kubernetes et les options de démarrage Docker sont liés.

Mémoire : demande et limite

containers:
...
 resources:
   requests:
     memory: "0.5Gi"
   limits:
     memory: "1Gi"

Comme mentionné ci-dessus, la mémoire est mesurée en octets. En se basant sur documentation Kubernetes, nous pouvons indiquer la mémoire sous forme de nombre. En général, c'est un entier, par exemple 2678 — c'est-à-dire 2678 octets. On peut également utiliser des suffixes G et Gi, il est important de se rappeler qu'ils ne sont pas équivalents. Le premier est décimal, tandis que le second est binaire. Comme exemple, mentionné dans la documentation k8s : 128974848, 129e6, 129M, 123Mi — ils sont pratiquement équivalents.

La clé Kubernetes limits.memory correspond à l'option --memory de Docker. En ce qui concerne le request.memory la flèche pour Docker est absente, car Docker n'utilise pas ce champ. Vous pourriez demander si cela est vraiment nécessaire ? Oui, c'est nécessaire. Comme je l'ai déjà mentionné, ce champ a de l'importance pour Kubernetes. Sur la base des informations qu'il contient, le kube-scheduler détermine sur quel nœud planifier le Pod.

Que se passe-t-il si on définit une demande de mémoire insuffisante ?

Si le conteneur atteint les limites de la mémoire demandée, le Pod est placé dans un groupe de Pods qui s'arrêtent en cas de manque de mémoire dans le nœud.

Que se passe-t-il si vous définissez une limite de mémoire trop basse ?

Si le conteneur dépasse la limite de mémoire, il sera terminé pour cause d'OOM-Killed. Il sera redémarré si possible en fonction de RestartPolicy, où la valeur par défaut est Always.

Que se passera-t-il si aucune mémoire demandée n'est spécifiée ?

Kubernetes prendra la limite de mémoire et l'établira comme valeur par défaut.

Que peut-il se passer si aucune limite de mémoire n'est spécifiée ?

Le conteneur n'a aucune restriction, il peut utiliser autant de mémoire qu'il le souhaite. S'il commence à utiliser toute la mémoire disponible du nœud, il sera tué par OOM. Le conteneur sera ensuite redémarré, si cela est possible selon la RestartPolicy.

Que se passe-t-il si aucune limite de mémoire n'est définie ?

C'est le pire scénario : le planificateur ne sait pas combien de ressources le conteneur nécessite, ce qui peut causer de graves problèmes sur le nœud. Dans ce cas, il serait bon d'avoir des limites par défaut dans l'espace de noms (établies par LimitRange). Il n'y a pas de limites par défaut - le Pod n'a aucune restriction, il peut utiliser autant de mémoire qu'il le souhaite.

Si la mémoire demandée est supérieure à ce que peut offrir le nœud, le Pod ne sera pas planifié. Il est important de se rappeler que Requests.memory n'est pas une valeur minimale. C'est une description de la quantité de mémoire suffisante pour le fonctionnement continu du conteneur.

On recommande généralement de définir la même valeur pour request.memory et limit.memory. Cela permet à Kubernetes de ne pas planifier le Pod sur un nœud qui a suffisamment de mémoire pour le démarrage du Pod, mais pas assez pour son fonctionnement. Gardez à l'esprit que lors de la planification du Pod, Kubernetes ne prend en compte que requests.memory, et limits.memory et ne prend pas en compte.

CPU : demande et limite

containers:
...
 resources:
   requests:
     cpu: 1
   limits:
     cpu: "1200m"

Pour le CPU, c'est un peu plus compliqué. En revenant à l'image de la relation entre Kubernetes et Docker, on peut remarquer que request.cpu correspond à --cpu-shares, tandis que limit.cpu correspond à l'option cpus dans Docker.

Le CPU demandé par Kubernetes est multiplié par 1024 — la proportion des cycles CPU. Si vous souhaitez demander un cœur complet, vous devez ajouter cpu: 1, comme indiqué ci-dessus.

La demande d'un cœur complet (ratio = 1024) ne signifie pas que votre conteneur l'obtiendra. Si votre hôte ne dispose que d'un seul cœur et que vous utilisez plus d'un conteneur, tous les conteneurs doivent partager le CPU disponible entre eux. Comment cela se passe-t-il ? Regardons l'illustration.

Comment accéder aux ressources d'un pod Kubernetes
Demande de CPU - système à un cœur

Imaginons que vous ayez un système hôte avec un seul cœur sur lequel des conteneurs sont en cours d'exécution. Maman (Kubernetes) a cuit un gâteau (CPU) et veut le partager avec ses enfants (les conteneurs). Trois enfants veulent un gâteau entier (ratio = 1024), un autre enfant veut la moitié d'un gâteau (512). Maman veut être juste et fait un calcul simple.

# Сколько пирогов хотят дети?
# 3 ребенка хотят по целому пирогу и еще один хочет половину пирога
cakesNumberKidsWant = (3 * 1) + (1 * 0.5) = 3.5
# Выражение получается так:
3 (ребенка/контейнера) * 1 (целый пирог/полное ядро) + 1 (ребенок/контейнер) * 0.5 (половина пирога/половина ядра)
# Сколько пирогов испечено?
availableCakesNumber = 1
# Сколько пирога (максимально) дети реально могут получить?
newMaxRequest = 1 / 3.5 =~ 28%

D'après le calcul, trois enfants recevront 28 % d'un cœur, et non un cœur entier. Le quatrième enfant recevra 14 % d'un cœur complet, et non la moitié. Mais tout sera différent si vous avez un système multicœur.

Comment accéder aux ressources d'un pod Kubernetes
Demande de CPU - système multicœur (4)

Dans l'image ci-dessus, on voit que trois enfants veulent un gâteau entier, et un veut la moitié. Comme maman a cuit quatre gâteaux, chacun de ses enfants recevra autant qu'il le souhaite. Dans un système multicœur, les ressources processeur sont réparties sur tous les cœurs disponibles. Si un conteneur est limité à moins d'un cœur complet de CPU, il peut quand même l'utiliser à 100 %.

Les calculs ci-dessus sont simplifiés pour illustrer comment le CPU est réparti entre les conteneurs. Bien sûr, en plus des conteneurs eux-mêmes, il existe d'autres processus qui utilisent également les ressources CPU. Lorsque des processus dans un conteneur sont inactifs, d'autres peuvent utiliser sa ressource. CPU : "200m" correspond à CPU : 0,2, ce qui représente environ 20 % d'un cœur.

Maintenant, parlons de limit.cpu. Le CPU, qui limite Kubernetes, est multiplié par 100. Le résultat est le temps pendant lequel le conteneur peut utiliser chaque 100 microsecondes (cpu-period).

limit.cpu correspond à l'option Docker --cpus. C'est une nouvelle combinaison des anciennes --cpu-period et --cpu-quota. En le définissant, nous indiquons combien de ressources CPU disponibles un conteneur peut utiliser au maximum avant que le throttling ne commence :

  • cpus — combinaison cpu-period et cpu-quota. cpus = 1.5 équivaut à définir cpu-period = 100000 et cpu-quota = 150000;
  • cpu-period — période du planificateur CPU CFS, par défaut 100 microsecondes ;
  • cpu-quota — nombre de microsecondes à l'intérieur cpu-period, dont le conteneur est limité.

Que se passera-t-il si vous configurez une demande de CPU insuffisante ?

Si le conteneur a besoin de plus que ce qui est alloué, il volera du CPU à d'autres processus.

Que se passera-t-il si vous configurez une limite de CPU insuffisante ?

Étant donné que la ressource CPU est réglementée, le throttling sera activé.

Que se passera-t-il si vous ne spécifiez pas de demande de CPU ?

Comme pour la mémoire, la valeur de la demande est égale à la limite.

Que se passera-t-il si vous ne spécifiez pas de limite de CPU ?

Le conteneur utilisera autant de CPU que nécessaire. Si une politique de CPU par défaut est définie dans l'espace de noms (LimitRange), cette limite sera également appliquée au conteneur.

Que se passera-t-il si vous ne spécifiez ni demande ni limite de CPU ?

Comme pour la mémoire, ceci est le pire scénario. Le planificateur ne saura pas combien de ressources votre conteneur a besoin, ce qui peut causer des problèmes graves sur le nœud. Pour éviter cela, il est nécessaire de définir des limites par défaut pour les espaces de noms (LimitRange).

N'oubliez pas : si vous demandez plus de CPU que ce que les nœuds peuvent fournir, le Pod ne sera pas planifié. Requests.cpu — ce n'est pas la valeur minimale, mais la valeur suffisante pour faire fonctionner le Pod sans erreurs. Si l'application n'effectue pas de calculs intensifs, le mieux est de définir request.cpu <= 1 et d'exécuter autant de répliques que nécessaire.

La quantité idéale de ressources demandées ou de limite de ressources

Nous avons appris à propos de la limitation des ressources de calcul. Il est maintenant temps de répondre à la question : "De combien de ressources mon Pod a-t-il besoin pour faire fonctionner l'application sans problèmes ? Quelle est la quantité idéale ?".

Malheureusement, il n'y a pas de réponses définitives à ces questions. Si vous ne savez pas comment votre application fonctionne, combien de CPU ou de mémoire elle nécessite, le mieux est de donner à l'application beaucoup de mémoire et de CPU, puis d'exécuter des tests de performance.

En plus des tests de performance, surveillez le comportement de l'application au cours de la semaine avec un monitoring. Si les graphiques montrent que votre application consomme moins de ressources que ce que vous avez demandé, vous pouvez réduire le montant de CPU ou de mémoire demandé.

Comme exemple, regardez ce tableau de bord Grafana. Il affiche la différence entre les ressources demandées ou la limite de ressources et l'utilisation actuelle des ressources.

Conclusion

La demande et la limitation des ressources aident à maintenir la fonctionnalité d'un cluster Kubernetes. Une configuration adéquate des limites minimise les coûts et maintient les applications en fonctionnement de manière constante.

En résumé, il y a plusieurs points à garder à l'esprit :

  1. Les ressources demandées sont la configuration qui est prise en compte au moment du démarrage (lorsque Kubernetes planifie le déploiement de l'application). En revanche, la limitation des ressources est importante pendant l'exécution — lorsque l'application est déjà en cours d'exécution sur un nœud.
  2. Comparé à la mémoire, le CPU est une ressource régulable. En cas de pénurie de CPU, votre Pod ne s'arrêtera pas, un mécanisme de throttling s'enclenchera.
  3. Les ressources demandées et les limites de ressources ne sont pas des valeurs minimales et maximales ! En définissant les ressources demandées, vous garantissez que l'application fonctionnera sans problème.
  4. Il est bon de définir une demande de mémoire égale à la limite de mémoire.
  5. Il est bon de définir la demande CPU <= 1, si l'application ne nécessite pas de calculs complexes.
  6. Si vous demandez plus de ressources qu'il n'y en a sur le nœud, alors le Pod ne sera jamais planifié sur ce nœud.
  7. Pour déterminer la quantité appropriée de ressources demandées / limites de ressources, utilisez des tests de charge et du monitoring.

J'espère que cet article vous aidera à comprendre le concept fondamental des limitations de ressources. Et vous pourrez appliquer ces connaissances dans votre travail.

Bonne chance !

Que lire d'autre :

  1. Observabilité SRE : espaces de noms et structure des métriques.
  2. 90+ outils utiles pour Kubernetes : déploiement, gestion, monitoring, sécurité et plus encore.
  3. Notre chaîne Autour de Kubernetes sur Telegram.

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