TL;DR — Résumé Rapide

OpenTelemetry unifie traces, métriques et logs sous un standard neutre. Maîtrisez le Collector OTel, l'instrumentation automatique et les backends Grafana.

OpenTelemetry (OTel) est le standard CNCF open-source qui unifie la collecte de traces, métriques et logs sous une API, un SDK et un protocole réseau neutres vis-à-vis du fournisseur. Avant OTel, chaque éditeur d’observabilité — Datadog, New Relic, Dynatrace, Elastic — nécessitait son propre agent propriétaire, obligeant les équipes à instrumenter le code plusieurs fois. OTel brise cette dépendance : instrumentez une fois, envoyez vers n’importe quel backend. Ce guide couvre l’architecture complète d’OTel, la configuration du Collector, l’instrumentation automatique et manuelle, le tracing distribué, les patterns de déploiement Kubernetes et un exemple pratique de microservices Node.js + Python avec Grafana.

Prérequis

  • Familiarité avec Docker et Kubernetes basique (pour les sections Kubernetes)
  • Une application cible en Node.js, Python, Java, .NET ou Go
  • Docker Compose ou un cluster Kubernetes pour l’exemple pratique
  • Compréhension de base de ce que sont les traces, métriques et logs conceptuellement
  • kubectl configuré si vous utilisez les sections de l’opérateur Kubernetes

Architecture d’OpenTelemetry

OTel est organisé en trois couches clairement séparées :

API — Interfaces spécifiques au langage (ex. : tracer.startSpan()). C’est ce qu’appelle le code de votre application. L’API seule ne fait rien ; elle a besoin d’une implémentation SDK injectée à l’exécution.

SDK — L’implémentation de l’API. Le SDK gère les décisions d’échantillonnage, la détection de ressources (hostname, nom du pod k8s, région cloud) et l’export de télémétrie via OTLP.

Collector — Un binaire autonome (ou conteneur Docker) qui agit comme pipeline de télémétrie : reçoit des apps → traite → exporte vers les backends.

OTLP (OpenTelemetry Line Protocol) est le format réseau canonique : protobuf sur gRPC (port 4317) ou HTTP/JSON (port 4318).

Le Collector OpenTelemetry

Le Collector est le composant le plus puissant d’un déploiement OTel en production, fonctionnant comme un pipeline à trois étapes :

Receivers

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"
      http:
        endpoint: "0.0.0.0:4318"
  prometheus:
    config:
      scrape_configs:
        - job_name: "mon-app"
          static_configs:
            - targets: ["app:8080"]
  filelog:
    include: ["/var/log/app/*.log"]
    operators:
      - type: json_parser
  hostmetrics:
    collection_interval: 30s
    scrapers:
      cpu: {}
      memory: {}
      disk: {}
      network: {}

Processors

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 400
  batch:
    send_batch_size: 10000
    timeout: 10s
  attributes:
    actions:
      - key: environment
        value: production
        action: insert
  filter:
    traces:
      span:
        - 'attributes["http.target"] == "/health"'
  tail_sampling:
    decision_wait: 10s
    policies:
      - name: politique-erreurs
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: politique-traces-lentes
        type: latency
        latency: {threshold_ms: 1000}
      - name: politique-probabiliste
        type: probabilistic
        probabilistic: {sampling_percentage: 10}

Le tail_sampling s’exécute dans le Collector et prend des décisions après avoir vu la trace complète, capturant toutes les erreurs tout en n’échantillonnant qu’une fraction des traces saines.

Exporters et Pipelines

exporters:
  otlp/tempo:
    endpoint: "tempo:4317"
    tls:
      insecure: true
  prometheus:
    endpoint: "0.0.0.0:9464"
  loki:
    endpoint: "http://loki:3100/loki/api/v1/push"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, tail_sampling]
      exporters: [otlp/tempo]
    metrics:
      receivers: [otlp, prometheus, hostmetrics]
      processors: [memory_limiter, batch, attributes]
      exporters: [prometheus]
    logs:
      receivers: [otlp, filelog]
      processors: [memory_limiter, batch]
      exporters: [loki]

Auto-Instrumentation

L’auto-instrumentation ajoute OTel sans modifier le code de l’application, interceptant automatiquement les appels HTTP, base de données et messagerie.

Java — Exécutez l’agent Java OTel comme flag JVM :

java -javaagent:/otel-javaagent.jar \
  -Dotel.service.name=mon-service \
  -Dotel.exporter.otlp.endpoint=http://collector:4317 \
  -jar mon-app.jar

Python — Utilisez opentelemetry-instrument :

pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap --action=install
opentelemetry-instrument \
  --service_name mon-service \
  --exporter_otlp_endpoint http://collector:4317 \
  python app.py

Node.js — Importez le SDK avant tout code applicatif :

// tracing.js — doit être chargé en premier via --require
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({
  serviceName: 'mon-service-node',
  traceExporter: new OTLPTraceExporter({ url: 'http://collector:4317' }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

Instrumentation Manuelle

Création de Spans

const opentelemetry = require('@opentelemetry/api');
const tracer = opentelemetry.trace.getTracer('mon-service', '1.0.0');

async function traiterCommande(commandeId) {
  return tracer.startActiveSpan('traiterCommande', async (span) => {
    try {
      span.setAttribute('commande.id', commandeId);
      const resultat = await debiterPaiement(commandeId);
      span.addEvent('paiement.debite', { 'paiement.montant': resultat.montant });
      return resultat;
    } catch (err) {
      span.recordException(err);
      span.setStatus({ code: opentelemetry.SpanStatusCode.ERROR });
      throw err;
    } finally {
      span.end();
    }
  });
}

Création de Métriques

const meter = opentelemetry.metrics.getMeter('mon-service', '1.0.0');

const compteurRequetes = meter.createCounter('http.server.requests');
const histogrammLatence = meter.createHistogram('http.server.duration', { unit: 'ms' });
const connexionsActives = meter.createUpDownCounter('db.connections.active');
meter.createObservableGauge('process.memory.usage').addCallback((result) => {
  result.observe(process.memoryUsage().heapUsed);
});

Tracing Distribué

Une trace est un graphe acyclique dirigé de spans représentant une requête de bout en bout. Chaque span possède : TraceId, SpanId, ParentSpanId, SpanKind, attributs, événements et statut.

Échantillonnage en tête — décide au span racine, simple mais ne peut pas favoriser les traces lentes ou erronées. Échantillonnage en queue — collecte la trace complète d’abord, puis décide de la conserver en capturant 100% des erreurs et seulement 10% du trafic sain.

Les exemplaires relient les observations d’histogramme Prometheus à des TraceIds Tempo spécifiques, permettant la navigation en un clic depuis un pic de latence P99 directement vers une trace représentative.

Kubernetes avec l’OTel Operator

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: otel-instrumentation
spec:
  exporter:
    endpoint: http://otel-agent:4317
  propagators: [tracecontext, baggage]
  sampler:
    type: parentbased_traceidratio
    argument: "0.1"
  nodejs:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest
  python:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest

Annotez un Deployment pour injecter l’agent Node.js automatiquement :

annotations:
  instrumentation.opentelemetry.io/inject-nodejs: "true"

Backends et Comparaison

BackendSignauxStockage
JaegerTraces uniquementCassandra/Elasticsearch
PrometheusMétriques uniquementTSDB local
Grafana LokiLogs uniquementStockage d’objets
Grafana TempoTraces uniquementStockage d’objets
SigNozTraces + Métriques + LogsClickHouse
PlateformeLock-inModèle de coût
OpenTelemetry + GrafanaAucunGratuit (auto-hébergé)
DatadogÉlevéPar hôte + ingestion
New RelicMoyenPar Go ingéré
DynatraceTrès élevéPar unité hôte
Elastic APMMoyenPar Go

Exemple Pratique : Node.js + Python avec Grafana Stack

Une configuration à deux services : un gateway API Node.js qui appelle un service de commandes Python. Toute la télémétrie passe par un seul Collector vers Tempo, Prometheus et Loki, visualisés dans Grafana.

services:
  collector:
    image: otel/opentelemetry-collector-contrib:latest
    volumes:
      - ./collector-config.yaml:/etc/otel/config.yaml
    ports: ["4317:4317", "4318:4318", "9464:9464"]
  node-api:
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
      - OTEL_SERVICE_NAME=node-api
  python-commandes:
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
      - OTEL_SERVICE_NAME=python-commandes

Dans Grafana : ouvrez Explore → Tempo → recherchez par service.name=node-api → cliquez sur une trace → utilisez le lien exemplaire pour voir la métrique P95 Prometheus corrélée → cliquez sur le lien Loki pour voir la ligne de log exacte de cette requête.

Pièges et Cas Particuliers

Dérive d’horloge — Les traces distribuées dépendent d’horloges synchronisées. Une dérive NTP supérieure à 1ms cause des problèmes d’ordonnancement des spans. Utilisez chrony ou la synchronisation temporelle native du cloud.

Tail sampling nécessite un routage cohérent par TraceId — Si vous exécutez plusieurs réplicas du Collector gateway avec tail_sampling, tous les spans de la même trace doivent arriver à la même instance. Utilisez l’exporter loadbalancing avec routing_key: traceId.

Explosion de cardinalité — N’utilisez jamais des valeurs de haute cardinalité (IDs utilisateur, IDs commande) comme valeurs de labels de métriques. Les attributs OTel sur les spans sont sûrs à toute cardinalité ; les dimensions de métriques ne le sont pas.

Résumé

  • OpenTelemetry fournit une API, un SDK et un protocole réseau (OTLP) uniques et neutres pour traces, métriques et logs
  • Le Collector OTel découple les applications des backends ; utilisez des agents (DaemonSet) alimentant un gateway (Deployment) avec tail sampling en production
  • L’auto-instrumentation ajoute des traces sans changement de code ; l’instrumentation manuelle ajoute du contexte métier avec startActiveSpan et createCounter/createHistogram
  • Les exemplaires relient les observations de métriques Prometheus aux TraceIds Tempo pour un drill-down en un clic
  • La Grafana Stack (Tempo + Prometheus + Loki + Grafana) est la combinaison de backends OTel open-source la plus populaire

Articles Connexes