Limites du processeur et limitation agressive dans Kubernetes

Noter. trad.: Cette histoire révélatrice d'Omio, un agrégateur de voyages européen, emmÚne les lecteurs de la théorie de base aux fascinantes subtilités pratiques de la configuration de Kubernetes. La connaissance de tels cas permet non seulement d'élargir vos horizons, mais également d'éviter des problÚmes non triviaux.

Limites du processeur et limitation agressive dans Kubernetes

Avez-vous dĂ©jĂ  vu une application rester bloquĂ©e, cesser de rĂ©pondre aux contrĂŽles de santĂ© et ne pas comprendre pourquoi ? Une explication possible est liĂ©e aux limites de quota de ressources CPU. C'est ce dont nous parlerons dans cet article.

TL; DR:
Nous recommandons fortement de désactiver les limites de CPU dans Kubernetes (ou de désactiver les quotas CFS dans Kubelet) si vous utilisez une version du noyau Linux avec une erreur de quota CFS. Dans le noyau il y a sérieux et bien connu un bug qui entraßne une limitation et des retards excessifs
.

Sur Omio toute l'infrastructure est gérée par Kubernetes. Toutes nos charges de travail avec et sans état s'exécutent exclusivement sur Kubernetes (nous utilisons Google Kubernetes Engine). Au cours des six derniers mois, nous avons commencé à observer des ralentissements aléatoires. Les applications se bloquent ou cessent de répondre aux contrÎles de santé, perdent la connexion au réseau, etc. Ce comportement nous a longtemps intrigués et nous avons finalement décidé de prendre le problÚme au sérieux.

Résumé de l'article:

  • Quelques mots sur les conteneurs et Kubernetes ;
  • Comment les demandes et les limites du processeur sont mises en Ɠuvre ;
  • Comment fonctionne la limite du processeur dans les environnements multicƓurs ;
  • Comment suivre la limitation du processeur ;
  • Solution du problĂšme et nuances.

Quelques mots sur les conteneurs et Kubernetes

Kubernetes est essentiellement la norme moderne dans le monde des infrastructures. Sa tĂąche principale est l'orchestration des conteneurs.

Containers

Dans le passĂ©, nous devions crĂ©er des artefacts tels que des JAR/WAR Java, des Python Eggs ou des exĂ©cutables Ă  exĂ©cuter sur des serveurs. Cependant, pour les faire fonctionner, un travail supplĂ©mentaire a dĂ» ĂȘtre effectuĂ© : installer l'environnement d'exĂ©cution (Java/Python), placer les fichiers nĂ©cessaires aux bons endroits, s'assurer de la compatibilitĂ© avec une version spĂ©cifique du systĂšme d'exploitation, etc. En d’autres termes, il fallait prĂȘter une attention particuliĂšre Ă  la gestion de la configuration (qui Ă©tait souvent source de conflits entre dĂ©veloppeurs et administrateurs systĂšme).

Les conteneurs ont tout changĂ©. L'artefact est dĂ©sormais une image conteneur. Il peut ĂȘtre reprĂ©sentĂ© comme une sorte de fichier exĂ©cutable Ă©tendu contenant non seulement le programme, mais Ă©galement un environnement d'exĂ©cution Ă  part entiĂšre (Java/Python/...), ainsi que les fichiers/packages nĂ©cessaires, prĂ©installĂ©s et prĂȘts Ă  ĂȘtre utilisĂ©s. courir. Les conteneurs peuvent ĂȘtre dĂ©ployĂ©s et exĂ©cutĂ©s sur diffĂ©rents serveurs sans aucune Ă©tape supplĂ©mentaire.

De plus, les conteneurs s'exĂ©cutent dans leur propre environnement isolĂ©. Ils disposent de leur propre carte rĂ©seau virtuelle, de leur propre systĂšme de fichiers Ă  accĂšs restreint, de leur propre hiĂ©rarchie de processus, de leurs propres limites de processeur et de mĂ©moire, etc. Tout cela est implĂ©mentĂ© grĂące Ă  un sous-systĂšme du noyau dĂ©diĂ©. Linux — espaces de noms.

Kubernetes

Comme indiqué précédemment, Kubernetes est un orchestrateur de conteneurs. Cela fonctionne comme ceci : vous lui donnez un pool de machines, puis dites : « Hé, Kubernetes, lançons dix instances de mon conteneur avec 2 processeurs et 3 Go de mémoire chacune, et faisons-les fonctionner ! Kubernetes s'occupera du reste. Il va trouver de la capacité libre, lancer des conteneurs et les redémarrer si nécessaire, déployer une mise à jour lors d'un changement de version, etc. Essentiellement, Kubernetes vous permet d'abstraire le composant matériel et de créer une grande variété de systÚmes adaptés au déploiement et à l'exécution d'applications.

Limites du processeur et limitation agressive dans Kubernetes
Kubernetes du point de vue du profane

Que sont les requĂȘtes et les limites dans Kubernetes

D'accord, nous avons couvert les conteneurs et Kubernetes. Nous savons Ă©galement que plusieurs conteneurs peuvent rĂ©sider sur la mĂȘme machine.

Une analogie peut ĂȘtre faite avec un appartement communal. Un local spacieux (machines/unitĂ©s) est pris et louĂ© Ă  plusieurs locataires (conteneurs). Kubernetes agit en tant qu'agent immobilier. La question se pose, comment Ă©viter les conflits entre locataires ? Et si l’un d’eux, par exemple, dĂ©cide d’emprunter la salle de bain pour une demi-journĂ©e ?

C’est lĂ  qu’interviennent les demandes et les limites. CPU Demander nĂ©cessaire uniquement Ă  des fins de planification. C'est quelque chose comme une « liste de souhaits » du conteneur, et elle est utilisĂ©e pour sĂ©lectionner le nƓud le plus appropriĂ©. En mĂȘme temps le CPU Limiter peut ĂȘtre comparĂ© Ă  un contrat de location - dĂšs que nous sĂ©lectionnons une unitĂ© pour le conteneur, le ne sera pas capable dĂ©passer les limites Ă©tablies. Et c'est lĂ  que le problĂšme se pose...

Comment les requĂȘtes et les limites sont implĂ©mentĂ©es dans Kubernetes

Kubernetes utilise un mécanisme de limitation (saut de cycles d'horloge) intégré au noyau pour implémenter les limites du processeur. Si une application dépasse la limite, la limitation est activée (c'est-à-dire qu'elle reçoit moins de cycles CPU). Les demandes et les limites de mémoire sont organisées différemment, elles sont donc plus faciles à détecter. Pour cela, il suffit de vérifier le dernier statut de redémarrage du pod : s'il est « OOMKilled ». La limitation du processeur n'est pas si simple, puisque K8 ne rend les métriques disponibles que par utilisation, et non par groupes de contrÎle.

Demande de processeur

Limites du processeur et limitation agressive dans Kubernetes
Comment la requĂȘte CPU est implĂ©mentĂ©e

Pour plus de simplicitĂ©, examinons le processus en utilisant comme exemple une machine dotĂ©e d'un processeur Ă  4 cƓurs.

K8s utilise un mécanisme de groupe de contrÎle (cgroups) pour contrÎler l'allocation des ressources (mémoire et processeur). Un modÚle hiérarchique lui est proposé : l'enfant hérite des limites du groupe parent. Les détails de la distribution sont stockés dans un systÚme de fichiers virtuel (/sys/fs/cgroup). Dans le cas d'un processeur, c'est /sys/fs/cgroup/cpu,cpuacct/*.

K8s utilise un fichier cpu.share pour allouer les ressources du processeur. Dans notre cas, le groupe de contrĂŽle racine obtient 4096 100 parts de ressources CPU, soit 1 % de la puissance du processeur disponible (1024 cƓur = XNUMX XNUMX ; il s'agit d'une valeur fixe). Le groupe racine rĂ©partit les ressources proportionnellement en fonction des parts des descendants inscrits dans cpu.share, et eux, Ă  leur tour, font de mĂȘme avec leurs descendants, etc. Sur un nƓud Kubernetes typique, le groupe de contrĂŽle racine a trois enfants : system.slice, user.slice Đž kubepods. Les deux premiers sous-groupes sont utilisĂ©s pour rĂ©partir les ressources entre les charges systĂšme critiques et les programmes utilisateur en dehors des K8. Le dernier - kubepods — créé par Kubernetes pour distribuer les ressources entre les pods.

Le diagramme ci-dessus montre que le premier et le deuxiĂšme sous-groupes ont reçu chacun 1024 partages, avec le sous-groupe kuberpod attribuĂ© 4096 actions Comment est-ce possible : aprĂšs tout, le groupe racine n'a accĂšs qu'Ă  4096 actions, et la somme des actions de ses descendants dĂ©passe largement ce nombre (6144L'important est que la valeur ait une signification logique, donc le planificateur Linux (CFS) l'utilise pour rĂ©partir proportionnellement les ressources du processeur. Dans notre cas, les deux premiers groupes reçoivent 680 actions rĂ©elles (16,6% de 4096), et kubepod reçoit le reste 2736 actions En cas d'indisponibilitĂ©, les deux premiers groupes n'utiliseront pas les ressources allouĂ©es.

Heureusement, le planificateur dispose d'un mécanisme pour éviter de gaspiller les ressources CPU inutilisées. Il transfÚre la capacité « inactive » vers un pool global, à partir duquel elle est distribuée aux groupes ayant besoin de puissance de processeur supplémentaire (le transfert s'effectue par lots pour éviter les pertes d'arrondi). Une méthode similaire est appliquée à tous les descendants des descendants.

Ce mécanisme garantit une répartition équitable de la puissance du processeur et garantit qu'aucun processus ne « vole » les ressources des autres.

Limite du processeur

MalgrĂ© le fait que les configurations de limites et de requĂȘtes dans les K8 se ressemblent, leur mise en Ɠuvre est radicalement diffĂ©rente : le plus trompeur et la partie la moins documentĂ©e.

Les K8 s'engagent MĂ©canisme de quotas du CSA pour mettre en Ɠuvre des limites. Leurs paramĂštres sont spĂ©cifiĂ©s dans des fichiers cfs_period_us Đž cfs_quota_us dans le rĂ©pertoire cgroup (le fichier s'y trouve Ă©galement cpu.share).

Contrairement Ă  cpu.share, le quota est basĂ© sur pĂ©riode de temps, et non sur la puissance disponible du processeur. cfs_period_us spĂ©cifie la durĂ©e de la pĂ©riode (Ă©poque) - elle est toujours de 100000 100 ÎŒs (8 ms). Il existe une option pour modifier cette valeur dans KXNUMX, mais elle n'est disponible qu'en alpha pour le moment. Le planificateur utilise l'Ă©poque pour redĂ©marrer les quotas utilisĂ©s. DeuxiĂšme fichier cfs_quota_us, spĂ©cifie le temps disponible (quota) Ă  chaque Ă©poque. Notez qu'il est Ă©galement spĂ©cifiĂ© en microsecondes. Le quota peut dĂ©passer la durĂ©e de l'Ă©poque ; autrement dit, elle peut ĂȘtre supĂ©rieure Ă  100 ms.

Examinons deux scĂ©narios sur des machines Ă  16 cƓurs (le type d'ordinateur le plus courant dont nous disposons chez Omio) :

Limites du processeur et limitation agressive dans Kubernetes
ScĂ©nario 1 : 2 threads et une limite de 200 ms. Pas de limitation

Limites du processeur et limitation agressive dans Kubernetes
ScĂ©nario 2 : 10 threads et limite de 200 ms. La limitation commence aprĂšs 20 ms, l'accĂšs aux ressources du processeur reprend aprĂšs 80 ms supplĂ©mentaires

Disons que vous définissez la limite du processeur sur 2 graines; Kubernetes traduira cette valeur en 200 ms. Cela signifie que le conteneur peut utiliser un maximum de 200 ms de temps CPU sans limitation.

Et c'est lĂ  que le plaisir commence. Comme mentionnĂ© ci-dessus, le quota disponible est de 200 ms. Si vous travaillez en parallĂšle dix threads sur une machine Ă  12 cƓurs (voir illustration du scĂ©nario 2), alors que tous les autres pods sont inactifs, le quota sera Ă©puisĂ© en seulement 20 ms (puisque 10 * 20 ms = 200 ms), et tous les threads de ce pod se bloqueront » (Manette de Gaz) pour les 80 prochaines ms. Le dĂ©jĂ  mentionnĂ© bug du planificateur, Ă  cause de quoi une limitation excessive se produit et le conteneur ne peut mĂȘme pas remplir le quota existant.

Comment Ă©valuer la limitation dans les pods ?

Connectez-vous simplement au pod et exécutez cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — le nombre total de pĂ©riodes de planification ;
  • nr_throttled — nombre de pĂ©riodes limitĂ©es dans la composition nr_periods;
  • throttled_time — temps de limitation cumulĂ© en nanosecondes.

Limites du processeur et limitation agressive dans Kubernetes

Que se passe-t-il réellement ?

En conséquence, nous obtenons une limitation élevée dans toutes les applications. Parfois, il est dans une fois et demie plus fort que prévu !

Cela entraĂźne diverses erreurs : Ă©checs du contrĂŽle de prĂ©paration, gels des conteneurs, ruptures de connexion rĂ©seau, dĂ©lais d'attente lors des appels de service. Cela se traduit finalement par une latence accrue et des taux d’erreur plus Ă©levĂ©s.

Décision et conséquences

Tout est simple ici. Nous avons abandonné les limites du processeur et commencé à mettre à jour le noyau du systÚme d'exploitation en cluster vers la derniÚre version, dans laquelle le bug a été corrigé. Le nombre d'erreurs (HTTP 5xx) dans nos services a immédiatement diminué de maniÚre significative :

Erreurs HTTP 5xx

Limites du processeur et limitation agressive dans Kubernetes
Erreurs HTTP 5xx pour un service critique

Temps de réponse p95

Limites du processeur et limitation agressive dans Kubernetes
Latence des demandes de service critiques, 95e percentile

Les coûts d'exploitation

Limites du processeur et limitation agressive dans Kubernetes
Nombre d'heures d'instance passées

Quel est le piĂšge?

Comme indiqué au début de l'article :

Une analogie peut ĂȘtre faite avec un appartement communal... Kubernetes agit en tant qu'agent immobilier. Mais comment Ă©viter les conflits entre locataires ? Et si l’un d’eux, par exemple, dĂ©cide d’emprunter la salle de bain pour une demi-journĂ©e ?

Voici le piÚge. Un conteneur imprudent peut consommer toutes les ressources CPU disponibles sur une machine. Si vous disposez d'une pile d'applications intelligente (par exemple, JVM, Go, Node VM sont correctement configurés), alors ce n'est pas un problÚme : vous pouvez travailler longtemps dans de telles conditions. Mais si les applications sont mal optimisées ou pas optimisées du tout (FROM java:latest), la situation pourrait devenir incontrÎlable. Chez Omio, nous avons des Dockerfiles de base automatisés avec des paramÚtres par défaut adéquats pour la principale pile de langues, ce problÚme n'existait donc pas.

Nous vous recommandons de surveiller les métriques UTILISATION (utilisation, saturation et erreurs), délais API et taux d'erreur. S'assurer que les résultats répondent aux attentes.

références

C'est notre histoire. Les documents suivants ont grandement aidĂ© Ă  comprendre ce qui se passait :

Rapports de bogues Kubernetes :

Avez-vous rencontrĂ© des problĂšmes similaires dans votre pratique ou avez-vous une expĂ©rience liĂ©e Ă  la limitation dans des environnements de production conteneurisĂ©s ? Partagez votre histoire dans les commentaires !

PS du traducteur

A lire aussi sur notre blog :

Source: habr.com

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