Résolution des goulots d'étranglement de performance courants dans Kubernetes

Apprenez à diagnostiquer et résoudre systématiquement les goulots d'étranglement de performance courants dans Kubernetes, notamment le throttling CPU, les OOMKills mémoire et les retards de planification. Ce guide fournit des commandes actionnables et des bonnes pratiques pour ajuster les demandes de ressources, optimiser la mise à l'échelle HPA et identifier les contraintes sous-jacentes du cluster afin de garantir des performances applicatives optimales.

Résolution des goulots d'étranglement de performance courants dans Kubernetes

Les problèmes de performance dans Kubernetes se manifestent rarement par un message explicite « Kubernetes est lent ». Vous observez un déploiement qui bloque, une API qui renvoie soudainement des erreurs 5xx, une file d'attente qui cesse de se vider, ou des pods qui semblent sains alors que les utilisateurs se plaignent de latence. Le cluster n'est qu'une partie de l'histoire, mais c'est la partie que vous pouvez inspecter avec un ensemble cohérent de commandes.

L'astuce consiste à éviter de se précipiter sur la taille des nœuds ou le nombre de réplicas. Commencez par déterminer le type de goulot d'étranglement : throttling CPU, pression mémoire, retard de planification, mise à l'échelle lente, latence réseau, latence de stockage, ou une application qui effectue simplement plus de travail que prévu.

Phase 1 : Identification des symptômes

Avant de plonger dans des composants spécifiques, définissez clairement la dégradation de performance observée. Les symptômes courants se répartissent souvent dans l'une de ces catégories :

  • Déploiements/Mises à jour lents : La création de pods prend un temps excessif, ou les mises à jour progressives stagnent.
  • Applications non réactives : Les pods sont en cours d'exécution mais ne répondent pas au trafic applicatif (ex. : latence élevée, erreurs 5xx).
  • Pics de ressources élevés : Des pics inexpliqués d'utilisation du CPU ou de la mémoire sur les nœuds ou des déploiements spécifiques.
  • Retards de planification : Les nouveaux pods restent indéfiniment à l'état Pending.

Phase 2 : Diagnostic des contraintes de ressources (CPU et mémoire)

Une mauvaise gestion des ressources est la cause la plus fréquente des problèmes de performance dans Kubernetes. Des demandes et limites mal définies entraînent un throttling ou des OOMKills.

1. Vérification de l'utilisation et des limites des ressources

Commencez par inspecter les allocations de ressources de l'application concernée à l'aide de kubectl describe et kubectl top.

Vérification actionnable : Comparez les requests et limits avec l'utilisation réelle rapportée par les serveurs de métriques.

# Obtenir l'utilisation des ressources pour tous les pods d'un namespace
kubectl top pods -n <namespace>

# Examiner les demandes/limites de ressources pour un pod spécifique
kubectl describe pod <pod-name> -n <namespace>

Inspectez également la charge de travail propriétaire pour comprendre si le problème affecte un seul pod ou l'ensemble du déploiement :

kubectl get deploy <deployment-name> -n <namespace> -o yaml
kubectl get pods -n <namespace> -l app=<label> -o wide

Si un seul pod est lent et se trouve sur un nœud différent des autres, la pression au niveau du nœud est plus probable. Si chaque réplica est lent, les paramètres de ressources, les dépendances en aval ou le comportement de l'application méritent plus d'attention.

2. Throttling CPU

Si l'utilisation du CPU d'un conteneur atteint à plusieurs reprises sa limite définie, le noyau le limitera, entraînant des pics de latence sévères même si le nœud lui-même a de la capacité disponible. Ceci est souvent confondu avec une privation générale de CPU.

Conseil de diagnostic : Recherchez des réponses à latence élevée, même lorsque kubectl top n'affiche pas une utilisation CPU à 100 % sur le nœud. Le throttling se produit par conteneur.

Pour une confirmation plus approfondie, utilisez votre système de métriques s'il expose les métriques de throttling CPU des conteneurs. Dans les configurations basées sur Prometheus, les équipes surveillent souvent des métriques telles que les périodes CPU limitées en parallèle de la latence des requêtes. L'utilisation brute du CPU seule peut masquer le throttling car un conteneur peut être limité avant même d'utiliser un cœur complet du nœud.

Résolution :

  • Augmentez la limit CPU si la charge de travail nécessite légitimement plus de puissance de traitement.
  • Si l'application est en attente active, optimisez le code de l'application plutôt que d'augmenter simplement les limites.
  • Envisagez de supprimer les limites CPU pour certains services sensibles à la latence tout en conservant les demandes CPU, si cela correspond à votre politique de plateforme. Cela évite un throttling strict tout en fournissant des informations de placement utiles au planificateur.

3. Pression mémoire et OOMKills

Si un conteneur dépasse sa limite de mémoire, Kubernetes initie un kill Out-Of-Memory (OOM), redémarrant le conteneur à plusieurs reprises.

Diagnostic : Vérifiez l'état du pod pour les redémarrages fréquents (consultez la colonne RESTARTS dans kubectl get pods) et examinez les logs pour les événements OOMKilled.

# Vérifier les événements récents pour les OOMKills
kubectl get events --field-selector involvedObject.name=<pod-name> -n <namespace>

Résolution :

  • Si les OOMKills sont fréquents, augmentez immédiatement la limit mémoire.
  • Pour des correctifs à long terme, profilez l'application pour trouver et corriger les fuites mémoire ou réduire la taille du tas.

La mémoire se comporte différemment du CPU. Le CPU peut être limité et le processus continue de fonctionner lentement. Les dépassements de limite mémoire se terminent généralement par la mort du processus. Cela fait ressembler les problèmes de mémoire à des incidents de fiabilité : redémarrages, connexions interrompues, caches froids et requêtes en vol échouées.

Meilleure pratique : Définissez judicieusement les demandes. Assurez-vous que les requests de ressources sont définies raisonnablement proches de l'utilisation minimale attendue. Si les requests sont trop faibles, le planificateur pourrait sur-réserver le nœud, entraînant une contention lorsque tous les pods atteignent leurs demandes simultanément.

Phase 3 : Enquête sur les goulots d'étranglement de planification

Lorsque les pods restent à l'état Pending, le problème réside dans l'incapacité du planificateur à trouver un nœud approprié.

1. Analyse des pods en attente

Utilisez kubectl describe pod sur un pod en attente pour lire la section Events. Cette section contient généralement une explication claire de l'échec de la planification.

Messages courants du planificateur :

  • 0/3 nodes are available: 3 Insufficient cpu. (Problème de capacité du nœud)
  • 0/3 nodes are available: 3 node(s) had taint {dedicated: infra}, that the pod didn't tolerate. (Incompatibilité de Taints/Tolerations)
  • 0/3 nodes are available: 1 node(s) had taint {NoSchedule: true}, that the pod didn't tolerate. (Pression du nœud ou maintenance)

2. Saturation des ressources du cluster

Si la planification est retardée en raison d'un manque de CPU/Mémoire, le cluster manque de capacité agrégée suffisante.

Résolution :

  • Ajoutez plus de nœuds au cluster.
  • Vérifiez que l'utilisation des nœuds n'est pas artificiellement élevée en raison de demandes de ressources mal configurées (voir Phase 2).
  • Utilisez Cluster Autoscaler (CA) si vous travaillez sur des fournisseurs cloud pour ajouter dynamiquement des nœuds lorsque des pods en attente s'accumulent.

Si Cluster Autoscaler est activé mais que les nœuds ne sont pas ajoutés, lisez ses logs avant de supposer que le fournisseur cloud est défaillant. L'autoscaler peut refuser d'ajouter des nœuds parce que les groupes de nœuds ont atteint leur taille maximale, que le pod en attente a des contraintes qu'aucun groupe de nœuds ne peut satisfaire, ou que des quotas empêchent de nouvelles instances.

Phase 4 : Problèmes de performance dans les mécanismes de mise à l'échelle

La mise à l'échelle automatisée doit réagir rapidement, mais des erreurs de configuration dans les Horizontal Pod Autoscalers (HPA) ou les Vertical Pod Autoscalers (VPA) peuvent causer des problèmes.

1. Latence du Horizontal Pod Autoscaler (HPA)

HPA dépend du Metrics Server pour rapporter une utilisation précise du CPU/Mémoire ou des métriques personnalisées.

Étapes de diagnostic :

  1. Vérifier la santé du Metrics Server : Assurez-vous que le Metrics Server est en cours d'exécution et accessible.
    kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
    
  2. Vérifier l'état du HPA : Inspectez la configuration HPA et les événements récents.
    kubectl describe hpa <hpa-name> -n <namespace>
    
    Recherchez des messages indiquant si la source de métriques est indisponible ou si la boucle de décision de mise à l'échelle fonctionne.

Goulots d'étranglement : Si des métriques personnalisées sont utilisées, assurez-vous que le fournisseur de métriques externe fonctionne correctement et rapporte des données assez souvent pour que le HPA puisse prendre des décisions utiles.

HPA est réactif. Il ne sait pas qu'un pic de trafic arrive à moins que votre métrique ne le reflète. Pour les charges de travail avec des pics soudains, vous pouvez avoir besoin de réplicas minimum plus élevés, de métriques personnalisées plus rapides, d'une mise à l'échelle basée sur les files d'attente, ou d'une pré-mise à l'échelle avant des événements connus.

2. Interactions du Vertical Pod Autoscaler (VPA)

Bien que VPA ajuste automatiquement les demandes de ressources, il peut provoquer une instabilité des performances pendant sa phase d'ajustement s'il redémarre ou redimensionne fréquemment les pods, en particulier pour les applications avec état qui ne tolèrent pas les redémarrages.

Recommandation : Utilisez VPA en mode Recommender d'abord, ou utilisez updateMode: "Off" pour seulement observer les recommandations sans application automatique, atténuant ainsi les perturbations de redimensionnement inutiles.

Phase 5 : Performance réseau et de stockage

Lorsque les ressources de calcul semblent correctes, le réseau ou le stockage persistant peut être le point d'étranglement.

1. Problèmes de CNI (Container Network Interface)

Si la communication entre les pods (en particulier entre les nœuds) est lente ou échoue par intermittence, le plugin CNI peut être surchargé ou mal configuré.

Dépannage :

  • Vérifiez les logs des pods du daemonset CNI (ex. : Calico, Flannel).
  • Testez la connectivité de base à l'aide de ping ou curl entre des pods sur différents nœuds.

2. Latence des volumes persistants (PV)

Les applications qui dépendent fortement des E/S disque (bases de données, systèmes de journalisation) souffriront si la latence du volume persistant sous-jacent est élevée.

Vérification actionnable : Confirmez le type de provisionneur (ex. : AWS EBS gp3 vs. io1) et vérifiez que le volume répond aux spécifications IOPS/débit requises.

Avertissement sur le stockage : N'exécutez jamais de bases de données à haut débit directement sur des volumes hostPath standard sans comprendre les caractéristiques de performance du disque sous-jacent. Utilisez des solutions de stockage cloud gérées ou des provisionneurs de stockage local haute performance pour les charges de travail exigeantes.

Goulots d'étranglement au niveau du nœud

Parfois, tous les pods d'un nœud deviennent plus lents en même temps. C'est votre signal pour arrêter de regarder un seul déploiement et inspecter le nœud.

kubectl describe node <node-name>
kubectl top node <node-name>
kubectl get pods --all-namespaces -o wide | grep <node-name>

Recherchez les conditions MemoryPressure, DiskPressure et PIDPressure. La pression disque est facile à négliger car le symptôme de l'application peut être un démarrage lent, des échecs de pull d'image ou des évictions plutôt qu'une erreur disque évidente.

Sur le nœud lui-même, si vous y avez accès, vérifiez :

df -h
iostat -x 1
free -h
journalctl -u kubelet --since "30 minutes ago"

Les services Kubernetes gérés peuvent limiter l'accès direct au nœud, mais la même idée s'applique : utilisez les métriques du fournisseur, les événements kubelet et les conditions du nœud pour décider si le nœud est le goulot d'étranglement partagé.

Pression du plan de contrôle et de l'API

La plupart des latences applicatives ne sont pas causées par le serveur API Kubernetes. Votre requête web n'appelle généralement pas le serveur API à chaque requête utilisateur. Mais la pression du plan de contrôle peut nuire aux performances opérationnelles : déploiements lents, planification retardée, mises à jour lentes des endpoints, ou contrôleurs qui prennent du retard.

Les symptômes incluent :

  • Les commandes kubectl sont lentes sur l'ensemble du cluster.
  • Les déploiements prennent plus de temps que d'habitude pour créer des pods.
  • Les contrôleurs sont en retard par rapport à l'état souhaité.
  • Les événements montrent des timeouts API répétés.

Vérifiez si le problème affecte le trafic applicatif normal ou les opérations du cluster. Si seuls les déploiements et la planification sont lents, examinez la santé du serveur API, le comportement du gestionnaire de contrôleurs, les webhooks d'admission et la santé d'etcd dans les clusters où vous gérez le plan de contrôle.

Les webhooks d'admission méritent une attention particulière. Un webhook lent ou indisponible peut retarder la création de pods même lorsque les nœuds ont une capacité suffisante. Si un déploiement bloque au moment de la création et que les événements mentionnent des appels de webhook, enquêtez sur le service de webhook avant de redimensionner les nœuds.

Un ordre pratique de dépannage

Commencez par le symptôme visible par l'utilisateur :

  • Requêtes HTTP lentes : comparez la latence de l'application, le throttling CPU, les redémarrages mémoire, la latence en aval et le chemin réseau.
  • Démarrage lent des pods : vérifiez le temps de pull d'image, les événements de planification, le temps d'attachement du volume et les conteneurs init.
  • Pods en attente : vérifiez les demandes, la capacité du nœud, les taints, l'affinité, les quotas et les limites de l'autoscaler.
  • Pics de latence périodiques : vérifiez le throttling CPU, le garbage collection, les voisins bruyants, la latence de stockage et le timing de mise à l'échelle HPA.
  • Redémarrages aléatoires : vérifiez OOMKilled, les sondes de vivacité, la pression du nœud et les logs d'application du conteneur précédent.

Ensuite, prouvez ou éliminez une couche à la fois. Par exemple, si les pics de latence coïncident exactement avec le throttling CPU, vous avez une piste solide. Si les pics de latence se produisent alors que le CPU, la mémoire, le réseau et le stockage semblent tous calmes, le goulot d'étranglement peut se trouver à l'intérieur de l'application ou d'un service en aval en dehors de Kubernetes.

Ajustement des demandes et limites sans conjectures

De mauvais paramètres de ressources créent de nombreux problèmes de performance :

  • Demandes trop faibles : le planificateur entasse trop de pods occupés sur le même nœud.
  • Demandes trop élevées : les pods restent en attente même si l'utilisation réelle est modeste.
  • Limites CPU trop faibles : les applications sensibles à la latence sont limitées.
  • Limites mémoire trop faibles : les conteneurs sont tués au lieu de ralentir.
  • Aucune demande : la planification devient moins prévisible et les charges de travail critiques peuvent mal rivaliser avec les voisins bruyants.

Utilisez les métriques de production récentes comme point de départ, puis laissez une marge pour les pics normaux. Pour les charges de travail Java, Node.js, Go, Python et de type base de données, le comportement mémoire peut être très différent, évitez donc de copier les limites d'un service à un autre simplement parce que la taille de l'image du conteneur semble similaire.

Prochaines étapes

Les meilleures enquêtes de performance Kubernetes sont ennuyeuses dans le bon sens : définissez le symptôme, vérifiez le pod, vérifiez le nœud, vérifiez la mise à l'échelle, puis vérifiez le réseau et le stockage. kubectl describe et kubectl top ne sont que le début, mais ils vous indiquent généralement quelle direction vaut la peine d'être suivie.

  1. Mettez en œuvre des Resource Quotas robustes pour empêcher les voisins bruyants d'affamer les applications critiques.
  2. Examinez régulièrement les compteurs de redémarrage des pods pour détecter rapidement les OOM subtils ou les comportements d'application défaillants.
  3. Utilisez des tableaux de bord Prometheus/Grafana qui suivent spécifiquement les métriques de throttling CPU, et pas seulement l'utilisation brute.