Aller au contenu
medium

Observabilité Kubernetes : signaux clés et pièges à éviter

32 min de lecture

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.

  • 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

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.

CoucheCe qu'elle mesureExemples de questions
ApplicationLe comportement métier de votre codeCombien de requêtes ? Quel taux d'erreur ? Quelle latence ?
ConteneurLa consommation ressource de chaque conteneurCombien de CPU/mémoire utilisés ? Des OOM ? Du throttling ?
NodeLa santé de la machine hôteDisque plein ? Réseau saturé ? Kernel panics ?
Control planeLe fonctionnement de Kubernetes lui-mêmeL'API server répond-il ? Le scheduler place-t-il les pods ? etcd est-il sain ?

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 (KSM) écoute l'API server Kubernetes et convertit l'état des objets (pods, deployments, nodes, etc.) en métriques Prometheus.

Ce qu'il exposeCe qu'il ne fait pas
Nombre de pods pending, running, failedConsommation CPU/mémoire des conteneurs
Nombre de replicas désiré vs actuelMé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 K8sLogs

Exemples de métriques :

  • kube_pod_status_phase{phase="Pending"}, pods en attente de scheduling
  • kube_pod_container_status_restarts_total, compteur de redémarrages
  • kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}, OOM
  • kube_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 exposeCe 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

Le kubelet expose ses propres métriques sur le fonctionnement du node : opérations de pod lifecycle, stockage, runtime.

Ce qu'il exposeCe 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 podsMétriques du control plane
Nombre de pods par nodeLogs applicatifs

Exemples de métriques :

  • kubelet_pod_start_duration_seconds, temps de démarrage des pods
  • kubelet_volume_stats_available_bytes, espace disponible par PVC
  • kubelet_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.

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.

SourceDéployé parEndpoint typiqueCas d'usage principal
kube-state-metricsVous (Helm/manifest)kube-state-metrics:8080/metricsÉtats K8s : pending, OOMKilled, replicas
cAdvisorIntégré au kubelet<node>:10250/metrics/cadvisorConso ressource : CPU, mémoire, I/O
kubeletKubernetes<node>:10250/metricsSanté node : pod lifecycle, volumes

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.

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]) > 0
and on (namespace, pod, container)
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1

Alerte :

- 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 :

  1. Vérifier les requests/limits mémoire actuels vs consommation réelle
  2. Investiguer les fuites mémoire (profiling, heap dumps)
  3. Vérifier la configuration du GC (Java, Go, Node.js)
  4. Augmenter les limits seulement si la consommation est légitime

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]) > 0

Alerte :

- 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.

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"}) > 0

Alerte :

- 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).

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"}) == 0

Alerte :

- 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).

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.2

Alerte :

- 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 :

  1. Vérifier les CPU limits actuels vs consommation réelle
  2. Augmenter les limits CPU si le throttling est constant
  3. Pour les workloads sensibles à la latence : envisager "requests only"

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ègeConséquenceSolution
Logs trop volumineuxStockage qui explose, coûts élevésFiltrer à la source, échantillonner, définir des rétentions courtes
Logs non structurésRecherche impossible, parsing fragileLogger en JSON, définir un schéma standard
Pas de labels K8sImpossible de corréler avec les métriques/tracesEnrichir avec pod, namespace, container au moment de la collecte

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.

ComposantLogs utilesCas d'usage
kube-apiserverRequêtes lentes, erreurs d'authentification, rate limitingDebug RBAC, audit, perf API
kube-schedulerÉchecs de placement, raisons de pendingComprendre pourquoi un pod ne se schedule pas
kube-controller-managerErreurs de reconciliationDebug deployments, replicasets, jobs
etcdLatences, compactions, élections leaderDiagnostic cluster instable

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.

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.

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.

LabelCardinalité typiqueRisque
podIllimitée (change à chaque restart)Très élevé
uidIllimitée (unique par objet)Très élevé
container_idIllimitéeTrès élevé
idIllimitée (cgroup id)Très élevé
namePotentiellement illimitéeTrès élevé
imageHaute (change à chaque tag)Moyen-élevé
revisionPotentiellement élevéeMoyen
nodeNombre de nodes (souvent < 100)Faible
namespaceNombre de namespaces (souvent < 50)Faible

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, statefulset au lieu de pod
  • namespace, service au lieu de container_id
  • Normalisez les chemins : /users/12345/users/:id

3. Surveillez votre cardinalité :

# Top 10 métriques par nombre de séries
topk(10, count by (__name__) ({__name__!=""}))
# Séries par label pour une métrique
count 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".

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)

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_left
sum(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"}

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éthodeDescriptionQuand l'utiliser
Resource QuotasLimites par namespace (CPU/mémoire totaux)Multi-tenant
Limit RangesDefaults et limites par pod/conteneurEmpêcher les pods sans limits
Priority ClassesÉviction ordonnée en cas de pressionProtéger les workloads critiques
Pod Disruption BudgetsGarantir un minimum de réplicasHaute disponibilité

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"

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-patternPourquoi c'est un problèmeSolution
Scraper pod comme labelCardinalité explose à chaque deployAgréger par deployment, service
Collecter toutes les métriques cAdvisorCentaines de séries par conteneurFiltrer au scraping (labeldrop sur labels explosifs)
Pas de kube-state-metricsImpossible de voir pending, OOMKilled, replicasDéployer KSM dès le premier jour
Alertes sur les métriques containers seulesManque le contexte K8s (rollout, scaling)Combiner avec kube-state-metrics
Logs sans labels K8sImpossible de corréler avec metrics/tracesEnrichir avec namespace, pod, container
Ignorer le control planeProblèmes API server, scheduler invisiblesCollecter les métriques du control plane
Pas de resource quotasUn namespace peut tuer le clusterResource quotas en multi-tenant
Limits CPU trop bassesThrottling artificiel, latence dégradéeÉvaluer requests-only pour workloads sensibles
Dashboards par pod200 pods = dashboard illisibleAgréger par deployment, namespace, service
Rétention logs illimitéeCoûts qui explosentRé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.

  1. 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_*.

  2. 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_configs pour filtrer les labels dangereux (id, name, container_id, uid).

  3. 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.

  4. 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).

  5. 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_url vers vos runbooks.

  6. Configurez les resource quotas (clusters multi-tenant)

    Limites par namespace pour CPU et mémoire. Limit ranges pour les defaults pod.

  7. 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.

  8. 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.

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ègeConséquenceSolution
Scraper toutes les métriques K8sPrometheus OOM, requêtes lentesFiltrer dès le scraping
Ignorer le throttling CPULatence dégradée sans explicationAlerter sur container_cpu_cfs_throttled
Dashboards avec 500 sériesGrafana timeout, impossible à lireAgréger, filtrer, utiliser des variables
Logs en mode debug en prodVolumétrie ×10, coûts ×10Niveau info ou warn en prod
Pas de rétention différenciéeTout garder = coûts exponentielsHot/warm/cold tiers, rétention par criticité
  1. Kubernetes expose des métriques à 4 couches : application (vous l'instrumentez), conteneur (cAdvisor), node (kubelet), control plane (API server, scheduler, etcd)

  2. 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

  3. 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)

  4. La cardinalité explose avec les labels dynamiques (pod, uid, container_id). Filtrez dès le scraping avec labeldrop, agrégez par deployment ou service, surveillez avec des requêtes PromQL dédiées

  5. Les noisy neighbors se détectent via comparaison de consommation par namespace/deployment. Protégez-vous avec resource quotas et limit ranges

  6. 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

  7. 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

  8. Commencez avec 8 alertes essentielles : OOMKilled (événementiel), crash loop, pending, node not ready, throttling, memory near limit, replicas mismatch, PVC pending

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn