infra / monitoring-observabilite
Cardinalité, coûts et bonnes pratiques de production
Explication
Ce que vous allez apprendre
- Calculer la cardinalité réelle d'une métrique à partir de ses labels
- Diagnostiquer une explosion de cardinalité déjà en cours en production
- Corriger une instrumentation applicative dangereuse avant qu'elle ne cause un incident
- Filtrer des métriques indésirables à la source avec
metric_relabel_configs - Appliquer une checklist de production complète pour une stack Prometheus
Dans quel contexte ?
Un vendredi soir, le serveur Prometheus de production se met soudain à consommer toute sa mémoire disponible et finit par crasher, sans qu'aucun changement de configuration n'ait été fait. En creusant, l'équipe découvre qu'un développeur a ajouté un label user_id à une métrique deux semaines plus tôt — et que la base d'utilisateurs vient justement de dépasser les 500 000 comptes actifs.
D'abord, il faut comprendre ce que "cardinalité" veut dire concrètement
La cardinalité d'une métrique est le nombre de combinaisons UNIQUES possibles entre tous ses labels. Une métrique avec 4 méthodes HTTP, 50 routes et 6 status codes possibles crée 4 × 50 × 6 = 1200 séries temporelles distinctes — un chiffre parfaitement gérable pour Prometheus.
Le problème surgit dès qu'un label prend un nombre de valeurs qui n'est PAS borné à l'avance. Ajouter user_id à cette même métrique, avec 500 000 utilisateurs actifs, multiplie instantanément la cardinalité par 500 000 : elle passe de 1200 à 600 millions de séries, largement suffisant pour saturer la mémoire de n'importe quel Prometheus.
| Situation | Cardinalité | Verdict |
|---|---|---|
method (4) × path (50) × status (6) | 1 200 séries | Parfaitement gérable |
Ajout de status_class ("2xx"/"4xx"/"5xx") au lieu du code exact | Réduite | Bonne pratique |
Ajout de user_id (500 000 valeurs) | 600 000 000 séries | Catastrophe mémoire |
Prérequis
Cette leçon suppose que tu as déjà instrumenté des métriques applicatives avec des labels (voir la leçon dédiée) ; elle revient sur ce même code avec un regard critique orienté production.
Une fois le problème identifié en théorie, il faut savoir le diagnostiquer en vrai
Prometheus expose lui-même un endpoint de debug, /api/v1/status/tsdb, qui liste les métriques avec le plus de séries actives. C'est le premier réflexe à avoir face à une consommation mémoire anormale, avant même de chercher dans les logs applicatifs.
Piège fréquent
Le réflexe naturel serait de tracer un utilisateur précis via un label de métrique, pour faciliter le debug. C'est justement l'erreur à ne jamais commettre : les identifiants à haute cardinalité (user_id, request_id, timestamp, adresse IP) doivent toujours être tracés via les LOGS ou les TRACES, jamais via un label de métrique Prometheus.
Il reste une question pratique : que faire d'une métrique déjà mal instrumentée, sans pouvoir redéployer le code immédiatement ?
metric_relabel_configs, appliqué directement dans prometheus.yml, permet de supprimer une métrique entière (action: drop) ou juste un label problématique (action: labeldrop) AVANT que la donnée n'atteigne le stockage. C'est une solution de contournement immédiate, en attendant une correction propre côté code applicatif.
Bonne pratique
Prends l'habitude de revoir la cardinalité de toute nouvelle métrique en code review, exactement comme on revoit la sécurité ou la performance d'un changement. C'est bien moins coûteux de refuser un label dangereux avant fusion que de le corriger en urgence un vendredi soir de production.
Le dernier réflexe pour une stack vraiment prête pour la production
Une checklist explicite — rétention dimensionnée, recording rules pour les requêtes lourdes, for: sur toutes les alertes, cardinalité revue, Alertmanager en cluster, dashboards versionnés en Git, SLO définis par service — transforme le monitoring d'un outil bricolé en système fiable à part entière, digne de confiance même sous pression.
La toute dernière leçon de ce cours rassemble tout ce que tu as appris autour d'un seul concept unificateur, utilisé par les plus grandes équipes SRE au monde : les golden signals.
Commandes & code
Cardinalité, coûts et bonnes pratiques de production
Cardinalité = nombre de combinaisons UNIQUES de labels pour une métrique.
http_requests_total{method, path, status}
method: 4 valeurs (GET, POST, PUT, DELETE)
path: 50 valeurs (routes distinctes)
status: 6 valeurs (200, 201, 400, 404, 500, 503)
-> cardinalité = 4 * 50 * 6 = 1200 séries temporelles pour CETTE seule métrique. Gérable.
http_requests_total{method, path, status, user_id}
user_id: 500 000 valeurs (un utilisateur = une valeur)
-> cardinalité = 4 * 50 * 6 * 500000 = 600 MILLIONS de séries. Catastrophe mémoire garantie.# Diagnostiquer une explosion de cardinalité EN PRODUCTION
topk(10, count by (__name__)({__name__=~".+"})) # les métriques avec le plus de séries
count({__name__="http_requests_total"}) # nombre total de séries pour cette métrique
# Endpoint natif Prometheus pour le debug de cardinalité
curl -s http://localhost:9090/api/v1/status/tsdb | jq '.data.seriesCountByMetricName[:10]'# Erreurs classiques à éviter dans l'instrumentation applicative
from prometheus_client import Counter
# MAUVAIS : user_id, request_id, timestamp en label = cardinalité illimitée
BAD = Counter("requests", "...", ["user_id", "request_id"])
# BON : garder les labels sur des dimensions à cardinalité BORNÉE et connue à l'avance
GOOD = Counter("requests_total", "...", ["method", "status_class"]) # "status_class" = "2xx"/"4xx"/"5xx", pas le code exact
# Pour tracer un utilisateur précis, utiliser les LOGS ou les TRACES, jamais un label de métrique# metric_relabel_configs : DROP des métriques/labels indésirables AVANT stockage (contrôle des coûts)
scrape_configs:
- job_name: "mon_api"
static_configs:
- targets: ["mon-api:8000"]
metric_relabel_configs:
# Supprime complètement une métrique bruyante et peu utile
- source_labels: [__name__]
regex: "go_gc_duration_seconds.*"
action: drop
# Supprime un label à haute cardinalité d'une métrique qu'on veut garder par ailleurs
- regex: "instance_uuid"
action: labeldrop# Downsampling : réduire la résolution des vieilles données (via Thanos Compactor par exemple)
# raw (15s) pendant 7 jours -> 5m pendant 30 jours -> 1h au-delà de 30 jours
# -> queries récentes restent précises, requêtes sur 1 an restent RAPIDES et peu coûteuses en stockageChecklist de production pour une stack de monitoring :
[ ] Rétention Prometheus dimensionnée selon le besoin réel (pas "le plus possible par défaut")
[ ] Recording rules pour toute requête PromQL lourde utilisée dans un dashboard fréquenté
[ ] Alertes testées avec `for:` pour éviter le bruit sur des pics transitoires
[ ] Cardinalité des métriques applicatives revue en code review (pas de user_id/request_id en label)
[ ] Alertmanager en cluster (au moins 2 noeuds) pour éviter un SPOF sur les notifications elles-mêmes
[ ] Dashboards provisionnés en code (Git), jamais construits uniquement à la main dans l'UI
[ ] SLO définis PAR SERVICE, avec error budget suivi et utilisé dans les décisions de déploiementRésumé
- La cardinalité est le facteur numéro un de coût et d'instabilité d'une stack Prometheus : ne jamais mettre
user_id/request_id/timestampen label. metric_relabel_configs(drop/labeldrop) permet de couper à la source les métriques ou labels non maîtrisés, avant qu'ils n'atteignent le stockage.- Le downsampling (Thanos Compactor ou équivalent) garde les données récentes précises tout en gardant l'historique long terme exploitable et abordable.
- Une checklist de production explicite (rétention, recording rules, cardinalité, HA d'Alertmanager) transforme le monitoring d'un "nice to have" en système fiable lui-même.
Exercices pratiques
Mission : le vendredi soir où Prometheus a crashé
Objectif : Diagnostiquer une explosion de cardinalité déjà en cours, corriger l'instrumentation coupable et limiter les dégâts sans redéploiement immédiat.
Contexte
Prometheus vient de crasher par manque de mémoire, un vendredi soir. En creusant, l'équipe soupçonne une métrique checkout_attempts ajoutée récemment, avec un label session_id généré aléatoirement à chaque visite. Le code applicatif responsable ne peut pas être redéployé avant lundi.