Observabilité : la stack LGTM expliquée simplement

Loki, Grafana, Tempo, Mimir : à quoi sert chaque brique, comment elles se branchent, et pourquoi la vraie valeur n'est pas dans les trois piliers mais dans leur corrélation.

Les quatre briques de la stack LGTM : Loki (logs), Grafana (visualisation), Tempo (traces), Mimir (métriques)

Tout le monde parle des « trois piliers de l’observabilité » : métriques, logs, traces. C’est vrai, mais ça cache l’essentiel. Trois silos qui ne se parlent pas, c’est trois fois plus de fatigue à 3h du matin, pas une vision unifiée. La stack LGTM de Grafana existe précisément pour recoudre ces trois piliers en un seul réflexe.

LGTM, c’est quoi ces quatre lettres

Un acronyme, quatre composants open source qui se branchent ensemble :

  • L — Loki : les logs. Pensé comme Prometheus, mais pour les logs : il n’indexe pas le contenu, seulement des labels (app, namespace, level…). Résultat : stockage bon marché sur du object storage (S3, GCS), et requêtes en LogQL.
  • G — Grafana : l’interface. Le point d’entrée unique où tu visualises les trois piliers, construis tes dashboards et déclenches tes alertes.
  • T — Tempo : les traces distribuées. Suit une requête de service en service. Lui non plus n’indexe pas tout : tu retrouves une trace par son trace_id, le stockage reste sur de l’object storage.
  • M — Mimir : les métriques. Un backend Prometheus longue durée, scalable horizontalement, multi-tenant. Tu y envoies tes métriques en remote_write et tu les requêtes en PromQL.

Le fil rouge : les trois backends stockent sur de l’object storage et restent peu coûteux, parce qu’ils indexent le minimum. C’est le pari de Grafana — l’inverse d’Elasticsearch qui indexe tout et te le facture.

Schéma : les applications envoient leurs signaux à Grafana Alloy, qui route les métriques vers Mimir, les logs vers Loki et les traces vers Tempo ; les trois persistent sur de l'object storage et Grafana les requête

Ce qui change vraiment : la corrélation

Si tu retiens une seule chose : la valeur de LGTM n’est pas d’avoir trois outils, c’est de passer de l’un à l’autre en un clic, sans changer d’onglet ni copier-coller un timestamp.

Le scénario concret :

  1. Une alerte saute sur une métrique Mimir : la latence p99 explose.
  2. Grâce aux exemplars, ton point de métrique porte un trace_id. Tu cliques : Grafana t’ouvre la trace Tempo correspondante.
  3. Dans la trace, tu vois quel span est lent. Via les derived fields, tu sautes directement aux logs Loki de ce service, sur la bonne fenêtre de temps.

Métrique → trace → log, en trois clics, sur un seul incident. C’est ça qu’on achète avec LGTM. Sans corrélation, tu fais le même chemin à la main, et tu perds dix minutes à aligner des horloges.

Schéma : une alerte sur la métrique p99 (Mimir) ouvre via un exemplar la trace lente (Tempo), qui mène via un derived field aux logs d'erreur (Loki) — le tout en trois clics

Pour que ça marche, une règle d’or : des labels cohérents partout. Le même app, le même service.name, le même namespace doivent désigner la même chose dans les métriques, les logs et les traces. Sinon la corrélation se casse.

Qui collecte tout ça : Grafana Alloy

Les quatre composants stockent et requêtent, mais ne collectent rien. Il te faut un agent de collecte. Côté Grafana, c’est Alloy (l’héritier de Grafana Agent), compatible OpenTelemetry. Il scrape tes métriques, ramasse tes logs, reçoit tes traces en OTLP, et pousse le tout vers les bons backends.

// Réception des traces OTLP envoyées par tes applis
otelcol.receiver.otlp "default" {
  grpc { endpoint = "0.0.0.0:4317" }
  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}

// Envoi des traces vers Tempo
otelcol.exporter.otlp "tempo" {
  client { endpoint = "tempo:4317" }
}

// Métriques : on scrape puis on remote_write vers Mimir
prometheus.remote_write "mimir" {
  endpoint { url = "http://mimir:9009/api/v1/push" }
}

Si tu préfères rester 100 % vendor-neutral, l’OpenTelemetry Collector fait le même travail. C’est même le choix par défaut que je recommande pour l’instrumentation applicative : tu instrumentes une fois avec les SDK OTel, et tu restes libre de changer de backend plus tard.

Tester en local en cinq minutes

Pas besoin d’un cluster pour se faire une idée. Un docker-compose minimal suffit à voir les trois piliers se parler.

# docker-compose.yml — démo locale, NE PAS utiliser tel quel en prod
services:
  mimir:
    image: grafana/mimir:latest
    command: ["-config.file=/etc/mimir/config.yaml"]
    volumes: ["./mimir.yaml:/etc/mimir/config.yaml"]

  loki:
    image: grafana/loki:latest
    ports: ["3100:3100"]

  tempo:
    image: grafana/tempo:latest
    command: ["-config.file=/etc/tempo/config.yaml"]
    volumes: ["./tempo.yaml:/etc/tempo/config.yaml"]

  grafana:
    image: grafana/grafana:latest
    ports: ["3000:3000"]
    environment:
      # On préprovisionne les data sources pour que la corrélation marche d'emblée
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    depends_on: [mimir, loki, tempo]

Pour aller plus vite encore, le dépôt officiel grafana/intro-to-mltp embarque la stack complète plus une appli déjà instrumentée qui génère métriques, logs et traces corrélés. C’est le meilleur bac à sable pour comprendre la corrélation sans écrire une ligne.

Coût et rétention : là où ça se joue vraiment (le réflexe FinOps)

LGTM est réputé « pas cher », mais ça ne veut pas dire gratuit. Deux leviers à régler dès le départ :

  • La rétention. Tu n’as pas besoin de garder tes traces 90 jours. 7 à 15 jours suffisent dans 90 % des cas — un incident se débugue à chaud. Garde les métriques plus longtemps (elles sont compactes et servent aux tendances), et fais expirer logs et traces plus tôt. C’est là que part la facture object storage.
  • L’échantillonnage des traces. Tracer 100 % du trafic d’un service à fort volume, c’est cher et inutile. Active du sampling (souvent 1 à 10 %), idéalement du tail-sampling qui garde quand même toutes les traces en erreur ou lentes. Tu veux les anomalies, pas le bruit.

Règle de bon sens : commence serré sur la rétention, élargis si un besoin réel apparaît. L’inverse — collecter tout « au cas où » — finit toujours en mauvaise surprise sur la note.

Les pièges classiques

  • Loki n’est pas Elasticsearch. Ne mets pas en label une donnée à forte cardinalité (un user_id, un request_id). Chaque combinaison de labels crée un stream : trop de cardinalité, et Loki s’écroule. Les labels servent à filtrer grossièrement, pas à tout indexer.
  • Pas de labels cohérents = pas de corrélation. Je le répète parce que c’est la cause n°1 d’une stack LGTM décevante. Standardise service.name via OTel dès l’instrumentation.
  • Confondre Mimir et Prometheus. Mimir n’est pas un remplacement de Prometheus pour scraper : c’est le stockage longue durée derrière. Tu gardes un scraper (Prometheus ou Alloy) qui pousse en remote_write.

En résumé

LGTM, ce n’est pas « trois outils de monitoring de plus ». C’est une stack pensée pour que métriques, logs et traces partagent les mêmes labels et se naviguent d’un clic, le tout sur du stockage objet bon marché. Le vrai travail n’est pas d’installer les quatre briques — c’est de soigner ton instrumentation et tes labels pour que la corrélation tienne. Fais ça bien, et ton prochain incident se résout en trois clics au lieu de trois fenêtres.