OpenTelemetry : traces distribuées et observabilité en production
← Retour au blogDevOps

OpenTelemetry : traces distribuées et observabilité en production

19 mars 202611 min de lectureOpenTelemetryTracingObservabilité

OpenTelemetry est devenu le standard d'observabilité cloud-native. Traces, métriques et logs unifiés en un seul SDK — découvrez comment l'implémenter en production avec Grafana Tempo.

Pourquoi OpenTelemetry ?

Avant OpenTelemetry, chaque outil d'observabilité avait son propre SDK propriétaire : Jaeger SDK, Zipkin SDK, Datadog Agent, New Relic SDK. Instrumenter une application signifiait choisir un vendor dès le départ, avec un couplage fort et des coûts de migration prohibitifs. OpenTelemetry (OTel) résout ce problème fondamental en proposant un SDK unique, vendor-neutral, qui devient le standard de facto du CNCF.

OTel unifie les trois signaux d'observabilité dans une seule API :

  • Traces : suivi de bout en bout d'une requête à travers plusieurs services (spans)
  • Métriques : compteurs, jauges, histogrammes exposés en format OTLP ou Prometheus
  • Logs : logs structurés avec corrélation automatique aux traces via Trace ID

Architecture OTel

La chaîne de collecte OTel suit ce flux :

Application (SDK OTel)
    │  OTLP gRPC/HTTP
    ▼
OTel Collector (pipeline)
    ├── Receivers  : OTLP, Prometheus, Jaeger, Zipkin, Fluentd
    ├── Processors : batch, memory_limiter, resource, sampling
    └── Exporters  : Tempo (traces), Prometheus (métriques), Loki (logs)
         │
         ├── Grafana Tempo  → Traces
         ├── Prometheus     → Métriques
         └── Grafana Loki   → Logs

Auto-instrumentation Node.js

OTel propose des librairies d'auto-instrumentation qui patchent automatiquement les modules populaires (Express, HTTP, gRPC, Redis, PostgreSQL) sans modifier le code applicatif :

// tracing.ts — à charger AVANT tout autre module
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'payment-service',
    [SemanticResourceAttributes.SERVICE_VERSION]: process.env.APP_VERSION,
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV,
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4317',
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({ url: 'http://otel-collector:4317' }),
    exportIntervalMillis: 15000,
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
process.on('SIGTERM', () => sdk.shutdown());
# package.json — démarrage avec auto-instrumentation
{
  "scripts": {
    "start": "node --require ./tracing.js dist/server.js"
  }
}

Configuration du OTel Collector

Le Collector est le composant central qui reçoit, transforme et exporte les signaux vers les backends :

# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  prometheus:
    config:
      scrape_configs:
        - job_name: 'kubernetes-pods'
          kubernetes_sd_configs:
            - role: pod

processors:
  batch:
    timeout: 5s
    send_batch_size: 1024
  memory_limiter:
    limit_mib: 512
    spike_limit_mib: 128
  resource:
    attributes:
      - key: deployment.environment
        value: production
        action: upsert
  tail_sampling:
    decision_wait: 10s
    policies:
      - name: errors-policy
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: slow-traces
        type: latency
        latency: {threshold_ms: 500}
      - name: probabilistic
        type: probabilistic
        probabilistic: {sampling_percentage: 10}

exporters:
  otlp/tempo:
    endpoint: http://tempo:4317
    tls:
      insecure: true
  prometheusremotewrite:
    endpoint: http://prometheus:9090/api/v1/write
  loki:
    endpoint: http://loki:3100/loki/api/v1/push

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

Grafana Tempo comme backend de traces

Tempo est le backend de traces distribué de Grafana Labs, conçu pour stocker massivement des traces à bas coût en utilisant S3/GCS comme stockage objet :

# values.yaml pour le chart Helm Tempo
tempo:
  storage:
    trace:
      backend: s3
      s3:
        bucket: my-tempo-traces
        region: eu-west-1
        access_key: ${AWS_ACCESS_KEY_ID}
        secret_key: ${AWS_SECRET_ACCESS_KEY}
  retention: 168h   # 7 jours

tempoQuery:
  enabled: true
helm repo add grafana https://grafana.github.io/helm-charts
helm upgrade --install tempo grafana/tempo   -f values.yaml   --namespace monitoring

Corrélation traces ↔ logs ↔ métriques dans Grafana

La puissance d'OTel réside dans la corrélation automatique des trois signaux. Dans Grafana, configurez les datasources pour activer la navigation entre signaux :

  • Trace to Logs : depuis une trace Tempo, ouvrir les logs Loki filtrés par traceID
  • Trace to Metrics : depuis une trace, voir les métriques Prometheus du service à ce moment précis
  • Exemplars : depuis une métrique Prometheus, accéder directement à une trace représentative

Propagation du contexte W3C Trace Context

Pour les traces distribuées cross-services, OTel utilise le standard W3C Trace Context (RFC 7230). Les headers traceparent et tracestate sont automatiquement propagés par les SDK entre services HTTP et gRPC.

Stratégies de sampling

  • Head-based sampling : décision prise à l'entrée de la trace (avant d'avoir les données complètes). Simple, faible overhead. Configurable par rate (10 % des traces).
  • Tail-based sampling : décision prise après la fin de la trace (accès à toutes les données). Permet de conserver 100 % des traces en erreur et de sampler à 5 % les traces normales. Requiert plus de mémoire dans le Collector.

OTel Operator pour Kubernetes

L'OTel Operator permet d'auto-instrumenter des applications via une simple annotation Kubernetes, sans modifier le code ni les images Docker :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  template:
    metadata:
      annotations:
        instrumentation.opentelemetry.io/inject-nodejs: "true"
        # Aussi disponible : inject-java, inject-python, inject-dotnet
    spec:
      containers:
        - name: app
          image: payment-service:1.0.0
# Configuration de l'Instrumentation CRD
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: default-instrumentation
  namespace: production
spec:
  exporter:
    endpoint: http://otel-collector:4317
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "0.1"   # 10 % sampling

Conclusion

OpenTelemetry a résolu le problème du vendor lock-in dans l'observabilité. Avec un seul SDK, vous pouvez changer de backend (Datadog → Grafana Stack, ou NewRelic → Jaeger) sans toucher au code applicatif. La stack open-source OTel Collector + Grafana Tempo + Prometheus + Loki offre une observabilité de niveau enterprise à coût maîtrisé sur Kubernetes.

← Retour au blog