Retour au cours

infra / monitoring-observabilite

Monitoring de conteneurs : cAdvisor et Kubernetes

Leçon 121 exercice

Explication

Ce que vous allez apprendre

  • Comprendre la différence entre la consommation réelle (cAdvisor) et l'état déclaratif (kube-state-metrics)
  • Déployer cAdvisor pour surveiller la consommation de conteneurs Docker
  • Détecter un conteneur proche de l'OOM-kill avant qu'il ne soit tué
  • Utiliser un ServiceMonitor pour automatiser la découverte de cibles dans Kubernetes
  • Repérer un déploiement Kubernetes dégradé via ses métriques de replicas

Dans quel contexte ?

L'infrastructure a évolué : mon-api ne tourne plus sur un serveur unique surveillé par node_exporter (leçon précédente), mais dans des conteneurs Docker, bientôt orchestrés par Kubernetes. Un conteneur peut consommer toute la mémoire disponible sans que ça n'apparaisse jamais dans les métriques classiques du serveur hôte, car cette consommation reste isolée à l'intérieur du conteneur — un nouvel angle mort qu'il faut couvrir spécifiquement.

D'abord, il faut un exporter capable de voir À L'INTÉRIEUR des conteneurs

node_exporter donne une vue globale de la machine, mais ne distingue pas la consommation de chaque conteneur individuellement. cAdvisor (Container Advisor), développé par Google, comble ce manque : il expose la consommation CPU, mémoire et réseau conteneur par conteneur, pas seulement machine par machine.

Cette distinction devient cruciale dès qu'un seul serveur héberge plusieurs applications indépendantes : sans cAdvisor, impossible de savoir LEQUEL des dix conteneurs consomme la mémoire qui manque.

OutilCe qu'il mesureQuestion à laquelle il répond
node_exporterConsommation de la machine entière"Le serveur va-t-il manquer de ressources ?"
cAdvisorConsommation par conteneur individuel"Quel conteneur consomme le plus ?"
kube-state-metricsÉtat déclaratif des objets Kubernetes"Le déploiement a-t-il le bon nombre de replicas ?"

Prérequis

Une connaissance de base de Docker (voir le cours dédié) est nécessaire pour comprendre les volumes montés par cAdvisor (/sys, /var/lib/docker) ; des notions Kubernetes aident pour la seconde moitié de la leçon.

Une fois cAdvisor en place, un cas d'usage concret devient immédiatement possible

En comparant container_memory_usage_bytes à container_spec_memory_limit_bytes, on peut détecter qu'un conteneur approche dangereusement de sa limite mémoire — et donc d'un OOM-kill imminent, le mécanisme par lequel le noyau Linux tue brutalement un processus qui dépasse sa limite.

Piège fréquent

Attendre l'alerte "conteneur redémarré" pour réagir revient toujours à réagir APRÈS l'incident. Poser une alerte sur container_memory_usage_bytes / container_spec_memory_limit_bytes > 0.9 permet d'intervenir (augmenter la limite, corriger une fuite mémoire) avant que le conteneur ne soit tué par le noyau.

Maintenant que la consommation est couverte, il reste une autre dimension propre à Kubernetes : l'état déclaratif

Kubernetes fonctionne de façon déclarative : on décrit l'état voulu ("3 replicas de mon-api"), et le contrôleur s'efforce de le maintenir. kube-state-metrics expose justement cet écart entre l'état voulu et l'état réel, une information que cAdvisor, focalisé sur la consommation, ne fournit pas du tout.

kube_deployment_status_replicas_available < kube_deployment_spec_replicas détecte ainsi un déploiement dégradé — par exemple si un pod crash en boucle — souvent avant même que l'application ne remonte la moindre erreur applicative.

Il reste un dernier problème pratique : comment scraper automatiquement des pods qui changent constamment ?

Astuce

Dans un cluster Kubernetes dynamique, éditer prometheus.yml à la main à chaque nouveau service devient vite intenable. Le CRD ServiceMonitor du Prometheus Operator automatise cette découverte : il suffit qu'un Service Kubernetes porte le bon label pour que Prometheus commence à le scraper automatiquement, sans redéploiement de configuration.

Après cette plongée dans les conteneurs et Kubernetes, la prochaine leçon revient au code : comment écrire ses propres exporters personnalisés pour des systèmes qui n'en ont pas encore.

Commandes & code

Monitoring de conteneurs : cAdvisor et Kubernetes

yaml
# docker-compose.yml — cAdvisor expose les métriques de TOUS les conteneurs d'un hôte Docker
services:
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    ports: ["8080:8080"]
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
promql
# Métriques cAdvisor par conteneur — filtrer par le label "name" ou "container_label_com_docker_compose_service"
sum(rate(container_cpu_usage_seconds_total{name=~"app.*"}[5m])) by (name)          # CPU par conteneur
container_memory_usage_bytes{name=~"app.*"} / 1024 / 1024                          # mémoire en Mo
rate(container_network_receive_bytes_total{name=~"app.*"}[5m])                     # réseau entrant

# Détecter les conteneurs en train de se faire OOM-kill
container_memory_usage_bytes / container_spec_memory_limit_bytes > 0.9              # >90% de la limite mémoire
yaml
# Kubernetes : kube-state-metrics + node_exporter (DaemonSet) + cAdvisor (déjà intégré au kubelet)
# kube-state-metrics expose l'état DÉCLARATIF des objets K8s (pas la conso, mais l'état : replicas désirés vs actuels)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-state-metrics
  namespace: monitoring
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: kube-state-metrics
          image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.13.0
          ports:
            - containerPort: 8080
yaml
# ServiceMonitor (CRD du Prometheus Operator) : découverte automatique des cibles à scraper dans K8s
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: mon-api-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: mon-api                 # cible tous les Services portant ce label
  endpoints:
    - port: metrics                 # nom du port défini dans le Service K8s
      interval: 15s
      path: /metrics
promql
# Métriques Kubernetes essentielles
kube_deployment_status_replicas_available{deployment="mon-api"}
< kube_deployment_spec_replicas{deployment="mon-api"}
# -> vrai si moins de replicas disponibles que voulus (déploiement dégradé)

rate(container_cpu_usage_seconds_total{namespace="production"}[5m])
  / on(pod) kube_pod_container_resource_limits{resource="cpu"}
# -> % d'utilisation CPU par rapport à la limite définie dans le manifest (détecte le throttling)

kube_pod_container_status_restarts_total > 5     # pods qui crash-loop (redémarrages fréquents)
yaml
# Sidecar de scraping dans un Pod K8s (pattern courant sans Operator) : annotation lue par Prometheus
apiVersion: v1
kind: Pod
metadata:
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8000"
    prometheus.io/path: "/metrics"

Résumé

  • cAdvisor donne la CONSOMMATION réelle par conteneur (CPU, mémoire, réseau) ; kube-state-metrics donne l'ÉTAT DÉCLARATIF des objets K8s (replicas, restarts).
  • Le Prometheus Operator (CRD ServiceMonitor) automatise la découverte des cibles à scraper dans un cluster, sans éditer prometheus.yml à la main.
  • container_memory_usage_bytes / container_spec_memory_limit_bytes proche de 1 annonce un OOM-kill imminent : alerte à poser systématiquement.
  • Comparer kube_deployment_status_replicas_available à kube_deployment_spec_replicas détecte un déploiement dégradé avant même l'alerte applicative.

Exercices pratiques

1 disponible
1

Mission : le conteneur qui va se faire tuer

Objectif : Détecter un conteneur proche de l'OOM-kill avant qu'il ne soit tué, et diagnostiquer un déploiement Kubernetes dégradé via ses métriques déclaratives.

Contexte

mon-api tourne maintenant dans un Deployment Kubernetes avec 3 replicas voulus. cAdvisor est en place. Un des conteneurs consomme une mémoire anormalement croissante et le déploiement semble instable, mais aucune alerte applicative n'a encore été déclenchée.

Résoudre l’exercice →