Introduction
Lorsqu'un pod Kubernetes est déjà en cours d'exécution et que les utilisateurs signalent des lenteurs, des délais d'attente ou des échecs intermittents, vous avez besoin d'une approche systématique qui passe du symptôme observé à la correction vérifiée sans provoquer de nouveaux incidents. Déboguer un pod en cours d'exécution diffère du débogage d'un déploiement qui ne démarre pas : le pod traite du trafic, donc chaque action doit minimiser les perturbations et préserver les preuves.
Ce guide pratique présente le processus d'optimisation des performances d'un pod actif. Il couvre l'inventaire de l'environnement, les modifications de configuration sûres, les diagnostics ciblés, les modes de défaillance courants et une liste de contrôle opérationnelle. Vous utiliserez des commandes réelles telles que kubectl top, kubectl exec et kubectl logs, interpréterez leurs sorties et effectuerez des changements avec un rayon d'impact contrôlé. L'accent est mis sur les conteneurs Linux fonctionnant sous Kubernetes 1.28+ avec kubectl 1.28 ou ultérieur, mais les principes s'appliquent à toute version récente.
L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter la portée de toute intervention, vérifier les résultats avec des métriques et documenter les chemins de restauration. Vous apprendrez à diagnostiquer la limitation CPU, la pression mémoire, les goulots d'étranglement d'E/S disque et les problèmes réseau sans redémarrer inutilement un pod.
Inventaire des versions et de l'environnement
Avant de toucher à un pod en cours d'exécution, établissez les versions exactes et l'état du cluster. Cela évite les suppositions erronées et garantit que les conseils d'optimisation correspondent à votre environnement.
Exécutez les commandes en lecture seule suivantes :
kubectl version --short
kubectl get nodes -o wide
kubectl describe node <nom-de-votre-nœud>
La sortie attendue comprend la version du serveur Kubernetes (par exemple, Server Version: v1.28.7), la version du noyau du nœud, le runtime de conteneur (par exemple, containerd://1.7.11) et le système d'exploitation. Notez ces informations dans vos notes d'incident. Si le nœud affiche un pourcentage élevé de %CPU ou %MEM, considérez le nœud comme un goulot d'étranglement potentiel avant de blâmer le pod.
Ensuite, inventorier le pod lui-même :
kubectl get pod <nom-du-pod> -n <espace-de-noms> -o yaml
Recherchez les requêtes et limites de ressources du pod sous spec.containers[].resources. Exemple d'extrait :
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
Si les requêtes sont absentes, le pod peut être affamé ou suralloué. Si les limites égalent les requêtes, la classe QoS est Garantie, ce qui confère une priorité plus élevée ; sinon, elle est Burstable ou BestEffort. Cela compte lors d'une pression sur le nœud.
Prérequis pour le débogage :
- Vous disposez d'un accès
kubectlavec les autorisationsget,describe,logs,execettop. - Vous pouvez lire la sortie de metrics-server. Si
kubectl toprenvoieerror: Metrics API not available, installez ou activez metrics-server avant de vous fier aux métriques CPU/mémoire. - Vous avez un moyen de générer du trafic de test, comme
hey,wrkou une bouclecurl.
Gardez les modifications locales : reproduisez le problème dans un espace de noms de préproduction avec une copie de la spécification du pod avant de modifier la production. Utilisez kubectl port-forward pour tester les points de terminaison du service sans les exposer en externe.
Chemin de configuration sûr
Modifier la configuration d'un pod en cours d'exécution est risqué. Kubernetes ne permet pas de modifier directement la plupart des champs du pod ; vous devez soit mettre à jour le Deployment, soit terminer et recréer le pod. Pour l'optimisation des performances, vous ajustez souvent les limites de ressources, les variables d'environnement qui affectent le comportement à l'exécution, ou les annotations. Suivez toujours une séquence sûre :
- Observez le comportement actuel pendant au moins cinq minutes avec charge.
- Définissez une hypothèse (par exemple, « la limite CPU est trop basse, provoquant une limitation »).
- Effectuez le plus petit changement pour répondre à cette hypothèse.
- Comparez les métriques avant et après.
- Revenez en arrière si aucune amélioration ou régression.
Exemple : augmenter la limite CPU en raison d'une limitation
Vérifiez la limitation actuelle :
kubectl exec <nom-du-pod> -- cat /sys/fs/cgroup/cpu/cpu.stat
Recherchez nr_throttled et throttled_time. Si throttled_time augmente régulièrement sous charge, le pod atteint sa limite CPU.
Modification proposée dans le manifeste du Deployment :
resources:
limits:
cpu: "1000m" # augmenté de 500m
requests:
cpu: "500m"
Appliquez avec :
kubectl apply -f updated-deployment.yaml
kubectl rollout status deployment/<nom-du-deploiement> -n <espace-de-noms>
Après le déploiement, générez la même charge et comparez nr_throttled. Si la limitation tombe à zéro ou presque, le changement a fonctionné. Sinon, envisagez d'autres goulots d'étranglement.
Exemple : ajuster une variable d'environnement de garbage collection
De nombreux runtimes (Java, Node.js) ont des variables d'environnement qui affectent l'utilisation de la mémoire. Supposons qu'une application Node.js dépasse occasionnellement les limites de mémoire et soit tuée par OOMKilled. Vous pouvez définir NODE_OPTIONS=--max-old-space-size=512 pour limiter le tas. Tout d'abord, testez localement,
puis mettez à jour le Deployment :
env:
- name: NODE_OPTIONS
value: "--max-old-space-size=512"
Appliquez et vérifiez que le pod redémarre avec le nouvel environnement en utilisant kubectl describe pod et en vérifiant la section environnement.
Gardez toujours une copie du manifeste précédent pour la restauration :
kubectl rollout undo deployment/<nom-du-deploiement>
Vérification et diagnostics
Les diagnostics doivent être superposés : commencez par les métriques au niveau du cluster, puis au niveau du pod, puis au niveau des processus à l'intérieur du conteneur.
Utilisation des ressources du cluster et du pod
Utilisez kubectl top pour obtenir l'utilisation actuelle :
kubectl top node
kubectl top pod -n <espace-de-noms>
Exemple de sortie :
NAME CPU(cores) MEMORY(bytes)
my-app-pod 720m 289Mi
Si le pod utilise systématiquement plus de 80 % de sa limite, envisagez d'augmenter la limite. S'il utilise moins de 20 % de sa requête, vous risquez de surallouer et de provoquer une pression sur le nœud ailleurs.
Journaux d'application et événements
Recherchez des modèles d'erreur :
kubectl logs <nom-du-pod> --tail=100 -n <espace-de-noms>
kubectl logs <nom-du-pod> --previous # si redémarré
kubectl get events -n <espace-de-noms> --field-selector involvedObject.name=<nom-du-pod>
Les événements révèlent souvent des OOMKilled, des sondes échouées ou des problèmes de planification.
À l'intérieur du conteneur
Exécutez une commande dans le pod pour inspecter les processus et les métriques système :
kubectl exec -it <nom-du-pod> -n <espace-de-noms> -- /bin/sh
À l'intérieur, exécutez :
ps aux
cat /proc/1/status | grep -E 'VmRSS|Threads'
Vérifiez la croissance du nombre de threads (fuites potentielles) et la mémoire RSS (utilisation réelle). Si votre image manque de ces outils, utilisez kubectl debug pour créer un conteneur de débogage temporaire :
kubectl debug <nom-du-pod> -n <espace-de-noms> --image=nicolaka/netshoot --target=<nom-du-pod>
Ensuite, depuis le conteneur de débogage, vous pouvez exécuter curl ou tcpdump contre l'espace de noms réseau du pod.
Performances réseau
Testez la latence du service interne :
kubectl exec <nom-du-pod> -- curl -o /dev/null -s -w '%{time_total}\n' http://<nom-du-service>.<espace-de-noms>.svc.cluster.local:80
Une latence élevée peut indiquer une surcharge du maillage de services, des problèmes DNS ou des problèmes CNI. Vérifiez la résolution DNS :
kubectl exec <nom-du-pod> -- nslookup <nom-du-service>
Profilage du code de l'application
Pour une analyse plus approfondie, utilisez des profileurs spécifiques au langage. Par exemple, dans une application Java, attachez jcmd (s'il est présent dans le conteneur) pour obtenir l'histogramme du tas. Ou, pour Node.js, activez le port inspecteur et connectez-vous depuis votre machine via kubectl port-forward.
Corrélez toujours les métriques avec le moment des problèmes signalés par les utilisateurs. Utilisez un système de surveillance comme Prometheus pour graphiquer le CPU, la mémoire et la durée des requêtes au fil du temps.
Modes de défaillance et récupération
Les problèmes de performances entraînent souvent des redémarrages ou des évictions de pods. Comprenez les modes de défaillance courants et comment récupérer rapidement.
OOMKilled (code de sortie 137)
Si un pod redémarre avec la raison OOMKilled :
kubectl describe pod <nom-du-pod> | grep -A 5 "Last State"
Sortie :
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Cela signifie que le conteneur a dépassé sa limite de mémoire. Options de récupération :
- Augmentez la limite de mémoire dans le Deployment.
- Réduisez l'utilisation de la mémoire par l'application (par exemple, baissez la taille du cache).
- Corrigez la fuite de mémoire.
Atténuation temporaire : définissez un memory.limit_in_bytes inférieur dans le cgroup et laissez le noyau tuer le processus gracieusement ; mais il vaut mieux corriger la cause première.
Limitation CPU
Si la limitation CPU est sévère, les temps de réponse augmentent et l'application peut échouer les sondes de préparation. Vérifiez :
kubectl describe pod <nom-du-pod> | grep -A 3 "Conditions"
Si la sonde de préparation échoue, le pod devient non prêt et le trafic est coupé. Récupération : augmentez la limite CPU, ou définissez la requête CPU égale à la limite pour obtenir la QoS Garantie et éviter la limitation dans certains environnements.
Pression disque
Si le disque du nœud est plein, les pods peuvent être évincés. Vérifiez les conditions du nœud :
kubectl describe node <nom-du-nœud> | grep -A 5 "Conditions"
Recherchez DiskPressure avec le statut True. Récupération : nettoyez les images inutilisées (crictl rmi --prune sur le nœud), supprimez les journaux inutiles ou ajoutez de la capacité disque.
Éviction de pod due à la pression du nœud
Si le pod est évincé, il est terminé et replanifié. Vérifiez la raison :
kubectl get pod <nom-du-pod> -n <espace-de-noms> -o yaml | grep -A 10 "status"
Recherchez message: The node was low on resource: memory.. Récupération : réduisez la requête de mémoire du pod ou ajoutez des nœuds.
CrashLoopBackOff
Si le pod plante à plusieurs reprises après le démarrage :
kubectl get pods -n <espace-de-noms>
# le statut affiche CrashLoopBackOff
kubectl logs <nom-du-pod> --previous
Souvent causé par une mauvaise configuration de l'application ou des limites de ressources trop basses. Vérifiez le code de sortie : 137 OOM, 139 segfault, 143 SIGTERM dû à un échec de la sonde de vivacité.
Pour chaque défaillance, ayez un runbook de récupération documenté. En cas d'urgence, vous pouvez revenir à une version précédente :
kubectl rollout undo deployment/<nom-du-deploiement>
Liste de contrôle opérationnelle pour l'optimisation des performances des pods
Utilisez cette liste de contrôle avant et après toute optimisation sur un pod en cours d'exécution. Chaque élément doit avoir un seul responsable (par exemple, l'ingénieur d'astreinte) et être revu à chaque incident de performance ou au moins mensuellement dans le cadre de l'examen des capacités.
| Élément de la liste | Action | Responsable | Fréquence | Résultat attendu |
|---|---|---|---|---|
| Inventaire des versions | Enregistrer les versions de Kubernetes, du système d'exploitation du nœud et du runtime | Ingénieure plateforme Priya Shah | Trimestriel ou lors d'une mise à niveau majeure | Les versions documentées correspondent à la matrice supportée |
| Métriques de référence | Capturer le CPU, la mémoire, le réseau et la latence du pod pendant 1 heure sous charge normale | Responsable DevOps Marcus Chen | Avant toute optimisation ou après un déploiement de code | Référence stockée dans le tableau de bord de surveillance |
| Examen des requêtes/limites de ressources | Comparer l'utilisation réelle aux requêtes et limites | Propriétaire de l'application Sarah Ali | Mensuel ou avant la planification des capacités | Écarts inférieurs à 20 % pour le CPU, 10 % pour la mémoire |
| Vérification de la limitation | Vérifier cpu.stat dans le pod pour nr_throttled | Ingénieur d'astreinte Alex Kim | Pendant un incident ou après un changement de limite | Le temps limité reste à zéro ou à un seuil acceptable |
| Analyse des journaux | Rechercher dans les journaux des erreurs, des requêtes lentes, des délais d'attente | Développeur d'application Diego Martinez | Hebdomadaire pendant le sprint | Aucun pic d'erreur nouveau |
| Réglage des sondes | Vérifier que les seuils des sondes de vivacité/préparation sont réalistes | Consultante DevOps Olga Petrova | Lors du déploiement des contrôles de santé | Les sondes passent avec une charge typique, échouent sous extrême mais récupèrent |
| Plan de restauration | S'assurer que la version précédente du manifeste est stockée dans Git et que kubectl rollout undo fonctionne | Responsable des versions Tom Wilson | Avant chaque changement en production | Restauration testée en préproduction ; s'exécute en moins de 5 minutes |
| Vérification post-changement | Après l'optimisation, exécuter un test de charge et comparer les métriques | Ingénieure QA Emily Zhang | Après chaque optimisation | Les performances répondent aux SLO définis |
Cette liste de contrôle assure la responsabilisation et l'exécution cohérente. Stockez-la dans le runbook de votre équipe ou dans un outil d'automatisation.
Pièges courants et comment les éviter
De nombreux problèmes de performances Kubernetes proviennent de malentendus. Voici des erreurs fréquentes et comment les éviter ou s'en remettre.
- Définir des limites trop basses sans comprendre le comportement de l'application : Les développeurs copient souvent des limites d'exemple (par exemple, 128Mi) et se demandent ensuite pourquoi l'application est tuée par OOMKilled. Évitez en profilant l'utilisation de la mémoire sous charge et définissez des limites à au moins 2x l'utilisation moyenne. Récupération : augmentez les limites et configurez l'autoscaling horizontal des pods.
- Ignorer la limitation CPU : La limitation peut dégrader silencieusement les performances. De nombreuses équipes ne regardent que la mémoire. Utilisez
kubectl topetcpu.statpour détecter la limitation. Récupération : augmentez la limite CPU ou utilisez le réglagecpushares.
- Ne pas distinguer les requêtes des limites : Les requêtes sont utilisées pour la planification, les limites pour l'application. Si vous définissez une limite élevée mais une requête basse, le nœud peut devenir suralloué et le pod évincé. Récupération : définissez des requêtes proches des limites pour les charges de travail critiques.
- Utiliser des étiquettes
latesten production : Cela conduit à des performances imprévisibles car le conteneur change sans modification du manifeste. Utilisez des étiquettes immuables et enregistrez le condensé de l'image. Récupération : épinglez les images au condensé et redéployez.
- Désactiver les sondes de vivacité parce qu'elles échouent pendant la charge : Certaines équipes suppriment les sondes pour arrêter les redémarrages, masquant le vrai problème. Au lieu de cela, ajustez les seuils des sondes (
failureThreshold: 3,periodSeconds: 10,timeoutSeconds: 5) pour être plus tolérants, et corrigez le problème de performance sous-jacent.
- Ne pas réserver de ressources pour les démons système : Si vous définissez des requêtes de pod trop élevées par rapport à la capacité du nœud, le kubelet peut ne pas avoir assez. Laissez toujours de la place pour le kubelet, le runtime de conteneur et le système d'exploitation. Récupération : définissez
systemReservedetkubeReservedsur les nœuds.
- Déboguer un pod actif en attachant un débogueur sans considérer l'impact : Un débogueur peut mettre en pause l'application et aggraver les performances. Utilisez des outils non intrusifs comme l'échantillonnage
pprofou utilisez un conteneur de débogage side-car avecshareProcessNamespace: truepour inspecter sans arrêter le processus principal.
- Se fier uniquement à
kubectl logspour les performances : Les journaux sont utiles mais pas pour toutes les métriques. Utilisez la surveillance, le traçage et le profilage ensemble.
Conclusion
Optimiser un pod Kubernetes en cours d'exécution exige une approche prudente et mesurée. Vous devez d'abord comprendre l'environnement et les versions, puis utiliser des diagnostics en lecture seule pour former des hypothèses, effectuer un petit changement à la fois et vérifier avec des métriques. Ayez toujours un chemin de restauration.
Points clés à retenir :
- Établissez une référence avant les changements.
- Vérifiez d'abord la limitation CPU, les limites de mémoire et la pression du nœud ; ce sont les goulots d'étranglement les plus courants.
- Utilisez
kubectl top,kubectl describe,kubectl execetkubectl debugpour recueillir des preuves. - Appliquez les changements via des mises à jour de Deployment avec contrôle de version et capacité de restauration.
- Documentez chaque étape et attribuez un seul responsable pour chaque élément de la liste de contrôle.
Comme prochaine étape, choisissez une vérification à faible risque, comme la vérification de la limitation CPU sur un pod critique. Enregistrez l'état actuel, exécutez la commande, comparez avec les valeurs attendues, puis envisagez si un réglage des ressources est nécessaire. Revisitez ce processus mensuellement pour maintenir des performances saines.
Un flux de travail fiable rend les défaillances visibles, protège les valeurs sensibles, limite les changements à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision.