Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Le 27 avril à la conférence Grève 2019, dans le cadre de la section « DevOps », le rapport « Autoscaling et gestion des ressources dans Kubernetes » a été remis. Il explique comment vous pouvez utiliser les K8 pour garantir la haute disponibilité de vos applications et garantir des performances optimales.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Par tradition, nous sommes heureux de vous présenter vidéo du reportage (44 minutes, beaucoup plus informatif que l'article) et le résumé principal sous forme de texte. Aller!

Analysons mot par mot le sujet du rapport et commençons par la fin.

Kubernetes

Disons que nous avons des conteneurs Docker sur notre hôte. Pour quoi? Pour garantir la répétabilité et l'isolation, qui à leur tour permettent un déploiement simple et efficace, CI/CD. Nous disposons de nombreux véhicules de ce type avec des conteneurs.

Que propose Kubernetes dans ce cas ?

  1. On arrête de penser à ces machines et on commence à travailler avec le « cloud » groupe de conteneurs ou pods (groupes de conteneurs).
  2. De plus, nous ne pensons même pas aux pods individuels, mais gérons plusоdes groupes plus importants. Tel primitives de haut niveau permettez-nous de dire qu'il existe un modèle pour exécuter une certaine charge de travail, et voici le nombre d'instances requis pour l'exécuter. Si nous modifions ultérieurement le modèle, toutes les instances changeront.
  3. Avec API déclarative Au lieu d'exécuter une séquence de commandes spécifiques, nous décrivons la « structure du monde » (en YAML) créée par Kubernetes. Et encore une fois : lorsque la description change, son affichage réel change également.

La gestion des ressources

Processeur

Exécutons nginx, php-fpm et mysql sur le serveur. Ces services auront en réalité encore plus de processus en cours d'exécution, dont chacun nécessite des ressources informatiques :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)
(les chiffres sur la diapositive sont des « perroquets », le besoin abstrait de chaque processus en puissance de calcul)

Pour faciliter le travail avec cela, il est logique de combiner les processus en groupes (par exemple, tous les processus nginx en un seul groupe « nginx »). Une manière simple et évidente de procéder consiste à placer chaque groupe dans un conteneur :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Pour continuer, vous devez vous rappeler ce qu'est un conteneur (sous Linux). Leur apparition a été rendue possible grâce à trois fonctionnalités clés du noyau, implémentées depuis assez longtemps : capacités, espaces de noms и groupes de contrôle. Et le développement ultérieur a été facilité par d'autres technologies (y compris des « shells » pratiques comme Docker) :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Dans le cadre du rapport, nous nous intéressons uniquement à groupes de contrôle, car les groupes de contrôle font partie de la fonctionnalité des conteneurs (Docker, etc.) qui implémente la gestion des ressources. Les processus regroupés en groupes, comme nous le souhaitions, sont des groupes de contrôle.

Revenons aux exigences CPU pour ces processus, et maintenant pour les groupes de processus :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)
(Je répète que tous les chiffres sont une expression abstraite du besoin de ressources)

Dans le même temps, le processeur lui-même dispose d'une certaine ressource limitée (dans l'exemple, c'est 1000), dont tout le monde peut manquer (la somme des besoins de tous les groupes est de 150+850+460=1460). Que se passera-t-il dans ce cas ?

Le noyau commence à distribuer les ressources et le fait « équitablement », en donnant la même quantité de ressources à chaque groupe. Mais dans le premier cas, il y en a plus que nécessaire (333>150), donc l'excédent (333-150=183) reste en réserve, qui est également répartie également entre deux autres conteneurs :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Résultat : le premier conteneur avait suffisamment de ressources, le deuxième – il n’avait pas assez de ressources, le troisième – il n’avait pas assez de ressources. C'est le résultat d'actions planificateur "honnête" sous Linux - CFS. Son fonctionnement peut être ajusté à l'aide de l'affectation poids chacun des conteneurs. Par exemple, comme ceci :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Regardons le cas d'un manque de ressources dans le deuxième conteneur (php-fpm). Toutes les ressources du conteneur sont réparties de manière égale entre les processus. En conséquence, le processus principal fonctionne bien, mais tous les travailleurs ralentissent, recevant moins de la moitié de ce dont ils ont besoin :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

C'est ainsi que fonctionne le planificateur CFS. Nous appellerons en outre les poids que nous attribuons aux conteneurs demandes. Pourquoi il en est ainsi - voir plus loin.

Regardons toute la situation de l'autre côté. Comme vous le savez, tous les chemins mènent à Rome et, dans le cas d’un ordinateur, au CPU. Un processeur, de nombreuses tâches : vous avez besoin d'un feu tricolore. La manière la plus simple de gérer les ressources est celle des « feux de signalisation » : ils donnent à un processus un temps d'accès fixe au CPU, puis au suivant, etc.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Cette approche est appelée quotas stricts (limitation dure). Rappelons-le simplement comme limites. Cependant, si vous distribuez des limites à tous les conteneurs, un problème se pose : MySQL roulait sur la route et à un moment donné, son besoin en CPU a pris fin, mais tous les autres processus sont obligés d'attendre que le CPU inactif.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Revenons au noyau Linux et à son interaction avec le CPU - le tableau général est le suivant :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

cgroup a deux paramètres - ce sont essentiellement deux « torsions » simples qui vous permettent de déterminer :

  1. le poids du conteneur (demandes) est part;
  2. le pourcentage du temps CPU total pour travailler sur les tâches du conteneur (limites) est quota.

Comment mesurer le CPU ?

Il existe différentes manières :

  1. Qu'est-ce que perroquets, personne ne le sait, il faut négocier à chaque fois.
  2. Les intérêts plus clair, mais relatif : 50% d'un serveur à 4 cœurs et à 20 cœurs sont des choses complètement différentes.
  3. Vous pouvez utiliser ceux déjà mentionnés poids, ce que Linux connaît, mais ils sont aussi relatifs.
  4. L'option la plus adéquate consiste à mesurer les ressources informatiques dans secondes. Ceux. en secondes de temps processeur par rapport aux secondes de temps réel : 1 seconde de temps processeur a été donnée pour 1 seconde réelle - il s'agit d'un cœur de processeur entier.

Pour rendre la parole encore plus facile, ils ont commencé à mesurer directement dans graines, signifiant par eux le même temps CPU par rapport au temps réel. Étant donné que Linux comprend les pondérations, mais pas tellement le temps CPU/cœurs, un mécanisme était nécessaire pour passer de l'un à l'autre.

Prenons un exemple simple avec un serveur avec 3 cœurs de processeur, où trois pods recevront des poids (500, 1000 1500 et 0,5 1) qui seront facilement convertis en parties correspondantes des cœurs qui leur sont allouées (1,5, XNUMX et XNUMX).

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Si vous prenez un deuxième serveur, où il y aura deux fois plus de cœurs (6), et y placez les mêmes pods, la répartition des cœurs peut être facilement calculée en multipliant simplement par 2 (respectivement 1, 2 et 3). Mais un moment important survient lorsqu'un quatrième pod apparaît sur ce serveur, dont le poids, par commodité, sera de 3000. Il enlève une partie des ressources CPU (la moitié des cœurs), et pour les pods restants elles sont recalculées (réduites de moitié) :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Kubernetes et ressources CPU

Dans Kubernetes, les ressources CPU sont généralement mesurées en milliadrax, c'est à dire. 0,001 noyaux sont pris comme poids de base. (La même chose dans la terminologie Linux/cgroups est appelée partage de CPU, bien que, plus précisément, 1000 1024 millicores = XNUMX XNUMX partages de CPU.) K8s garantit qu'il ne place pas plus de pods sur le serveur qu'il n'y a de ressources CPU pour la somme des poids de tous les pods.

Comment cela peut-il arriver? Lorsque vous ajoutez un serveur à un cluster Kubernetes, le nombre de cœurs de processeur dont il dispose est indiqué. Et lors de la création d'un nouveau pod, le planificateur Kubernetes sait de combien de cœurs ce pod aura besoin. Ainsi, le pod sera attribué à un serveur où il y a suffisamment de cœurs.

Que se passera-t-il si aucun la demande est spécifiée (c'est-à-dire que le pod n'a pas un nombre défini de cœurs dont il a besoin) ? Voyons comment Kubernetes compte généralement les ressources.

Pour un pod, vous pouvez spécifier à la fois les requêtes (planificateur CFS) et les limites (vous vous souvenez du feu tricolore ?) :

  • S'ils sont spécifiés égaux, alors le pod se voit attribuer une classe QoS garantie. Ce nombre de cœurs toujours disponibles est garanti.
  • Si la demande est inférieure à la limite - classe QoS éclatable. Ceux. Nous nous attendons à ce qu'un pod, par exemple, utilise toujours 1 cœur, mais cette valeur ne constitue pas une limitation pour celui-ci : parfois Le pod peut en utiliser davantage (lorsque le serveur dispose de ressources gratuites pour cela).
  • Il existe également une classe QoS meilleur effort — il inclut ces mêmes pods pour lesquels la demande n'est pas spécifiée. Les ressources leur sont données en dernier lieu.

Mémoire

Avec la mémoire, la situation est similaire, mais légèrement différente - après tout, la nature de ces ressources est différente. En général, l'analogie est la suivante :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Voyons comment les requêtes sont implémentées en mémoire. Laissez les pods vivre sur le serveur, en modifiant la consommation de mémoire, jusqu'à ce que l'un d'eux devienne si gros qu'il manque de mémoire. Dans ce cas, le tueur OOM apparaît et tue le processus le plus important :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Cela ne nous convient pas toujours, il est donc possible de réglementer quels processus sont importants pour nous et ne doivent pas être interrompus. Pour ce faire, utilisez le paramètre oom_score_adj.

Revenons aux classes QoS du CPU et faisons une analogie avec les valeurs oom_score_adj qui déterminent les priorités de consommation de mémoire pour les pods :

  • La valeur oom_score_adj la plus basse pour un pod - -998 - signifie qu'un tel pod doit être tué en dernier, ceci garantie.
  • Le plus élevé - 1000 - est meilleur effort, ces gousses sont tuées en premier.
  • Pour calculer les valeurs restantes (éclatable) il existe une formule dont l'essence se résume au fait que plus un pod a demandé de ressources, moins il a de chances d'être tué.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

La deuxième "torsion" - limit_in_bytes - pour les limites. Avec lui, tout est plus simple : on attribue simplement la quantité maximale de mémoire émise, et ici (contrairement au CPU) il n'est pas question de savoir comment la mesurer (mémoire).

En tout

Chaque pod dans Kubernetes est donné requests и limits - les deux paramètres pour le CPU et la mémoire :

  1. en fonction des requêtes, le planificateur Kubernetes fonctionne, qui distribue les pods entre les serveurs ;
  2. sur la base de tous les paramètres, la classe QoS du pod est déterminée ;
  3. Les poids relatifs sont calculés en fonction des requêtes CPU ;
  4. le planificateur CFS est configuré en fonction des requêtes CPU ;
  5. OOM Killer est configuré en fonction des demandes de mémoire ;
  6. un « feu tricolore » est configuré en fonction des limites du processeur ;
  7. En fonction des limites de mémoire, une limite est configurée pour le groupe de contrôle.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

En général, cette image répond à toutes les questions sur la manière dont se déroule l'essentiel de la gestion des ressources dans Kubernetes.

Mise à l'échelle automatique

Autoscaler de cluster K8s

Imaginons que l'ensemble du cluster soit déjà occupé et qu'un nouveau pod doive être créé. Bien que le pod ne puisse pas apparaître, son statut reste bloqué En Attente. Pour qu'elle apparaisse, nous pouvons connecter un nouveau serveur au cluster ou... installer cluster-autoscaler, qui le fera pour nous : commander une machine virtuelle auprès du fournisseur de cloud (à l'aide d'une requête API) et la connecter au cluster , après quoi le pod sera ajouté .

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Il s'agit de la mise à l'échelle automatique du cluster Kubernetes, qui fonctionne très bien (d'après notre expérience). Cependant, comme ailleurs, il y a ici quelques nuances...

Tant que nous augmentions la taille du cluster, tout allait bien, mais que se passe-t-il lorsque le cluster a commencé à se libérer? Le problème est que la migration des pods (pour libérer des hôtes) est très difficile techniquement et coûteuse en termes de ressources. Kubernetes utilise une approche complètement différente.

Prenons un cluster de 3 serveurs dotés d'un déploiement. Il dispose de 6 pods : il y en a désormais 2 pour chaque serveur. Pour une raison quelconque, nous voulions désactiver l'un des serveurs. Pour ce faire, nous utiliserons la commande kubectl drain, qui:

  • interdira l'envoi de nouveaux pods à ce serveur ;
  • supprimera les pods existants sur le serveur.

Puisque Kubernetes est responsable du maintien du nombre de pods (6), il suffit va recréer sur d'autres nœuds, mais pas sur celui en cours de désactivation, car il est déjà marqué comme indisponible pour l'hébergement de nouveaux pods. Il s’agit d’un mécanisme fondamental pour Kubernetes.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Cependant, il y a aussi une nuance ici. Dans une situation similaire, pour StatefulSet (au lieu de Deployment), les actions seront différentes. Nous avons maintenant déjà une application avec état - par exemple, trois pods avec MongoDB, dont l'un a un problème (les données ont été corrompues ou une autre erreur qui empêche le pod de démarrer correctement). Et nous décidons à nouveau de désactiver un serveur. Que va-t-il se passer ?

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

MongoDB pourrait mourir car il lui faut un quorum : pour un cluster de trois installations, au moins deux doivent fonctionner. Cependant, ceci n'arrive pas - grâce à PodDisruptionBudget. Ce paramètre détermine le nombre minimum requis de pods de travail. Sachant que l'un des pods MongoDB ne fonctionne plus et voyant que PodDisruptionBudget est défini pour MongoDB minAvailable: 2, Kubernetes ne vous permettra pas de supprimer un pod.

Conclusion : pour que le mouvement (et en fait la recréation) des pods fonctionne correctement lors de la sortie du cluster, il est nécessaire de configurer PodDisruptionBudget.

Mise à l'échelle horizontale

Considérons une autre situation. Il existe une application exécutée en tant que déploiement dans Kubernetes. Le trafic utilisateur arrive à ses pods (par exemple, il y en a trois) et nous y mesurons un certain indicateur (par exemple, la charge du processeur). Lorsque la charge augmente, nous l'enregistrons selon un planning et augmentons le nombre de pods pour distribuer les demandes.

Aujourd'hui dans Kubernetes cela n'a pas besoin d'être fait manuellement : une augmentation/diminution automatique du nombre de pods est configurée en fonction des valeurs des indicateurs de charge mesurés.

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Les principales questions ici sont : que mesurer exactement и comment interpréter valeurs obtenues (pour prendre une décision sur la modification du nombre de pods). Vous pouvez mesurer beaucoup de choses :

Autoscaling et gestion des ressources dans Kubernetes (présentation et rapport vidéo)

Comment procéder techniquement : collecter des métriques, etc. — J'ai parlé en détail dans le rapport de Surveillance et Kubernetes. Et le principal conseil pour choisir les paramètres optimaux est expérience!

Il est Méthode UTILISER (Saturation d'utilisation et erreurs), dont la signification est la suivante. Sur quelle base est-il judicieux de faire évoluer, par exemple, php-fpm ? Compte tenu du fait que les travailleurs s'épuisent, c'est utilisation. Et si les travailleurs sont terminés et que les nouvelles connexions ne sont pas acceptées, c'est déjà saturation. Ces deux paramètres doivent être mesurés et, en fonction des valeurs, une mise à l'échelle doit être effectuée.

Au lieu d'une conclusion

Le rapport a une suite : sur la mise à l'échelle verticale et comment sélectionner les bonnes ressources. J'en parlerai dans de prochaines vidéos sur notre YouTube - abonnez-vous pour ne rien manquer !

Vidéos et diapositives

Vidéo de la performance (44 minutes) :

Présentation du rapport :

PS

Autres rapports sur Kubernetes sur notre blog :

Source: habr.com

Ajouter un commentaire