Guide de déploiement

Grafana Tempo et OpenTelemetry : traçage distribué sur VPS

Déployer sur un VPS Cloud →

Tutoriel

Grafana Tempo et OpenTelemetry : traçage distribué sur VPS

Sécurité & Monitoring12 min de lecture7 étapes

Suivre une requête à travers plusieurs services sans outil de traçage, c'est trier des milliers de lignes de logs à la main. OpenTelemetry et Grafana Tempo forment un duo open-source qui transforme cette investigation en quelques clics. Ce guide vous explique comment déployer la stack complète sur un VPS, instrumenter vos applications et exploiter TraceQL pour diagnostiquer vos lenteurs.

Sommaire· Pourquoi adopter OpenTelemetry plutôt qu'un agent propriétaire1/12
  1. 01Pourquoi adopter OpenTelemetry plutôt qu'un agent propriétaire
  2. 02Ce que la stack OTel + Grafana Tempo vous donne
  3. 03Prérequis VPS
  4. 04Architecture de la stack : du span à la visualisation
  5. 05Déployer la stack OTel + Tempo en sept étapes
  6. 06Configurer la rétention et le stockage des traces
  7. 07Instrumenter une application Node.js
  8. 08Instrumenter une application Python
  9. 09OTel + Tempo vs Jaeger vs Zipkin vs Datadog APM
  10. 10Alertes et SLOs dans Grafana
  11. 11Combiner traces, logs et métriques dans un seul Collector
  12. 12Résoudre les problèmes courants

Pourquoi adopter OpenTelemetry plutôt qu'un agent propriétaire

OpenTelemetry est un projet CNCF graduaté qui unifie les trois piliers de l'observabilité — traces, métriques et logs — sous un standard vendor-neutral. Contrairement aux agents Datadog ou New Relic qui vous enferment dans un contrat et un format propriétaire, l'OTel Collector reçoit, transforme et exporte la télémétrie vers n'importe quel backend compatible. Vous changez de stockage sans retoucher une ligne de votre code applicatif.

Les SDKs couvrent Node.js, Python, Go, Java, PHP, Ruby et la plupart des langages modernes. Les propagateurs de contexte W3C TraceContext et B3 sont inclus : ils transmettent automatiquement le trace-id entre vos microservices via les headers HTTP, sans configuration supplémentaire. Enfin, l'OTel Collector peut tourner en tant qu'agent local sur chaque VM ou en mode gateway centralisé — vous choisissez la topologie qui convient à votre infrastructure.

Ce que la stack OTel + Grafana Tempo vous donne

  • Tracing distribué sans index externe — Grafana Tempo v2.x stocke les traces en blocs compressés (parquet) sur disque local ou objet storage, sans Cassandra ni Elasticsearch.
  • Requêtes TraceQL — le langage dédié de Tempo permet de filtrer les traces par durée, service, statut d'erreur ou attributs personnalisés en quelques secondes.
  • Compatibilité multi-protocoles — Tempo accepte OTLP, Zipkin et Jaeger : vos applications existantes s'y connectent sans ré-instrumentation complète.
  • Stack légère sur VPS — 2 Go de RAM suffisent pour un environnement de dev ou staging ; en production avec charge, 4 Go assurent une marge confortable.
  • Coût maîtrisé — un VPS à 99 DH/mois self-hosted remplace un abonnement APM Datadog facturé environ 23 $ par hôte et par mois.
  • Corrélation traces–logs–métriques — en liant Tempo à Loki et Prometheus dans Grafana, vous passez d'une trace à ses logs et ses métriques en un seul clic.
  • Pas de vendor lock-in — si vous migrez vers Jaeger demain, vous reconfigurez un exporter dans le Collector ; vos SDKs et votre code ne bougent pas.

Prérequis VPS

Votre VPS doit disposer d'au moins 2 Go de RAM (4 Go recommandés en production) et d'un accès root ou sudo. Docker Engine ≥ 24 et Docker Compose v2 doivent être installés.

Prévoyez un sous-domaine dédié — par exemple grafana.votre-domaine.com — pour exposer l'interface Grafana derrière un reverse proxy TLS (nginx ou Caddy). Les ports 4317 (OTLP/gRPC) et 4318 (OTLP/HTTP) doivent être ouverts dans votre pare-feu côté privé pour recevoir la télémétrie depuis vos applications ; ne les exposez pas sur l'interface publique. Le port 3200 de Tempo et le port 3000 de Grafana restent internes au réseau Docker.

Côté stockage, prévoyez au minimum 10 Go libres pour les blocs de traces. La valeur montée par défaut dans /tmp/tempo ne survit pas aux redémarrages : remplacez-la dès la mise en production par un volume Docker ou un montage sur un disque dédié.

Architecture de la stack : du span à la visualisation

Le flux de données suit quatre étapes :

1. Votre application instrumente ses requêtes avec un SDK OTel et exporte des spans vers l'OTel Collector (OTLP gRPC port 4317 ou HTTP port 4318).
2. L'OTel Collector reçoit, met en tampon par batch processor, puis exporte vers Tempo via OTLP gRPC interne. Il peut simultanément envoyer les métriques vers Prometheus et les logs vers Loki.
3. Grafana Tempo stocke les blocs de traces compressés sur disque (ou S3). Il expose une API TraceQL sur le port 3200 pour les requêtes de Grafana.
4. Grafana interroge Tempo avec TraceQL, affiche les flamegraphs, les service graphs et les span metrics. Si Tempo est lié à Loki, un clic sur un span ouvre les logs du service concerné sur la même fenêtre temporelle.

TraceQL est le langage de requête natif de Tempo. Une requête typique ressemble à : {service.name="api" && status=error && duration>500ms}. Vous filtrez par attribut de ressource, attribut de span, statut ou durée — et vous obtenez immédiatement les traces qui franchissent un seuil de latence.

Les Span Metrics sont un pipeline Tempo qui dérive automatiquement des métriques RED (Rate, Error, Duration) à partir des traces entrantes, sans que vous n'ajoutiez un seul compteur dans votre code. Ces métriques peuvent être scrappées par Prometheus et affichées dans des tableaux de bord Grafana standard.

Déployer la stack OTel + Tempo en sept étapes

  1. Créer la structure de répertoires

    Organisez un dossier otel-stack/ sur votre VPS :

    mkdir -p otel-stack/config
    cd otel-stack

    Vous y placerez trois fichiers de configuration : tempo.yaml, otel-collector-config.yaml et docker-compose.yml.

  2. Écrire la configuration Tempo (tempo.yaml)

    Créez config/tempo.yaml avec le backend de stockage local et le support de TraceQL :

    server:
      http_listen_port: 3200
    
    distributor:
      receivers:
        otlp:
          protocols:
            grpc:
              endpoint: 0.0.0.0:4317
    
    ingester:
      max_block_bytes: 1_000_000
      max_block_duration: 5m
    
    compactor:
      compaction:
        block_retention: 168h   # 7 jours
    
    storage:
      trace:
        backend: local
        wal:
          path: /tmp/tempo/wal
        local:
          path: /tmp/tempo/blocks

    Ajustez block_retention selon la durée de rétention souhaitée (voir la section dédiée plus bas).

  3. Écrire la configuration de l'OTel Collector

    Créez config/otel-collector-config.yaml :

    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    
    processors:
      batch:
        timeout: 5s
        send_batch_size: 1000
    
    exporters:
      otlp:
        endpoint: tempo:4317
        tls:
          insecure: true
    
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [batch]
          exporters: [otlp]

    Pour ajouter les métriques vers Prometheus et les logs vers Loki, ajoutez les receivers et exporters correspondants dans les pipelines metrics et logs.

  4. Écrire le docker-compose.yml

    Créez docker-compose.yml à la racine de otel-stack/ :

    services:
      otel-collector:
        image: otel/opentelemetry-collector-contrib:latest
        volumes:
          - ./config/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
        ports:
          - "4317:4317"
          - "4318:4318"
        networks:
          - observability
        depends_on:
          - tempo
    
      tempo:
        image: grafana/tempo:latest
        command: ["-config.file=/etc/tempo.yaml"]
        volumes:
          - ./config/tempo.yaml:/etc/tempo.yaml
          - tempo-data:/tmp/tempo
        ports:
          - "3200:3200"
        networks:
          - observability
    
      grafana:
        image: grafana/grafana:latest
        ports:
          - "3000:3000"
        environment:
          - GF_AUTH_ANONYMOUS_ENABLED=true
          - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
        networks:
          - observability
        depends_on:
          - tempo
    
    volumes:
      tempo-data:
    
    networks:
      observability:

    Lancez la stack : docker compose up -d puis vérifiez que les trois services sont healthy avec docker compose ps.

  5. Configurer Grafana Tempo comme datasource

    Ouvrez Grafana sur http://votre-vps:3000. Naviguez vers Connections → Data sources → Add data source et sélectionnez Tempo. Renseignez l'URL http://tempo:3200. Activez Service graph et Span metrics pour enrichir la visualisation avec des graphiques de dépendances et des métriques RED calculées automatiquement. Si votre stack inclut Loki, liez les deux datasources dans le champ Linked data sources : vous pourrez naviguer d'un span à ses logs en un clic.

  6. Envoyer votre première trace de test

    Testez que la stack reçoit bien des traces avec l'outil telemetrygen :

    docker run --rm --network otel-stack_observability \
      ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
      traces \
      --otlp-endpoint otel-collector:4317 \
      --otlp-insecure \
      --duration 5s \
      --rate 10

    Dans Grafana, ouvrez Explore, sélectionnez la datasource Tempo et lancez la requête {} pour voir toutes les traces reçues. Si aucune trace n'apparaît, vérifiez les logs du Collector : docker compose logs otel-collector.

  7. Requêter vos traces avec TraceQL

    Dans le panneau Explore de Grafana (datasource Tempo), saisissez une requête TraceQL pour filtrer vos traces. Exemples utiles :

    - {service.name="mon-api"} — toutes les traces du service mon-api
    - {status=error} — toutes les traces en erreur
    - {duration>1s && service.name="mon-api"} — traces lentes de plus d'une seconde
    - {span.http.route="/api/orders" && status=error} — erreurs sur une route précise

    Cliquez sur une trace pour ouvrir le flamegraph détaillé : chaque span affiche sa durée, ses attributs et les erreurs éventuelles.

Configurer la rétention et le stockage des traces

Par défaut, Tempo stocke les blocs localement dans /tmp/tempo. Pour une installation durable, deux options s'offrent à vous.

Stockage local sur volume Docker : c'est la configuration recommandée pour un VPS standalone. Le volume tempo-data défini dans le docker-compose.yml ci-dessus persiste les données entre les redémarrages. Ajustez block_retention dans tempo.yaml selon votre besoin : 72 h pour un environnement de staging, 168 h (7 jours) à 720 h (30 jours) pour la production. Chaque heure de trace compressée occupe environ 100–300 Mo selon le volume de spans.

Stockage objet S3-compatible : pour une stack multi-nœuds ou un stockage sans plafond, remplacez le backend local par un backend s3 dans tempo.yaml :

storage:
  trace:
    backend: s3
    s3:
      bucket: mon-bucket-traces
      endpoint: s3.amazonaws.com
      region: eu-west-1
      access_key: ${S3_ACCESS_KEY}
      secret_key: ${S3_SECRET_KEY}
    wal:
      path: /tmp/tempo/wal

Tempo est compatible avec AWS S3, MinIO, Scaleway Object Storage et tout backend qui implémente l'API S3. La WAL (Write-Ahead Log) reste toujours sur disque local pour garantir la durabilité des traces en cours d'ingestion.

Le compacteur de Tempo fusionne les petits blocs et applique la politique de rétention. Il tourne en arrière-plan et ne nécessite aucune intervention manuelle. Surveillez la taille du dossier blocks/ avec docker exec tempo du -sh /tmp/tempo/blocks pour anticiper un éventuel manque d'espace.

Instrumenter une application Node.js

L'auto-instrumentation OTel pour Node.js capture automatiquement les appels HTTP sortants, les requêtes Express/Fastify, les requêtes de base de données et bien d'autres bibliothèques sans modifier votre code métier.

Installez les packages nécessaires :

npm install @opentelemetry/sdk-node \
             @opentelemetry/auto-instrumentations-node \
             @opentelemetry/exporter-trace-otlp-grpc

Créez un fichier tracing.js à charger en premier :

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'grpc://localhost:4317',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

Démarrez votre application en chargeant ce fichier en premier :

export OTEL_SERVICE_NAME=mon-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://votre-vps:4317
node --require ./tracing.js app.js

Pour les environnements Docker, passez les variables OTEL_SERVICE_NAME et OTEL_EXPORTER_OTLP_ENDPOINT dans votre docker-compose.yml ou votre manifest Kubernetes. Tous les appels HTTP entrants et sortants seront automatiquement tracés et corrélés par le propagateur W3C TraceContext.

Instrumenter une application Python

Le package opentelemetry-distro fournit un mécanisme de bootstrap qui installe automatiquement tous les packages d'instrumentation pour les bibliothèques détectées dans votre environnement.

Installez et bootstrappez :

pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

Lancez votre application en la préfixant avec opentelemetry-instrument :

export OTEL_SERVICE_NAME=mon-service-python
export OTEL_EXPORTER_OTLP_ENDPOINT=http://votre-vps:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
opentelemetry-instrument python app.py

Les frameworks Django, Flask, FastAPI, SQLAlchemy et les clients HTTP (requests, httpx, aiohttp) sont instrumentés automatiquement. Pour un déploiement en conteneur, définissez les variables OTEL_* dans votre fichier d'environnement et assurez-vous que le port 4317 de l'OTel Collector est accessible depuis le réseau de vos conteneurs.

Si vous préférez une instrumentation manuelle pour un contrôle plus fin, utilisez directement l'API OTel :

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("traitement-commande") as span:
    span.set_attribute("commande.id", commande_id)
    # votre logique métier

OTel + Tempo vs Jaeger vs Zipkin vs Datadog APM

Faites défiler le tableau

CritèreOTel + Grafana TempoJaegerDatadog APM
Coût mensuelHébergement seul (99 DH/mois VPS)Hébergement seul (similaire)~23 $ par hôte par mois
Stockage backendLocal, S3, GCS, Azure BlobCassandra, Elasticsearch, BadgerSaaS managé (opaque)
Langage de requêteTraceQL (natif, puissant)Jaeger Query UI (basique)APM Query Language (propriétaire)
Auto-instrumentationOTel SDK universelBibliothèques Jaeger ou OTelAgent Datadog propriétaire
Corrélation traces–logsOui (Loki + Grafana)LimitéeOui (intégré, payant)
Span Metrics (RED)Oui (pipeline Tempo natif)Non natifOui (calculé côté Datadog)
Vendor lock-inAucun (standard CNCF)Faible (compatible OTel)Fort (agent et format propriétaires)
Complexité d'installationMoyenne (3 conteneurs)Faible à moyenneFaible (agent auto)

Alertes et SLOs dans Grafana

Grafana 10+ permet de définir des alertes directement sur les métriques dérivées des traces. Les Span Metrics de Tempo génèrent automatiquement trois métriques par service : traces_spanmetrics_calls_total (taux de requêtes), traces_spanmetrics_latency_bucket (histogramme de latence) et leur compteur d'erreurs associé.

Pour créer un SLO sur le taux d'erreur de votre API :
1. Dans Grafana, allez dans Alerting → Alert rules → New alert rule.
2. Choisissez la datasource Prometheus (ou Tempo via Span Metrics) et saisissez une requête comme rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR", service="mon-api"}[5m]) / rate(traces_spanmetrics_calls_total{service="mon-api"}[5m]).
3. Définissez le seuil d'alerte (par exemple > 0.05 pour un taux d'erreur supérieur à 5 %) et la fenêtre d'évaluation.
4. Configurez un contact point : Alertmanager, PagerDuty, Slack ou webhook — tout s'intègre depuis Alerting → Contact points.

Pour le P99 de latence, interrogez histogram_quantile(0.99, rate(traces_spanmetrics_latency_bucket{service="mon-api"}[5m])) et déclenchez une alerte si la valeur dépasse votre budget d'erreur (par exemple 2 secondes). Ces alertes basées sur des traces sont bien plus précises que des alertes sur des métriques synthétiques, car elles reflètent le vécu réel de chaque requête.

Combiner traces, logs et métriques dans un seul Collector

L'OTel Collector n'est pas réservé aux traces. En ajoutant un receiver filelog dans votre configuration, vous collectez les logs de vos conteneurs Docker et les expédiez vers Loki. Un receiver prometheus scrape vos endpoints /metrics et les exporte vers Mimir ou Prometheus. Un seul processus gère ainsi l'intégralité de la télémétrie de votre VPS.

Dans Grafana, liez Tempo et Loki via des derived fields : quand un log contient un trace_id, un lien cliquable vous amène directement au flamegraph correspondant. À l'inverse, depuis une trace Tempo, un lien vous ouvre les logs Loki du service et de la fenêtre temporelle associés. Cette navigation croisée réduit considérablement le temps de diagnostic : vous n'avez plus à corréler manuellement des timestamps entre trois onglets distincts.

Résoudre les problèmes courants

Aucun span n'arrive dans Tempo. Vérifiez d'abord que l'OTel Collector est joignable depuis votre application (telnet votre-vps 4317). Consultez les logs du Collector (docker compose logs otel-collector) : une erreur connection refused sur tempo:4317 indique que Tempo n'est pas encore démarré ou que son port interne n'est pas exposé sur le réseau Docker. Assurez-vous que les deux services sont sur le même réseau (observability).

Connexion refusée depuis l'application vers le Collector. Sur un pare-feu UFW ou firewalld, autorisez les ports 4317 et 4318 uniquement pour les IP de vos applications, pas pour l'ensemble d'Internet. En production, les applications et le Collector doivent être sur le même réseau privé ; n'exposez jamais le Collector sur l'interface publique sans authentification.

Traces tronquées ou spans manquants. Le batch processor du Collector regroupe les spans avant de les envoyer. Si votre application envoie peu de spans, les traces peuvent mettre jusqu'à timeout secondes (5 s par défaut) pour apparaître dans Grafana. Pour le développement, réduisez timeout: 1s dans le batch processor. Si des spans sont systématiquement absents, vérifiez que le propagateur de contexte est activé dans votre SDK (W3CTraceContextPropagator) — sans lui, les appels entre services génèrent des traces orphelines.

Tempo consomme trop d'espace disque. Réduisez block_retention dans tempo.yaml et relancez Tempo. Le compacteur appliquera la nouvelle politique au prochain cycle (toutes les 5 minutes par défaut). En cas d'urgence, supprimez manuellement les anciens blocs dans /tmp/tempo/blocks tout en laissant la WAL intacte.

Grafana ne trouve pas les traces depuis les logs Loki. Vérifiez que vos applications injectent bien le trace_id dans leurs logs (format structuré JSON recommandé) et que le champ Derived fields dans la datasource Loki pointe vers le bon pattern ("traceId": "(\w+)"). L'URL du lien doit pointer vers la datasource Tempo avec le bon identifiant de datasource Grafana.

Grafana est dans le catalogue ServOrbit

Déployez Grafana en un clic depuis notre Marketplace et connectez-y votre stack OTel + Tempo sans configuration manuelle.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou contactez notre équipe — rappel, WhatsApp ou e-mail. Support en français, anglais et arabe.

Écrire sur WhatsApps'ouvre dans un nouvel onglet