Kubernetes génère des centaines de métriques par pod, par node, par composant du control plane. Sans stratégie, vous collectez tout, stockez tout, et ne comprenez rien. Ce guide vous apprend à identifier les signaux clés qui révèlent les vrais problèmes (OOMKilled, pending pods, restarts excessifs, nodes not ready), à distinguer les 3 sources de métriques (kube-state-metrics, cAdvisor, kubelet), et à éviter les pièges qui font exploser votre cardinalité et vos coûts. À la fin, vous saurez configurer un monitoring Kubernetes efficace qui détecte les vrais problèmes sans noyer votre Prometheus.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Les 4 couches d'observabilité K8s : application, conteneur, node, control plane, et pourquoi vous devez couvrir chacune
- Les 3 sources de métriques : kube-state-metrics, cAdvisor, kubelet, leur rôle, ce qu'elles exposent, comment les scraper proprement
- Les 5 signaux clés : OOMKilled, pod restarts, pending pods, node not ready, CPU throttling, les métriques exactes et les requêtes PromQL
- Les logs Kubernetes : stdout/stderr des pods, logs du control plane, pièges de volumétrie
- Le piège de la cardinalité : pourquoi les labels K8s font exploser Prometheus et comment s'en protéger
- Les noisy neighbors : détecter et isoler les workloads qui surconsomment les ressources partagées
- Les alertes essentielles : 8 alertes production-ready pour un cluster K8s minimal
- Les anti-patterns : les erreurs classiques qui rendent votre monitoring inutile ou ruineux
Les 4 couches d'observabilité Kubernetes
Section intitulée « Les 4 couches d'observabilité Kubernetes »Dans Kubernetes, les métriques viennent de 4 couches différentes. Chaque couche répond à des questions distinctes, et manquer l'une d'elles crée des angles morts.
| Couche | Ce qu'elle mesure | Exemples de questions |
|---|---|---|
| Application | Le comportement métier de votre code | Combien de requêtes ? Quel taux d'erreur ? Quelle latence ? |
| Conteneur | La consommation ressource de chaque conteneur | Combien de CPU/mémoire utilisés ? Des OOM ? Du throttling ? |
| Node | La santé de la machine hôte | Disque plein ? Réseau saturé ? Kernel panics ? |
| Control plane | Le fonctionnement de Kubernetes lui-même | L'API server répond-il ? Le scheduler place-t-il les pods ? etcd est-il sain ? |
Les 3 sources de métriques Kubernetes
Section intitulée « Les 3 sources de métriques Kubernetes »Kubernetes expose ses métriques via 3 composants distincts. Chacun a un rôle différent, les confondre mène à des dashboards redondants ou incomplets.
kube-state-metrics : l'état des objets K8s
Section intitulée « kube-state-metrics : l'état des objets K8s »kube-state-metrics (KSM) écoute l'API server Kubernetes et convertit l'état des objets (pods, deployments, nodes, etc.) en métriques Prometheus.
| Ce qu'il expose | Ce qu'il ne fait pas |
|---|---|
| Nombre de pods pending, running, failed | Consommation CPU/mémoire des conteneurs |
| Nombre de replicas désiré vs actuel | Métriques du node hôte |
| Raison du dernier redémarrage (OOMKilled, Error, etc.) | Métriques de l'application |
| État des PVC (bound, pending) | Latence réseau |
| Labels et annotations des objets K8s | Logs |
Exemples de métriques :
kube_pod_status_phase{phase="Pending"}, pods en attente de schedulingkube_pod_container_status_restarts_total, compteur de redémarrageskube_pod_container_status_last_terminated_reason{reason="OOMKilled"}, OOMkube_deployment_status_replicas_unavailable, replicas manquants
Installation : déployez kube-state-metrics dans le namespace kube-system
(Helm chart officiel) et configurez Prometheus pour scraper le service.
cAdvisor : la consommation ressource des conteneurs
Section intitulée « cAdvisor : la consommation ressource des conteneurs »cAdvisor (Container Advisor) est intégré au kubelet. Il mesure la consommation réelle de chaque conteneur : CPU, mémoire, réseau, I/O disque.
| Ce qu'il expose | Ce qu'il ne fait pas |
|---|---|
CPU utilisé par conteneur (container_cpu_usage_seconds_total) | État des objets K8s (replicas, pending) |
Mémoire utilisée (container_memory_usage_bytes) | Raison des redémarrages |
I/O disque (container_fs_reads_bytes_total) | Métriques du control plane |
Réseau par pod (container_network_receive_bytes_total) | Métriques applicatives |
Exemples de métriques :
container_cpu_usage_seconds_total, CPU utilisé (cumulatif)container_memory_working_set_bytes, mémoire effective (hors cache)container_cpu_cfs_throttled_seconds_total, temps passé en throttling CPU
Métriques kubelet : la santé des nodes
Section intitulée « Métriques kubelet : la santé des nodes »Le kubelet expose ses propres métriques sur le fonctionnement du node : opérations de pod lifecycle, stockage, runtime.
| Ce qu'il expose | Ce qu'il ne fait pas |
|---|---|
| Latence des opérations pod (start, stop, sync) | Consommation applicative |
| Erreurs du runtime (CRI) | État des objets K8s |
| Espace disque disponible pour les pods | Métriques du control plane |
| Nombre de pods par node | Logs applicatifs |
Exemples de métriques :
kubelet_pod_start_duration_seconds, temps de démarrage des podskubelet_volume_stats_available_bytes, espace disponible par PVCkubelet_running_pods, pods actifs sur le node
Scraping en production : auth, TLS et ServiceMonitors
Section intitulée « Scraping en production : auth, TLS et ServiceMonitors »Les endpoints kubelet (/metrics, /metrics/cadvisor) tournent sur le
port 10250 qui est sécurisé (TLS + authentification). En production,
évitez le "scrape brut" d'IP nodes sans durcissement.
Tableau comparatif des 3 sources
Section intitulée « Tableau comparatif des 3 sources »La colonne à retenir est Déployé par : seule kube-state-metrics est un composant que vous
installez vous-même, les deux autres sont déjà là dès qu'un nœud rejoint le cluster. C'est
pourquoi l'oubli le plus fréquent consiste à monitorer la consommation des conteneurs, disponible
sans effort, tout en restant aveugle aux pods Pending et aux OOMKilled, qui exigent le
déploiement de KSM.
| Source | Déployé par | Endpoint typique | Cas d'usage principal |
|---|---|---|---|
| kube-state-metrics | Vous (Helm/manifest) | kube-state-metrics:8080/metrics | États K8s : pending, OOMKilled, replicas |
| cAdvisor | Intégré au kubelet | <node>:10250/metrics/cadvisor | Conso ressource : CPU, mémoire, I/O |
| kubelet | Kubernetes | <node>:10250/metrics | Santé node : pod lifecycle, volumes |
Les 5 signaux clés à surveiller
Section intitulée « Les 5 signaux clés à surveiller »Ces 5 signaux couvrent 80 % des problèmes opérationnels Kubernetes. Chacun a une métrique précise, une requête PromQL, et une alerte recommandée.
1. OOMKilled, le conteneur manque de mémoire
Section intitulée « 1. OOMKilled, le conteneur manque de mémoire »Symptôme : le pod redémarre sans explication évidente, l'application perd ses sessions ou ses transactions en cours.
Métrique : kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}
Requête PromQL (pods OOMKilled récemment, corrélation avec restarts) :
increase(kube_pod_container_status_restarts_total[15m]) > 0and on (namespace, pod, container)kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1Alerte :
- alert: ContainerOOMKilled expr: | increase(kube_pod_container_status_restarts_total[15m]) > 0 and on (namespace, pod, container) kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1 for: 0m labels: severity: warning annotations: summary: "Container {{ $labels.container }} in pod {{ $labels.pod }} was OOMKilled (recent)"Actions :
- Vérifier les requests/limits mémoire actuels vs consommation réelle
- Investiguer les fuites mémoire (profiling, heap dumps)
- Vérifier la configuration du GC (Java, Go, Node.js)
- Augmenter les limits seulement si la consommation est légitime
2. Pod restarts, le cycle crash-restart
Section intitulée « 2. Pod restarts, le cycle crash-restart »Symptôme : un pod redémarre en boucle (CrashLoopBackOff), l'application est instable.
Métrique : kube_pod_container_status_restarts_total
Requête PromQL (pods avec > 0 restart sur 15 min) :
rate(kube_pod_container_status_restarts_total[15m]) > 0Alerte :
- alert: PodCrashLooping expr: rate(kube_pod_container_status_restarts_total[15m]) > 0 for: 5m labels: severity: warning annotations: summary: "Pod {{ $labels.pod }} is crash looping"Actions : examiner les logs (kubectl logs --previous), vérifier les
probes (liveness/readiness mal configurées), examiner les exit codes.
3. Pending pods, le scheduler ne peut pas placer
Section intitulée « 3. Pending pods, le scheduler ne peut pas placer »Symptôme : les pods restent en Pending, les déploiements n'avancent
pas, l'autoscaler ne scale pas.
Métrique : kube_pod_status_phase{phase="Pending"}
Requête PromQL (pods pending depuis > 5 min) :
sum by (namespace, pod) (kube_pod_status_phase{phase="Pending"}) > 0Alerte :
- alert: PodPendingTooLong expr: sum by (namespace, pod) (kube_pod_status_phase{phase="Pending"}) > 0 for: 5m labels: severity: warning annotations: summary: "Pod {{ $labels.pod }} is pending for more than 5 minutes" runbook_url: "https://example.com/runbooks/pod-pending"Actions : examiner les events (kubectl describe pod), vérifier les
ressources disponibles (CPU/mémoire du cluster), les contraintes
(nodeSelector, affinity, taints/tolerations), les PVC (pending storage).
4. Node not ready, le node est défaillant
Section intitulée « 4. Node not ready, le node est défaillant »Symptôme : les pods sur ce node sont évacués, ils basculent sur d'autres nodes, la capacité du cluster diminue.
Métrique : kube_node_status_condition{condition="Ready",status="true"}
Requête PromQL (nodes not ready, version robuste avec agrégation) :
sum by (node) (kube_node_status_condition{condition="Ready",status="true"}) == 0Alerte :
- alert: NodeNotReady expr: sum by (node) (kube_node_status_condition{condition="Ready",status="true"}) == 0 for: 2m labels: severity: critical annotations: summary: "Node {{ $labels.node }} is not ready"Actions : vérifier la connectivité réseau du node, l'état du kubelet
(systemctl status kubelet), les ressources système (disque, mémoire),
les logs kubelet (journalctl -u kubelet -f).
5. CPU throttling, le conteneur est limité
Section intitulée « 5. CPU throttling, le conteneur est limité »Symptôme : l'application est lente, la latence p99 augmente, mais l'utilisation CPU semble "normale".
Métrique : container_cpu_cfs_throttled_seconds_total
Requête PromQL (% de temps passé en throttling, approximation opérationnelle) :
rate(container_cpu_cfs_throttled_seconds_total[5m]) / (rate(container_cpu_usage_seconds_total[5m]) + 0.001) > 0.2Alerte :
- alert: ContainerCPUThrottling expr: | rate(container_cpu_cfs_throttled_seconds_total[5m]) / (rate(container_cpu_usage_seconds_total[5m]) + 0.001) > 0.2 for: 5m labels: severity: warning annotations: summary: "Container {{ $labels.container }} is being CPU throttled"Actions :
- Vérifier les CPU limits actuels vs consommation réelle
- Augmenter les limits CPU si le throttling est constant
- Pour les workloads sensibles à la latence : envisager "requests only"
Logs Kubernetes
Section intitulée « Logs Kubernetes »Logs des pods : stdout/stderr
Section intitulée « Logs des pods : stdout/stderr »Dans Kubernetes, les logs applicatifs sont les flux stdout et stderr des conteneurs. Le kubelet les capture et les stocke sur le node.
Collecte : déployez un DaemonSet de collecteurs (Grafana Alloy,
Fluent Bit, Vector) qui lit les fichiers /var/log/pods/ sur chaque
node et les envoie vers Loki, Elasticsearch, ou votre backend de logs.
Pièges courants :
| Piège | Conséquence | Solution |
|---|---|---|
| Logs trop volumineux | Stockage qui explose, coûts élevés | Filtrer à la source, échantillonner, définir des rétentions courtes |
| Logs non structurés | Recherche impossible, parsing fragile | Logger en JSON, définir un schéma standard |
| Pas de labels K8s | Impossible de corréler avec les métriques/traces | Enrichir avec pod, namespace, container au moment de la collecte |
Logs du control plane
Section intitulée « Logs du control plane »Les composants du control plane (API server, scheduler, controller manager,
etcd) écrivent leurs logs vers stdout, visibles via kubectl logs dans
le namespace kube-system ou via journald sur les nodes master.
| Composant | Logs utiles | Cas d'usage |
|---|---|---|
| kube-apiserver | Requêtes lentes, erreurs d'authentification, rate limiting | Debug RBAC, audit, perf API |
| kube-scheduler | Échecs de placement, raisons de pending | Comprendre pourquoi un pod ne se schedule pas |
| kube-controller-manager | Erreurs de reconciliation | Debug deployments, replicasets, jobs |
| etcd | Latences, compactions, élections leader | Diagnostic cluster instable |
Le piège de la cardinalité
Section intitulée « Le piège de la cardinalité »Kubernetes génère des labels dynamiques : pod, container, uid,
revision, node. Ces labels changent à chaque déploiement, chaque
restart, chaque scale. Le résultat : votre cardinalité explose.
Le problème
Section intitulée « Le problème »Prometheus stocke chaque combinaison unique de metric + labels comme une time series. Le nombre de séries = produit du nombre de valeurs de chaque label.
Exemple catastrophique :
container_cpu_usage_seconds_total{ namespace="production", pod="payment-api-abc123", container="payment", node="node-1"}Avec 50 pods × 20 conteneurs × 10 nodes × rolling updates fréquents, vous atteignez rapidement des millions de séries, et Prometheus s'effondre.
Les labels dangereux
Section intitulée « Les labels dangereux »Le critère de classement n'est pas le nombre de valeurs à un instant donné, mais la vitesse à
laquelle de nouvelles valeurs apparaissent. Un label node reste stable pendant des mois ; un
label pod change à chaque rollout, et chaque valeur disparue laisse derrière elle une série
inactive que Prometheus conserve jusqu'à l'expiration de la rétention. C'est ce phénomène, appelé
churn, qui fait grimper la consommation mémoire bien avant que le nombre de pods vivants ne
paraisse inquiétant.
| Label | Cardinalité typique | Risque |
|---|---|---|
pod | Illimitée (change à chaque restart) | Très élevé |
uid | Illimitée (unique par objet) | Très élevé |
container_id | Illimitée | Très élevé |
id | Illimitée (cgroup id) | Très élevé |
name | Potentiellement illimitée | Très élevé |
image | Haute (change à chaque tag) | Moyen-élevé |
revision | Potentiellement élevée | Moyen |
node | Nombre de nodes (souvent < 100) | Faible |
namespace | Nombre de namespaces (souvent < 50) | Faible |
Comment s'en protéger
Section intitulée « Comment s'en protéger »Principe : on filtre d'abord les labels à très forte cardinalité, on drop des métriques seulement quand on sait qu'on ne les utilise pas (après audit des dashboards, alerts, recording rules).
1. Filtrez les labels explosifs au scraping (metric_relabel_configs) :
scrape_configs: - job_name: 'cadvisor' metric_relabel_configs: # Supprimer les labels à cardinalité illimitée - action: labeldrop regex: 'id|name|image|container_id|uid'2. Utilisez des labels bornés :
deployment,statefulsetau lieu depodnamespace,serviceau lieu decontainer_id- Normalisez les chemins :
/users/12345→/users/:id
3. Surveillez votre cardinalité :
# Top 10 métriques par nombre de sériestopk(10, count by (__name__) ({__name__!=""}))
# Séries par label pour une métriquecount by (pod) (container_cpu_usage_seconds_total)4. Alerte sur la croissance :
- alert: HighCardinalityMetric expr: count by (__name__) ({__name__=~"container_.*"}) > 100000 for: 5m labels: severity: warning annotations: summary: "Metric {{ $labels.__name__ }} has very high cardinality"Noisy neighbors : détecter les workloads qui surconsomment
Section intitulée « Noisy neighbors : détecter les workloads qui surconsomment »Dans un cluster multi-tenant, un workload peut consommer toutes les ressources d'un node et dégrader les performances des autres pods, c'est le "noisy neighbor problem".
Symptômes
Section intitulée « Symptômes »Ce qui caractérise un noisy neighbor, c'est que les symptômes apparaissent sur des workloads qui
n'ont rien changé. Un déploiement dont la latence se dégrade sans nouvelle version ni hausse de
trafic doit vous faire regarder ses colocataires sur le node, pas son propre code. Le
recoupement se fait avec le label node : listez les autres pods du même node sur la fenêtre de
l'incident.
- Latence p99 qui augmente sur des pods "innocents"
- CPU throttling sur des pods qui ne devraient pas en avoir
- Evictions de pods pour pression mémoire
- Pods qui ne se schedulent plus (resources exhausted)
Métriques de détection
Section intitulée « Métriques de détection »Les trois requêtes ci-dessous répondent à trois questions distinctes : qui consomme, combien, et
qui n'est pas encadré. Notez le filtre container!="" : les métriques cAdvisor incluent une série
agrégée pour le sandbox du pod, dont le label container est vide. Sans ce filtre, vous
comptez deux fois la consommation.
CPU par namespace (part de la consommation totale du cluster) :
sum by (namespace) ( rate(container_cpu_usage_seconds_total{container!=""}[5m]))/ ignoring(namespace) group_leftsum(rate(container_cpu_usage_seconds_total{container!=""}[5m]))Mémoire par namespace :
sum by (namespace) (container_memory_working_set_bytes{container!=""})Conteneurs sans limite CPU (candidats au noisy neighbor) :
count by (namespace, pod, container) (kube_pod_container_info)unless on (namespace, pod, container)kube_pod_container_resource_limits{resource="cpu"}Protection
Section intitulée « Protection »Ces quatre mécanismes agissent à des moments différents de la vie d'un pod, et ils se combinent. Les Limit Ranges interviennent à l'admission en injectant des valeurs par défaut, ce qui traite la racine du problème : un conteneur sans limite. Les Resource Quotas plafonnent la consommation cumulée d'un namespace. Les Priority Classes n'entrent en jeu qu'en situation de pression, pour décider qui est évincé en premier. Commencez par les Limit Ranges, c'est le seul qui empêche le problème d'apparaître.
| Méthode | Description | Quand l'utiliser |
|---|---|---|
| Resource Quotas | Limites par namespace (CPU/mémoire totaux) | Multi-tenant |
| Limit Ranges | Defaults et limites par pod/conteneur | Empêcher les pods sans limits |
| Priority Classes | Éviction ordonnée en cas de pression | Protéger les workloads critiques |
| Pod Disruption Budgets | Garantir un minimum de réplicas | Haute disponibilité |
8 alertes essentielles pour un cluster K8s
Section intitulée « 8 alertes essentielles pour un cluster K8s »Voici un set minimal d'alertes production-ready, couvrant les signaux
critiques sans bruit excessif. Deux conventions structurent ce bloc et méritent d'être reprises
telles quelles. D'abord la sévérité : seul NodeNotReady est en critical, parce que c'est le
seul événement qui réduit la capacité du cluster sans action possible de l'application. Ensuite le
runbook_url, présent sur chaque règle : une alerte sans procédure associée finit par être
ignorée, et l'annotation est ce qui transforme une notification en action. Remplacez le domaine par
celui de votre propre base de connaissances.
groups: - name: kubernetes-essential rules: # 1. Pod OOMKilled (événement récent) - alert: ContainerOOMKilled expr: | increase(kube_pod_container_status_restarts_total[15m]) > 0 and on (namespace, pod, container) kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1 for: 0m labels: severity: warning annotations: summary: "Container {{ $labels.container }} was OOMKilled (recent)" runbook_url: "https://example.com/runbooks/oomkilled"
# 2. Pod crash looping - alert: PodCrashLooping expr: rate(kube_pod_container_status_restarts_total[15m]) > 0 for: 5m labels: severity: warning annotations: summary: "Pod {{ $labels.pod }} is crash looping" runbook_url: "https://example.com/runbooks/crashloop"
# 3. Pod pending - alert: PodPendingTooLong expr: sum by (namespace, pod) (kube_pod_status_phase{phase="Pending"}) > 0 for: 5m labels: severity: warning annotations: summary: "Pod {{ $labels.pod }} is pending" runbook_url: "https://example.com/runbooks/pod-pending"
# 4. Node not ready (version robuste) - alert: NodeNotReady expr: sum by (node) (kube_node_status_condition{condition="Ready",status="true"}) == 0 for: 2m labels: severity: critical annotations: summary: "Node {{ $labels.node }} is not ready" runbook_url: "https://example.com/runbooks/node-not-ready"
# 5. CPU throttling - alert: ContainerCPUThrottling expr: | rate(container_cpu_cfs_throttled_seconds_total[5m]) / (rate(container_cpu_usage_seconds_total[5m]) + 0.001) > 0.2 for: 5m labels: severity: warning annotations: summary: "Container {{ $labels.container }} is CPU throttled" runbook_url: "https://example.com/runbooks/cpu-throttling"
# 6. Mémoire proche de la limite # Le filtre "> 0" écarte les conteneurs sans limite, dont # container_spec_memory_limit_bytes vaut 0 (division par zéro). - alert: ContainerMemoryNearLimit expr: | container_memory_working_set_bytes{container!=""} / (container_spec_memory_limit_bytes{container!=""} > 0) > 0.9 for: 5m labels: severity: warning annotations: summary: "Container {{ $labels.container }} memory at 90%+ of limit" runbook_url: "https://example.com/runbooks/memory-near-limit"
# 7. Deployment replicas mismatch - alert: DeploymentReplicasMismatch expr: | kube_deployment_spec_replicas != kube_deployment_status_replicas_available for: 5m labels: severity: warning annotations: summary: "Deployment {{ $labels.deployment }} replicas mismatch" runbook_url: "https://example.com/runbooks/replicas-mismatch"
# 8. PVC pending - alert: PersistentVolumeClaimPending expr: kube_persistentvolumeclaim_status_phase{phase="Pending"} == 1 for: 5m labels: severity: warning annotations: summary: "PVC {{ $labels.persistentvolumeclaim }} is pending" runbook_url: "https://example.com/runbooks/pvc-pending"Anti-patterns : ce qu'il ne faut pas faire
Section intitulée « Anti-patterns : ce qu'il ne faut pas faire »Deux familles se dégagent de ce tableau. Les six premières lignes coûtent de l'argent et de la stabilité : elles produisent un Prometheus saturé et une facture de stockage qui grimpe sans que personne ne sache dire quelles séries servent réellement. Les quatre dernières coûtent du temps d'incident : le monitoring tourne, mais il ne répond pas aux questions qu'on se pose à trois heures du matin. Si vous devez n'en corriger qu'une seule, prenez « Pas de kube-state-metrics » : sans elle, aucun des cinq signaux clés n'est observable.
| Anti-pattern | Pourquoi c'est un problème | Solution |
|---|---|---|
Scraper pod comme label | Cardinalité explose à chaque deploy | Agréger par deployment, service |
| Collecter toutes les métriques cAdvisor | Centaines de séries par conteneur | Filtrer au scraping (labeldrop sur labels explosifs) |
| Pas de kube-state-metrics | Impossible de voir pending, OOMKilled, replicas | Déployer KSM dès le premier jour |
| Alertes sur les métriques containers seules | Manque le contexte K8s (rollout, scaling) | Combiner avec kube-state-metrics |
| Logs sans labels K8s | Impossible de corréler avec metrics/traces | Enrichir avec namespace, pod, container |
| Ignorer le control plane | Problèmes API server, scheduler invisibles | Collecter les métriques du control plane |
| Pas de resource quotas | Un namespace peut tuer le cluster | Resource quotas en multi-tenant |
| Limits CPU trop basses | Throttling artificiel, latence dégradée | Évaluer requests-only pour workloads sensibles |
| Dashboards par pod | 200 pods = dashboard illisible | Agréger par deployment, namespace, service |
| Rétention logs illimitée | Coûts qui explosent | Rétention courte (7-30 jours), tier froid |
Workflow : déployer l'observabilité sur un cluster K8s
Section intitulée « Workflow : déployer l'observabilité sur un cluster K8s »L'ordre de ces huit étapes n'est pas arbitraire. Le filtrage de cardinalité arrive dès l'étape 2, avant les dashboards et les alertes, parce que revenir dessus plus tard signifie perdre l'historique des séries déjà ingérées. À l'inverse, les resource quotas viennent tard : ils supposent de connaître la consommation réelle de chaque namespace, information que seules les étapes précédentes vous donnent. Comptez une demi-journée pour les étapes 1 à 5 sur un cluster existant, et considérez les étapes 7 et 8 comme un travail récurrent, pas comme une mise en place ponctuelle.
-
Déployez kube-state-metrics
Helm chart officiel ou manifest. Vérifiez que le service est scrapé par Prometheus. Vous devriez voir les métriques
kube_*. -
Configurez le scraping cAdvisor / kubelet avec ServiceMonitors
Utilisez les ServiceMonitors/PodMonitors de votre stack (kube-prometheus-stack). Configurez le RBAC et les certificats TLS. Ajoutez des
metric_relabel_configspour filtrer les labels dangereux (id,name,container_id,uid). -
Déployez un collecteur de logs
Grafana Alloy, Fluent Bit ou Vector en DaemonSet ; Promtail est en fin de vie depuis mars 2026 et ne doit plus être choisi pour un nouveau déploiement. Configurez l'enrichissement avec les labels K8s (
namespace,pod,container) et l'envoi vers Loki ou votre backend. -
Créez les dashboards de base
Un dashboard "Cluster overview" (nodes, pods pending, restarts). Un dashboard "Namespace" (CPU/mémoire par deployment, top consumers). Un dashboard "Pod debugging" (logs + métriques pour investigation).
-
Déployez les 8 alertes essentielles
PrometheusRule avec les alertes OOMKilled, crash loop, pending, node not ready, throttling, memory near limit, replicas mismatch, PVC pending. Ajoutez les annotations
runbook_urlvers vos runbooks. -
Configurez les resource quotas (clusters multi-tenant)
Limites par namespace pour CPU et mémoire. Limit ranges pour les defaults pod.
-
Surveillez votre cardinalité
Dashboard dédié : top métriques par séries, croissance au fil du temps. Alerte si une métrique dépasse un seuil.
-
Itérez sur les filtres et agrégations
Chaque semaine, identifiez les métriques inutilisées et les labels redondants. Affinez vos recording rules pour pré-agréger.
Pièges courants
Section intitulée « Pièges courants »Ces cinq pièges ont un point commun : ils ne se manifestent pas au moment où vous les créez. Un scraping trop large ne pose aucun problème sur un cluster de dix pods, il fait tomber Prometheus six mois plus tard quand le cluster en compte trois cents. Le seul moyen de les détecter tôt est de surveiller la tendance plutôt que la valeur du jour : nombre de séries actives, volume de logs ingérés par jour, durée des requêtes de vos dashboards.
| Piège | Conséquence | Solution |
|---|---|---|
| Scraper toutes les métriques K8s | Prometheus OOM, requêtes lentes | Filtrer dès le scraping |
| Ignorer le throttling CPU | Latence dégradée sans explication | Alerter sur container_cpu_cfs_throttled |
| Dashboards avec 500 séries | Grafana timeout, impossible à lire | Agréger, filtrer, utiliser des variables |
| Logs en mode debug en prod | Volumétrie ×10, coûts ×10 | Niveau info ou warn en prod |
| Pas de rétention différenciée | Tout garder = coûts exponentiels | Hot/warm/cold tiers, rétention par criticité |
À retenir
Section intitulée « À retenir »-
Kubernetes expose des métriques à 4 couches : application (vous l'instrumentez), conteneur (cAdvisor), node (kubelet), control plane (API server, scheduler, etcd)
-
Les 3 sources principales : kube-state-metrics pour l'état K8s (pending, OOMKilled, replicas), cAdvisor pour la consommation ressource (CPU, mémoire, I/O), kubelet pour la santé node
-
Les 5 signaux clés : OOMKilled (mémoire dépassée), pod restarts (crash loop), pending pods (scheduler bloqué), node not ready (node défaillant), CPU throttling (limits trop basses ou CFS quota)
-
La cardinalité explose avec les labels dynamiques (
pod,uid,container_id). Filtrez dès le scraping aveclabeldrop, agrégez pardeploymentouservice, surveillez avec des requêtes PromQL dédiées -
Les noisy neighbors se détectent via comparaison de consommation par namespace/deployment. Protégez-vous avec resource quotas et limit ranges
-
Les logs K8s sont stdout/stderr des pods. Enrichissez-les avec les labels K8s à la collecte, limitez la volumétrie avec des rétentions courtes
-
Pour le control plane (clusters self-hosted), collectez les métriques de l'API server, scheduler, controller manager, etcd. Sur les clusters managés, utilisez l'intégration du provider
-
Commencez avec 8 alertes essentielles : OOMKilled (événementiel), crash loop, pending, node not ready, throttling, memory near limit, replicas mismatch, PVC pending
- Kubernetes, Monitoring overview : kubernetes.io/docs/tasks/debug/monitoring, la référence officielle sur le monitoring K8s
- Kubernetes, System components metrics : kubernetes.io/docs/concepts/cluster-administration/system-metrics, documentation des endpoints kubelet, cAdvisor, et métriques du control plane
- kube-state-metrics, GitHub : github.com/kubernetes/kube-state-metrics, documentation et liste exhaustive des métriques exposées
- Robust Perception, Cardinality is key : robustperception.io/cardinality-is-key, Brian Brazil sur la cardinalité Prometheus
- Kubernetes, kubelet reference : kubernetes.io/docs/reference/command-line-tools-reference/kubelet, référence kubelet incluant les aspects CFS quota et throttling