← Tous les cours / Pipeline de métriques Kubernetes — Prometheus, Thanos & Grafana Cours
00 — LE PROBLÈME

Métriques à l'échelle — pourquoi Prometheus seul ne suffit pas

100 microservices × 50 métriques × 15 secondes = 90 milliards de points sur six mois. Un disque de pod ne peut pas tenir ça.

Tu as 100 microservices qui tournent dans ton cluster Kubernetes. Chacun d'eux expose un endpoint /metrics : une page de texte brut que Prometheus lit toutes les 15 secondes pour savoir comment va le service.

# Ce qu'un endpoint /metrics retourne
http_requests_total{method="GET", status="200"} 4821
http_request_duration_seconds_bucket{le="0.1"} 3987
process_resident_memory_bytes 52428800

Prometheus lit ces valeurs, les horodate et les stocke dans sa base de données interne. Jusqu'ici, ça tient la route. Mais maintenant imagine que tu veux garder ces données six mois pour faire du capacity planning.

Le problème de l'échelle
100 services × 50 métriques × 1 scrape toutes les 15 secondes = 340 000 points par minute. Sur six mois, c'est environ 90 milliards de points. Aucun disque de pod Kubernetes ne peut tenir ça — et même s'il pouvait, tu perdrais tout si le pod redémarre.

Ce cours conçoit la solution étape par étape. Voici les composants que tu vas apprendre à assembler :

Services K8s
/metrics
Prometheus
scrape 15s
Thanos
Sidecar · Query · Store · Compactor
S3
long terme
💡 Prérequis : ce cours suppose que tu sais ce qu'est un pod Kubernetes, que tu as déjà vu une ligne de PromQL, et que tu comprends à quoi sert Prometheus à haut niveau. Si ce n'est pas le cas, commence par le cours Observabilité II — Prometheus & métriques.

Simulation du scraping Prometheus : plusieurs services génèrent des métriques. Regarde le disque du pod se remplir — et les anciens blocs disparaître quand la rétention est dépassée.

01 — BLOCS PROMETHEUS

Comment Prometheus stocke les données — samples, chunks et blocs

Prometheus ne stocke pas les données ligne par ligne. Il les groupe en blocs de 2h sur disque — et le bloc scellé est l'unité de tout le reste.

Pour comprendre pourquoi Prometheus seul ne suffit pas, tu dois comprendre comment il stocke les données. Ce n'est pas une base de données classique avec des lignes et des colonnes. C'est une base de données de séries temporelles, organisée en blocs.

Du sample au bloc

Chaque valeur collectée (un sample) contient deux choses : un timestamp Unix et une valeur numérique. Prometheus les accumule d'abord en mémoire dans des structures appelées chunks, puis les écrit sur disque dans des blocs — chacun correspondant à une fenêtre de temps (2 heures par défaut).

Sample
ts + valeur
Chunk
en mémoire
Bloc (2h)
sur disque

Un bloc sur disque ressemble à ceci :

/prometheus/data/
  01BX6V8YMJT23VB/       ← répertoire du bloc (ULID)
    chunks/
      000001              ← données compressées des séries
    index                 ← index des labels pour les requêtes
    meta.json             ← intervalle de temps, stats
    tombstones            ← marqueurs de suppression

La fenêtre de rétention

Prometheus supprime automatiquement les blocs plus vieux que sa fenêtre de rétention. La valeur par défaut est 15 jours, configurable avec le flag --storage.tsdb.retention.time=15d.

Le problème : une fois un bloc supprimé, ces données sont perdues à jamais. Augmenter la rétention consomme plus de disque, mais ne résout pas le fond du problème — un pod Kubernetes avec un volume de quelques dizaines de GB ne pourra jamais stocker des années de métriques.

Un bloc est "scellé" (sealed) quand il est complet
Pendant les deux premières heures, le bloc actif est en cours d'écriture. Quand la fenêtre de 2h se ferme, le bloc est scellé — plus aucune donnée n'y sera ajoutée. C'est précisément à ce moment que Thanos Sidecar peut l'attraper et l'envoyer vers S3.
🔑 Pourquoi 2h et pas autre chose ? Avec Thanos, on configure Prometheus avec --storage.tsdb.min-block-duration=2h et --storage.tsdb.max-block-duration=2h pour forcer des blocs de taille fixe. Cela garantit que le Sidecar reçoit des blocs complets et immuables à intervalles réguliers.

Blocs qui s'accumulent sur le disque Prometheus. Quand le disque dépasse la rétention, les plus anciens sont supprimés. Ajuste la rétention pour voir l'effet.

02 — THANOS SIDECAR

Thanos Sidecar — archiver les blocs dans S3

Un container qui tourne aux côtés de Prometheus, attrape les blocs scellés et les uploade vers S3. La rétention courte devient un choix, pas une contrainte.

La solution au problème de rétention est simple en principe : envoyer les blocs scellés vers un stockage objet bon marché et durable comme Amazon S3. En pratique, Prometheus n'a aucune connaissance de S3. C'est le rôle du Thanos Sidecar.

Le pattern sidecar dans Kubernetes
Un sidecar est un deuxième container qui tourne dans le même pod qu'une application principale. Il partage le même réseau, le même système de fichiers monté, mais a son propre binaire. Le Thanos Sidecar tourne aux côtés de Prometheus et accède à son répertoire de données via un volume partagé.

Voici à quoi ressemble la configuration dans un StatefulSet :

# Extrait du StatefulSet Prometheus
containers:
  - name: prometheus
    image: prom/prometheus:v2.51.0
    args:
      - --storage.tsdb.path=/prometheus
      - --storage.tsdb.min-block-duration=2h  # blocs fixes pour Thanos
      - --storage.tsdb.max-block-duration=2h
      - --storage.tsdb.retention.time=12h   # court : Thanos prend le relais
    volumeMounts:
      - name: prometheus-data
        mountPath: /prometheus

  - name: thanos-sidecar               ← le sidecar
    image: thanosio/thanos:v0.35.0
    args:
      - sidecar
      - --prometheus.url=http://localhost:9090
      - --tsdb.path=/prometheus         ← même volume que Prometheus
      - --objstore.config-file=/etc/thanos/objstore.yaml
    volumeMounts:
      - name: prometheus-data
        mountPath: /prometheus          ← volume partagé
      - name: thanos-objstore
        mountPath: /etc/thanos

La configuration du bucket S3 dans objstore.yaml :

type: S3
config:
  bucket: my-thanos-metrics
  endpoint: s3.eu-west-1.amazonaws.com
  region: eu-west-1

Ce que fait le Sidecar

Le Sidecar surveille le répertoire de données Prometheus. Quand un nouveau bloc scellé (complet, immuable) apparaît, il l'uploade vers S3. À partir de ce moment, les données existent à deux endroits : les données récentes (< 12h dans cet exemple) restent sur le pod, toutes les autres sont dans S3.

💡 La rétention courte n'est pas un problème. Avec 12h de rétention sur Prometheus, les données fraîches sont toujours disponibles pour Grafana. Et dès qu'un bloc est uploadé dans S3, il ne sera plus jamais perdu — même si le pod Prometheus redémarre.

Cycle de vie d'un bloc : écriture par Prometheus → scellage → upload par le Sidecar vers S3. Les blocs verts sont disponibles localement, les bleus sont archivés dans S3.

03 — HAUTE DISPONIBILITÉ

Haute disponibilité — deux répliques Prometheus

Un seul Prometheus, c'est un single point of failure. Deux répliques qui scrape tout en parallèle éliminent ce risque — la déduplication viendra après.

Un seul pod Prometheus, c'est un single point of failure. Si ce pod redémarre — pour une mise à jour, un node qui tombe, n'importe quelle raison — les données des dernières minutes (celles pas encore scellées et uploadées) sont perdues.

La solution est simple : faire tourner deux répliques Prometheus qui scrape les mêmes services en parallèle. Si l'une tombe, l'autre continue.

Deux Prometheus + deux Sidecars
Chaque réplique scrape tous les services indépendamment. Chacune a son propre Sidecar qui uploade vers le même bucket S3. En cas de panne d'une réplique, l'autre conserve les données récentes et le Sidecar continue d'archiver. Les données ne sont pas perdues.
# StatefulSet avec deux répliques
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: prometheus
spec:
  replicas: 2            ← deux pods
  selector:
    matchLabels:
      app: prometheus
  template:
    spec:
      containers:
        - name: prometheus
          args:
            - --storage.tsdb.path=/prometheus
            - --web.enable-lifecycle
            - --storage.tsdb.retention.time=12h
        - name: thanos-sidecar
          args:
            - sidecar
            - --prometheus.url=http://localhost:9090
            - --tsdb.path=/prometheus
            - --objstore.config-file=/etc/thanos/objstore.yaml

Le problème de la duplication

Deux Prometheus qui scrape les mêmes services produisent des données en double dans S3. Si tu interroges directement les deux, tu obtiens deux fois les mêmes valeurs pour chaque métrique.

Thanos Query résoudra ce problème plus loin dans le cours via la déduplication. Pour l'instant, sache qu'on ajoute un label prometheus_replica à chaque instance pour qu'elles soient distinguables :

containers:
  - name: prometheus
    args:
      - --web.enable-lifecycle
      # Pod 0 :
      - --storage.tsdb.path=/prometheus
    env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
  - name: thanos-sidecar
    args:
      - sidecar
      - --prometheus.labels=prometheus_replica=$(POD_NAME)
🔑 Les deux répliques scrape tout. Ce n'est pas une partition du travail — c'est une copie complète. Le coût : deux fois plus de trafic vers tes services sur /metrics, et deux fois plus de données dans S3. En pratique, sur des petits services, ce surcoût est négligeable.

Deux répliques Prometheus. Clique sur Kill Pod pour simuler une panne — la deuxième réplique continue de scraper et de tenir les données récentes.

04 — STORE GATEWAY

Store Gateway — rendre S3 interrogeable

S3 stocke les fichiers mais ne sait pas lire du PromQL. Le Store Gateway fait le pont : il indexe les blocs Thanos et répond aux requêtes historiques.

Les données historiques sont maintenant dans S3. Mais S3 est un stockage objet muet : il conserve des fichiers, mais il ne sait pas interpréter leur contenu. Si tu lui demandes "quelles étaient les requêtes par seconde il y a trois mois ?", il ne peut pas répondre.

Pour rendre les blocs Thanos dans S3 interrogeables, on ajoute le Store Gateway.

Store Gateway
Le Store Gateway est un composant Thanos qui s'installe entre ton cluster et S3. Il lit l'index de chaque bloc dans S3, charge les métadonnées en mémoire (pas les données entières), et est capable de répondre à des requêtes PromQL en allant chercher les données pertinentes dans S3 à la demande.
# Déploiement du Store Gateway
apiVersion: apps/v1
kind: Deployment
metadata:
  name: thanos-store
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: thanos-store
          image: thanosio/thanos:v0.35.0
          args:
            - store
            - --objstore.config-file=/etc/thanos/objstore.yaml
            - --data-dir=/data         ← cache local des index (pas les blocs entiers)
            - --grpc-address=0.0.0.0:10901
          volumeMounts:
            - name: thanos-objstore
              mountPath: /etc/thanos

Le Store Gateway expose une interface gRPC que Thanos Query appelle pour lire les données historiques. Il ne charge les données S3 qu'au moment où une requête l'exige — pas tout en mémoire en permanence.

Ce qu'il ne fait pas

Le Store Gateway ne répond pas aux requêtes directement depuis Grafana ou Prometheus. Son seul client est Thanos Query. Il s'occupe exclusivement de la couche d'accès aux données historiques.

💡 Rétention du cache local : Le Store Gateway maintient un cache d'index sur son disque local (--data-dir). Ce cache accélère les requêtes en évitant de re-télécharger les index depuis S3 à chaque fois. Il n'a pas besoin d'être énorme — quelques GB suffisent pour la plupart des setups.

Une requête arrive sur des données historiques. Observe ce qui se passe avec et sans Store Gateway.

05 — THANOS QUERY

Thanos Query — la porte d'entrée unique

Fan-out vers les Sidecars + Store Gateway, fusion récent/historique, déduplication des répliques. Pour Grafana, c'est un Prometheus ordinaire.

Les données récentes se trouvent dans les Sidecars des deux répliques Prometheus. Les données historiques se trouvent dans le Store Gateway. Et personne ne devrait avoir à interroger chacun séparément, puis assembler les résultats à la main.

C'est le rôle de Thanos Query : la porte d'entrée unique qui reçoit toutes les requêtes PromQL et se charge de les fan-outer vers les bons composants.

Thanos Query — ce qu'il fait
(1) Il reçoit une requête PromQL. (2) Il l'envoie en parallèle à tous ses "stores" : les Sidecars des deux Prometheus + le Store Gateway. (3) Il fusionne les résultats en une seule timeline continue. (4) Il déduplique les données identiques venant des deux répliques Prometheus.
# Déploiement de Thanos Query
apiVersion: apps/v1
kind: Deployment
metadata:
  name: thanos-query
spec:
  template:
    spec:
      containers:
        - name: thanos-query
          image: thanosio/thanos:v0.35.0
          args:
            - query
            - --grpc-address=0.0.0.0:10901
            - --http-address=0.0.0.0:9090
            # Les Sidecars Prometheus
            - --store=prometheus-0.prometheus:10901
            - --store=prometheus-1.prometheus:10901
            # Le Store Gateway
            - --store=thanos-store:10901
            # Label utilisé pour la déduplication des répliques
            - --query.replica-label=prometheus_replica

La déduplication

Sans déduplication, une requête sur http_requests_total retournerait deux séries identiques — une par réplique. --query.replica-label=prometheus_replica indique à Thanos Query quel label distingue les répliques. Il garde alors le sample le plus récent disponible entre les deux et présente une seule série propre.

🔑 Thanos Query expose l'API Prometheus. Il répond sur le port 9090 avec la même API HTTP que Prometheus. Tu peux donc pointer Grafana directement sur Thanos Query sans changer ta config PromQL. Pour Grafana, c'est transparent.

Thanos Query reçoit une requête et la distribue à tous les composants. Observe le fan-out et la déduplication.

06 — GRAFANA

Grafana — une seule datasource, tout l'historique

Grafana pointe sur Thanos Query. Une requête PromQL donne accès à 6 mois de métriques, quelle que soit la plage de temps choisie.

Grafana est l'interface de visualisation. Il ne stocke aucune donnée lui-même — il envoie des requêtes PromQL à une datasource et affiche les résultats sous forme de graphes, jauges, tableaux.

Avec Thanos en place, la configuration de Grafana est simple : une seule datasource, pointée sur Thanos Query. Grafana ne sait pas qu'il y a deux Prometheus, un Store Gateway et un bucket S3 derrière. Pour lui, c'est un seul endpoint qui répond à du PromQL.

# Datasource Grafana (provisioning via ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
data:
  datasources.yaml: |
    apiVersion: 1
    datasources:
      - name: Thanos
        type: prometheus
        url: http://thanos-query:9090   ← Thanos Query, pas Prometheus
        isDefault: true
        jsonData:
          timeInterval: "15s"
          queryTimeout: "60s"

Grafana envoie des requêtes PromQL en précisant une plage de temps. Thanos Query détermine automatiquement quels composants consulter en fonction de cette plage : Sidecars pour les données récentes, Store Gateway pour les données historiques.

Quelques requêtes utiles

# Taux de requêtes par service (dernier 5 minutes)
sum by (service) (rate(http_requests_total[5m]))

# Latence p99 (percentile 99%)
histogram_quantile(0.99,
  sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))
)

# Utilisation mémoire de tous les pods
sum by (pod) (container_memory_working_set_bytes{namespace="production"})
💡 Grafana et la longue durée. Quand tu demandes "les 6 derniers mois", Grafana envoie la même requête PromQL à Thanos Query. Thanos Query consulte le Store Gateway qui lit les données downsamplées dans S3 (on en parle dans la section suivante). Le résultat revient en quelques secondes, même sur 6 mois de données.

Sélectionne une plage de temps — observe quels composants Thanos Query consulte en réponse.

07 — COMPACTEUR

Compacteur — garder S3 sain sur la durée

Fusion de petits blocs, downsampling (15s → 5min → 1h), application de la rétention longue. Une seule instance, toujours.

S3 a de la place en quasi-illimité et pour pas cher. Mais sans maintenance, le bucket finit par contenir des milliers de petits blocs de 2h, ce qui rend les requêtes longue durée lentes et le bucket difficile à gérer. Le Compacteur s'occupe de ça.

Le Compacteur Thanos — trois rôles
(1) Compaction : fusionne les petits blocs de 2h en blocs de 48h, puis de semaines. Moins de fichiers dans S3, requêtes plus efficaces. (2) Downsampling : crée des versions basse résolution des données (1 point / 5 min, puis 1 point / 1h). (3) Rétention : supprime les données plus vieilles que la fenêtre configurée.

La compaction

Sans compaction, après 30 jours tu as 360 blocs de 2h dans S3. Une requête sur "les 30 derniers jours" doit ouvrir et lire chacun d'eux. Avec compaction, ces 360 blocs deviennent quelques dizaines de blocs plus grands, et la requête est beaucoup plus rapide.

Le downsampling

Quand tu regardes "les 6 derniers mois" dans Grafana, tu n'as pas besoin de voir un point toutes les 15 secondes — un point toutes les heures suffit pour avoir une courbe lisible. Le Compacteur crée automatiquement des versions basse résolution :

RésolutionGranularitéQuand utilisée
Raw1 point / 15sDerniers jours (haute précision)
5min1 point / 5 minDernières semaines
1h1 point / 1hDerniers mois
# Déploiement du Compacteur (Job ou Deployment)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: thanos-compactor
spec:
  replicas: 1            ← TOUJOURS une seule instance (accès exclusif au bucket)
  template:
    spec:
      containers:
        - name: thanos-compactor
          image: thanosio/thanos:v0.35.0
          args:
            - compact
            - --wait                    ← tourne en continu
            - --objstore.config-file=/etc/thanos/objstore.yaml
            - --data-dir=/data
            # Rétention par résolution
            - --retention.resolution-raw=30d
            - --retention.resolution-5m=90d
            - --retention.resolution-1h=365d
⚠️ Une seule instance de Compacteur. Le Compacteur modifie les blocs dans S3 (fusions, suppressions). Deux instances concurrentes corromprait les données. Ne mets jamais replicas: 2 sur ce déploiement.

Blocs qui s'accumulent dans S3 sur la durée. Le Compacteur les fusionne régulièrement — observe la réduction du nombre de blocs et l'apparition des versions downsamplées.

08 — SYNTHÈSE

Architecture complète — tout assembler

Vue d'ensemble du pipeline, rôle de chaque composant, points de vigilance en production.

Voici l'architecture complète telle qu'elle tourne en production. Chaque composant a un rôle précis, et ensemble ils résolvent un problème que Prometheus seul ne peut pas résoudre.

Services K8s
/metrics × 100
Prometheus ×2
scrape 15s · rétention 12h
Thanos Sidecar ×2
blocs scellés → S3
S3
stockage long terme
Grafana
PromQL dashboard
Thanos Query
fan-out · dédup
Sidecars
données récentes
placeholder
placeholder
Store Gateway
données S3
Compacteur
fusion · downsample · rétention
S3
blocs compactés

Rôle de chaque composant

ComposantRôleOù tourne
Prometheus ×2Scrape, stockage court terme (12h)StatefulSet, 2 répliques
Thanos Sidecar ×2Upload des blocs scellés vers S3Container dans le pod Prometheus
Thanos Store GatewayRend S3 queryable via gRPCDeployment, 1+ répliques
Thanos QueryFan-out, fusion, déduplicationDeployment, scalable horizontalement
Thanos CompacteurFusionne blocs, downsample, rétentionDeployment, 1 seule instance
GrafanaVisualisation PromQLDeployment, datasource → Thanos Query
Amazon S3Stockage objet des blocs ThanosService AWS managé

Points de vigilance

⚠️ Compacteur : exactement une instance. Deux Compacteurs en parallèle sur le même bucket S3 corrompent les blocs. Ce composant n'est pas scalable horizontalement.
⚠️ Rétention Prometheus courte ≠ perte de données. Configurer Prometheus à 12h de rétention est intentionnel : le Sidecar a déjà uploadé tout ce qui est plus vieux vers S3. Ne pas comprendre ça, c'est augmenter inutilement la rétention Prometheus et dupliquer les données.
💡 Alternative managée : Cortex, Mimir, Grafana Cloud. Thanos est la solution open source que tu opères toi-même. Si tu ne veux pas gérer les Sidecars, Store Gateway et Compacteur, Grafana Mimir ou Grafana Cloud proposent un équivalent managé avec une API compatible Prometheus.