Surveillance des performances Kubernetes : outils et techniques d'optimisation
Surveillez les performances Kubernetes avec des métriques utiles, Prometheus, Grafana, kubectl et des habitudes pratiques d'optimisation des ressources.
Surveillance des performances Kubernetes : outils et techniques d'optimisation
La surveillance des performances Kubernetes ne se limite pas à observer les graphiques CPU. Un cluster peut afficher une utilisation CPU moyenne faible alors que les utilisateurs subissent des requêtes lentes. Un pod peut disposer de suffisamment de mémoire la plupart du temps et être tout de même tué lors d'un traitement par lots. Un nœud peut sembler sain jusqu'à ce que la pression disque commence à évincer des pods. Une bonne surveillance relie les signaux du cluster à l'expérience qui importe vraiment : le service est-il rapide, disponible et prévisible ?
La première erreur est de commencer par les outils plutôt que par les questions. Prometheus, Grafana, metrics-server, kube-state-metrics et les plateformes de surveillance cloud sont tous utiles, mais ils ne décident pas de ce qui compte. C'est vous qui décidez en comprenant la charge de travail. Une API publique se soucie de la latence et des erreurs. Un worker de file d'attente se soucie du backlog et du taux de traitement. Un traitement nocturne se soucie du temps d'exécution et des pods en échec. Une charge de travail de type base de données se soucie de la latence disque et de la pression mémoire.
Pour un aperçu rapide, kubectl top reste utile :
kubectl top nodes
kubectl top pods -A
kubectl top pod -n production api-7d9c8f7b9d-2x4mq --containers
Ces commandes dépendent de metrics-server. Elles donnent une utilisation récente du CPU et de la mémoire, pas un historique complet. Utilisez-les pendant le triage, pas comme seul système de surveillance. Si un pod a redémarré il y a dix minutes à cause d'un manque de mémoire, kubectl top peut ne pas montrer le pic qui en est la cause.
Prometheus est le fondement courant des métriques Kubernetes car il récupère des données de séries temporelles et fonctionne bien avec la découverte de services Kubernetes. Dans une configuration typique, les métriques proviennent de plusieurs endroits. Le kubelet expose les métriques de ressources des conteneurs et des pods. cAdvisor, intégré au kubelet, contribue aux données CPU, mémoire, système de fichiers et réseau des conteneurs. node-exporter rapporte les métriques au niveau de l'hôte. kube-state-metrics transforme l'état des objets Kubernetes en métriques : réplicas souhaités, réplicas disponibles, phases des pods, conditions des nœuds, etc.
Grafana transforme ensuite ces métriques en tableaux de bord. Un bon tableau de bord n'est pas un mur de jauges. Il doit répondre rapidement à des questions spécifiques : quel service est lent, quels pods sont limités, quels nœuds sont sous pression, quel Déploiement échoue à se déployer, et si l'autoscaling suit le rythme.
Commencez par la couche applicative. Pour les services orientés utilisateur, les signaux les plus importants sont le taux de requêtes, le taux d'erreur et la latence. Si vous avez des SLO, représentez-les graphiquement. Un graphique CPU d'un pod ne vous dit pas si le paiement échoue. Les métriques applicatives, si. Instrumentez les services avec les bibliothèques client Prometheus, OpenTelemetry ou le système de surveillance que votre plateforme utilise déjà. Les métriques Kubernetes expliquent pourquoi le service est malsain ; les métriques applicatives vous disent qu'il est malsain.
Ensuite, reliez les symptômes applicatifs aux ressources des pods. L'utilisation du CPU est facile à mal interpréter dans Kubernetes. Un conteneur avec une limite CPU peut être limité même lorsque l'utilisation CPU moyenne ne semble pas dramatique. La limitation se produit lorsque le conteneur tente d'utiliser plus de temps CPU que sa limite ne le permet dans la période de planification. Pour les applications sensibles à la latence, cela peut provoquer des requêtes lentes qui semblent aléatoires.
Une requête PromQL utile pour la limitation est :
rate(container_cpu_cfs_throttled_periods_total{namespace="production", container!=""}[5m])
Une valeur croissante signifie que le conteneur est limité. Associez-la à l'utilisation CPU et à la latence des requêtes. Si les pics de latence coïncident avec la limitation, envisagez d'augmenter ou de supprimer la limite CPU, d'augmenter les réplicas ou d'optimiser le chemin de code. Certaines équipes définissent des demandes CPU mais évitent les limites CPU pour les services sensibles à la latence, s'appuyant plutôt sur les demandes, l'autoscaling et les contrôles de capacité des nœuds. Cela peut être raisonnable, mais nécessite une discipline au niveau du cluster pour que les charges de travail bruyantes n'affament pas les autres.
La mémoire se comporte différemment. Le CPU peut être limité ; la mémoire ne peut pas être ralentie de la même manière. Si un conteneur dépasse sa limite mémoire, il peut être OOMKilled. Recherchez les raisons des redémarrages :
kubectl describe pod -n production api-7d9c8f7b9d-2x4mq
kubectl get pod -n production api-7d9c8f7b9d-2x4mq -o jsonpath='{.status.containerStatuses[*].lastState}'
Dans Prometheus, surveillez la mémoire de travail et comparez-la aux limites :
container_memory_working_set_bytes{namespace="production", container!=""}
N'ajustez pas la mémoire à partir d'une seule heure calme. Regardez le trafic de pointe, les fenêtres de traitement par lots, les déploiements et le comportement du ramasse-miettes. Les services Java, Go, Node.js et Python ont des profils mémoire différents. Une limite qui semble généreuse en trafic normal peut être trop serrée au démarrage, lors du réchauffement du cache ou d'une requête importante.
Les demandes de ressources sont importantes car le planificateur les utilise pour placer les pods. Si les demandes sont trop faibles, Kubernetes peut entasser trop de pods occupés sur le même nœud. Tout semble efficace jusqu'à ce que ces pods deviennent occupés en même temps. Si les demandes sont trop élevées, le cluster gaspille de la capacité et l'autoscaling peut ajouter des nœuds plus tôt que nécessaire. La meilleure demande est généralement basée sur l'utilisation observée plus une marge, pas une valeur copiée d'un autre service.
L'autoscaler vertical de pods peut aider en recommandant des demandes à partir de l'historique d'utilisation. De nombreuses équipes exécutent d'abord VPA en mode recommandation car les mises à jour automatiques peuvent redémarrer les pods selon la configuration et le type de charge de travail. Traitez les recommandations comme une entrée, pas une loi. Un service avec des pics rares mais importants peut nécessiter plus de marge que son historique moyen ne le suggère.
L'autoscaler horizontal de pods est utile lorsque plus de réplicas améliorent réellement le débit. Il fonctionne bien pour les services web sans état et les workers qui peuvent partager la charge. Il ne résout pas un goulot d'étranglement monothread, un verrou de base de données ou une dépendance aval déjà saturée.
Un HPA de base pourrait ressembler à ceci :
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Surveillez le comportement du HPA, pas seulement le nombre de réplicas. S'il monte et descend constamment, ajustez les fenêtres de stabilisation, les cibles ou la métrique. S'il atteint maxReplicas et que la latence est toujours mauvaise, le problème peut être la capacité, le code ou une dépendance. S'il ne monte jamais en charge alors que les pods sont clairement surchargés, vérifiez la disponibilité des métriques et si les demandes sont définies. Les cibles d'utilisation CPU dépendent des demandes CPU ; des demandes manquantes ou irréalistes peuvent rendre l'autoscaling trompeur.
La santé des nœuds est la couche suivante. Un problème de pod qui apparaît sur plusieurs services d'un même nœud est généralement un problème de nœud. Surveillez la saturation CPU, la charge moyenne, la mémoire disponible, la pression disque, l'utilisation des inodes, la latence du système de fichiers, les erreurs réseau et la santé du kubelet. Les conditions de nœud telles que MemoryPressure, DiskPressure et PIDPressure doivent être visibles dans les tableaux de bord et les alertes.
Utilisez kubectl describe node lorsqu'un nœud semble suspect :
kubectl describe node worker-12
Regardez les conditions, les ressources allouées, les événements et les pods planifiés sur le nœud. Un nœud peut être sur-engagé par les limites, les demandes ou l'utilisation réelle. La section des ressources allouées vous aide à voir si les hypothèses de planification correspondent à la réalité.
La surveillance du plan de contrôle est importante même si vos pods applicatifs semblent sains. La latence du serveur API peut ralentir les déploiements, l'autoscaling et les contrôleurs. La latence ou les problèmes disque d'etcd peuvent rendre tout le cluster lent. Les problèmes du gestionnaire de contrôleurs et du planificateur peuvent retarder le placement des pods ou la réconciliation. Dans Kubernetes géré, vous ne voyez peut-être pas tous les composants du plan de contrôle, mais les fournisseurs cloud exposent généralement certaines métriques de santé et de latence API.
Les événements sont utiles lors des incidents, mais ils ne sont pas un stockage de métriques à long terme. Néanmoins, ils expliquent souvent ce qui vient de se passer :
kubectl get events -A --sort-by=.lastTimestamp
Recherchez les échecs de planification, les erreurs de pull d'image, les échecs de sonde, les évictions et les messages de back-off. Les événements peuvent être bruyants, alors filtrez par espace de noms ou objet concerné si nécessaire.
Les sondes méritent une surveillance attentive. Des sondes de vivacité trop agressives peuvent redémarrer une application lente mais en cours de récupération et aggraver un incident. Des sondes de préparation qui échouent correctement peuvent protéger les utilisateurs en retirant les pods défaillants du service. Suivez les échecs de sonde et corrélez-les avec la limitation CPU, les pauses GC, les timeouts aval et les déploiements.
Pour les charges de travail gourmandes en stockage, le CPU et la mémoire des conteneurs ne suffisent pas. Surveillez la latence des volumes persistants, le débit disque, la profondeur de file d'attente et le remplissage du système de fichiers. Un pod en attente sur un stockage lent peut afficher un faible CPU car il est bloqué. Si une base de données ou une file d'attente s'exécute sur Kubernetes, les métriques de stockage font partie des performances applicatives, pas des détails d'infrastructure.
Un chemin de dépannage pratique commence large et se rétrécit. D'abord, confirmez le symptôme orienté utilisateur : latence, erreurs, traitements échoués ou backlog. Ensuite, identifiez le périmètre : un pod, un Déploiement, un nœud, un espace de noms ou tout le cluster. Troisièmement, vérifiez les changements récents : déploiements, mises à jour de configuration, activité de l'autoscaler, rotations de nœuds ou pics de trafic. Quatrièmement, inspectez le comportement des ressources des pods : limitation CPU, pression mémoire, redémarrages et échecs de sonde. Cinquièmement, inspectez la santé des nœuds et des dépendances.
L'alerte doit éviter de réveiller les gens pour du bruit inoffensif. Alertez d'abord sur l'impact utilisateur : taux d'erreur élevé, latence élevée, délai de traitement manqué, âge de la file d'attente croissant. Alertez ensuite sur les indicateurs avancés forts : OOMKills fréquents, limitation CPU soutenue sur les services sensibles à la latence, pods indisponibles en dessous des réplicas souhaités, pression sur les nœuds, pods en attente persistants et HPA bloqué au max de réplicas alors que les métriques de service sont mauvaises.
L'objectif n'est pas une utilisation parfaite. Un cluster fonctionnant à 95 % d'utilisation des ressources toute la journée peut sembler efficace jusqu'à ce qu'un nœud tombe en panne et qu'il n'y ait plus de place pour replanifier les pods. Laissez de la capacité pour les déploiements, les nouvelles tentatives, les pics de trafic et les pannes. L'optimisation doit réduire le gaspillage sans supprimer la marge qui maintient les incidents de petite taille.
Une bonne surveillance des performances Kubernetes semble pratique. Vous pouvez ouvrir un tableau de bord et voir la santé du service, la santé des pods, la santé des nœuds et le comportement de mise à l'échelle sans chercher dans vingt onglets. Vous pouvez répondre si un ralentissement est dû au code, aux limites de ressources, à la pression sur les nœuds, au stockage, au réseau ou au plan de contrôle. Et lorsque vous modifiez les demandes, les limites ou l'autoscaling, vous pouvez voir si le changement a aidé au lieu de deviner.
Les vues au niveau de l'espace de noms sont utiles lorsque plusieurs équipes partagent un cluster. Une seule équipe peut ne pas voir la saturation au niveau du nœud arriver si elle ne surveille que ses propres Déploiements. Les équipes de plateforme devraient exposer des tableaux de bord montrant les demandes CPU et mémoire de l'espace de noms, l'utilisation réelle, les nombres de pods, les redémarrages et la limitation. Cela rend les conversations sur la capacité moins émotionnelles. Au lieu de dire qu'une équipe utilise "trop", vous pouvez montrer les tendances des demandes, l'utilisation de pointe et le gaspillage.
L'optimisation des coûts devrait venir après les signaux de fiabilité, pas avant. Si un service n'a jamais eu de demandes ajustées, vous pouvez trouver des économies faciles. Mais réduire les demandes de manière agressive peut créer une pression de planification et des problèmes de voisin bruyant. Un bon processus modifie une classe de charge de travail à la fois, surveille la latence et les redémarrages, et laisse des notes de retour arrière. Traitez l'ajustement des ressources comme du code de production : petits changements, résultats mesurés.
Les déploiements eux-mêmes peuvent créer des incidents de performance. Un déploiement qui remplace trop de pods à la fois peut surcharger les caches froids, les pools de connexions ou les services aval. Surveillez la durée du déploiement, les réplicas indisponibles et la latence applicative pendant les déploiements. Ajustez maxSurge et maxUnavailable en fonction du comportement du service au démarrage. Un service avec un démarrage lent peut nécessiter un déploiement conservateur même si les performances en régime permanent sont bonnes.
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
Ce paramètre n'est pas universellement le meilleur, mais il montre le compromis : déploiement plus lent, plus de protection contre les baisses de capacité. Pour un service sans état qui démarre instantanément, vous pouvez choisir un déploiement plus rapide. Pour un service JVM réchauffant les caches et ouvrant de nombreuses connexions aval, plus lent peut être plus sûr.
Gardez un œil sur la cardinalité dans les métriques. Les étiquettes Kubernetes sont tentantes, mais les étiquettes à haute cardinalité comme l'UID du pod, l'ID de requête ou l'ID utilisateur peuvent rendre Prometheus coûteux et lent. Utilisez des étiquettes qui vous aident à agréger : espace de noms, charge de travail, pod, conteneur, nœud, code de statut, modèle de route. Évitez les étiquettes qui créent une nouvelle série temporelle pour chaque utilisateur ou chaque requête. La surveillance ne devrait pas devenir ce qui nuit aux performances du cluster.
Les logs et les traces complètent le tableau. Les métriques vous disent que la latence a augmenté ; les traces peuvent montrer quel appel aval est devenu lent ; les logs peuvent montrer l'erreur ou le timeout exact. OpenTelemetry est couramment utilisé pour connecter ces signaux, mais l'outil importe moins que la corrélation. Utilisez des noms de service, des espaces de noms, des versions et des ID de trace cohérents afin de pouvoir passer d'une alerte aux logs pertinents sans deviner.
Pour les systèmes de traitement par lots et workers, surveillez l'âge du backlog plutôt que seulement le CPU du pod. Un worker de file d'attente peut être sain au niveau du pod tout en prenant du retard parce que le travail entrant dépasse la capacité de traitement. Les métriques telles que l'âge du message le plus ancien, les traitements terminés par minute, les nouvelles tentatives et les comptes de lettres mortes importent souvent plus que l'utilisation du conteneur. Le HPA peut évoluer à partir de métriques personnalisées ou externes lorsque le CPU est le mauvais signal.
Révisez les tableaux de bord après les incidents. Si les intervenants ont dû exécuter cinq commandes manuelles pour répondre à la même question, cette question mérite d'être sur un tableau de bord ou dans un runbook. La surveillance s'améliore par l'usage. L'objectif n'est pas de prédire chaque panne ; c'est de rendre la prochaine investigation plus courte et moins dépendante de la mémoire d'une seule personne.