Aller au contenu principal
ObservabilitéOpenTelemetryMonitoringSRE

OpenTelemetry : architecture et Collector

30 min de lecture Observabilité Moderne — Chapitre 1

Les 3 piliers de l'observabilité (traces, métriques, logs), l'architecture OpenTelemetry SDK → Collector → Backend, et la configuration du Collector en détail.

Ce chapitre est gratuit

Pas besoin de compte pour le lire. Decouvre le contenu et decide si le programme est fait pour toi.

Voir les autres chapitres

Introduction — Pourquoi OpenTelemetry change la donne

Tu gères 8 microservices en production. L’équipe A utilise Datadog, l’équipe B préfère Jaeger, l’équipe C a mis Prometheus. Résultat : trois SDK différents dans le code, trois formats incompatibles, et quand un incident survient, impossible de corréler une erreur du service A avec la latence du service C. C’est le vendor lock-in appliqué à l’observabilité.

OpenTelemetry (OTel) résout exactement ce problème. Né en 2019 de la fusion d’OpenTracing (CNCF) et d’OpenCensus (Google), c’est aujourd’hui le 2ème projet CNCF le plus actif après Kubernetes. Son principe : une couche d’abstraction universelle entre tes applications et tes backends d’observabilité.

Tu instrumentes avec OTel, tu envoies les données vers le backend de ton choix. Tu changes de backend ? Tu modifies la config de l’exporter. Ton code applicatif ne bouge pas d’une ligne.

🔥 Cas réel : Une scale-up européenne payait ~80 000€/an de Datadog. Chaque service avait le SDK Datadog en dur. Migration estimée vers Grafana Cloud : 6 mois, 3 devs full-time. Avec OpenTelemetry, ça aurait été un changement de config du Collector — une demi-journée.

🧠 À retenir : OpenTelemetry n’est pas un backend. Ce n’est pas un remplaçant de Datadog ou Grafana. C’est le standard qui te libère du vendor lock-in en découplant instrumentation et stockage.


Les 3 piliers de l’observabilité

L’observabilité, ce n’est pas juste “du monitoring”. Le monitoring surveille des métriques connues et alerte sur des seuils. L’observabilité te donne la capacité de comprendre l’état interne d’un système à partir de ses sorties — même face à des problèmes que tu n’avais pas anticipés.

Trois types de signaux complémentaires composent cette vision :

Les traces répondent à : “Quel chemin a pris cette requête à travers mes services ?” — Le parcours complet, du frontend à la base de données, avec le temps passé à chaque étape.

Les métriques répondent à : “Comment le système performe-t-il globalement ?” — Vue agrégée : taux d’erreurs, latence, débit, utilisation des ressources.

Les logs répondent à : “Que s’est-il passé exactement à cet instant ?” — Le détail brut d’un événement, avec tout son contexte.

La puissance vient de la corrélation entre les trois. Tu vois un pic de latence dans les métriques → tu accèdes aux traces lentes → tu trouves les logs avec l’erreur exacte. OpenTelemetry unifie ces trois piliers avec un identifiant commun : le traceId.

Une trace se compose de spans — chaque opération (appel HTTP, requête DB, message Kafka) crée un span avec un début, une fin, un statut et des attributs. Les spans forment un arbre via les parentSpanId, donnant une vue hiérarchique complète de la requête.

Pour les métriques, OTel définit trois types fondamentaux : les Counters (compteurs monotones, comme le nombre de requêtes), les Gauges (valeurs instantanées, comme la mémoire utilisée), et les Histograms (distributions, indispensables pour les percentiles de latence).

💡 Tip DevOps : Les moyennes mentent. Si 99 requêtes prennent 10ms et 1 requête prend 10s, la moyenne affiche 109ms. Utilise toujours des percentiles (p50, p90, p99) via les histograms pour voir la vraie expérience utilisateur.

Pour les logs, l’innovation OTel est la corrélation automatique : chaque log porte le traceId et le spanId du contexte courant. Tu passes d’un log d’erreur à la trace complète en un clic.


Architecture OTel : SDK → Collector → Backend

L’architecture se résume en trois couches. Les applications génèrent la télémétrie via le SDK, l’envoient au Collector via le protocole OTLP, et le Collector route vers les backends.

Le SDK se compose de deux parties : l’API (interfaces stables que ton code utilise) et l’implémentation (collecte, batching, export). Cette séparation permet aux bibliothèques partagées de n’importer que l’API légère, laissant l’application finale choisir l’implémentation.

Le protocole OTLP est le format natif d’OTel, avec deux modes de transport : gRPC sur le port 4317 (performant, idéal en interne) et HTTP/protobuf sur le port 4318 (compatible firewalls et load balancers).

Le Collector est le composant central — un proxy intelligent qui reçoit, transforme et route les données. Pourquoi ne pas envoyer directement aux backends ? Parce que le Collector apporte le découplage (les apps ignorent les backends), le processing (filtrage, sampling, enrichissement), le multi-export (mêmes données vers plusieurs destinations) et la sécurité (credentials centralisés).

⚠️ Attention : Les applications doivent toujours envoyer en OTLP au Collector, jamais directement aux backends. C’est la clé du découplage qui rend les migrations indolores.


Le Collector : pipeline et configuration

Le Collector fonctionne en pipeline : Receivers (entrées) → Processors (transformations) → Exporters (sorties). Chaque type de signal (traces, métriques, logs) a son propre pipeline indépendant.

Voici une configuration production complète avec les receivers OTLP et Prometheus, les processors essentiels, et les exporters vers la stack Grafana :

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
          relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: true

Les processors sont le cœur du traitement. Le memory_limiter protège contre l’OOM, le batch regroupe les envois, et le tail_sampling applique un échantillonnage intelligent :

processors:
  memory_limiter:
    check_interval: 5s
    limit_mib: 512
    spike_limit_mib: 128
  batch:
    send_batch_size: 1024
    timeout: 5s
  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: 1000 }
      - name: probabilistic
        type: probabilistic
        probabilistic: { sampling_percentage: 10 }

Les exporters envoient vers les backends. Tu peux combiner plusieurs destinations :

exporters:
  otlp/tempo:
    endpoint: tempo.monitoring:4317
    tls:
      insecure: true
  prometheusremotewrite:
    endpoint: http://mimir.monitoring:9009/api/v1/push
  loki:
    endpoint: http://loki.monitoring:3100/loki/api/v1/push

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

🔥 Cas réel : Un service à 10 000 req/s générait 10 000 traces/s. Avec le tail sampling ci-dessus (100% des erreurs, 100% des traces lentes, 10% du reste), réduction de 90% du volume sans perte de visibilité sur les problèmes.

💡 Tip DevOps : Le memory_limiter est non négociable en production. Sans lui, un pic de trafic fait OOM le Collector et tu perds ta télémétrie au pire moment — pendant l’incident. Place-le toujours en premier dans la chaîne de processors.


Déploiement sur Kubernetes

Le Collector se déploie en deux modes complémentaires. Le mode Agent (DaemonSet) place un Collector par node pour la collecte locale et le preprocessing léger. Le mode Gateway (Deployment) centralise le processing lourd et l’export vers les backends.

Le déploiement se fait via le chart Helm officiel :

helm repo add open-telemetry \
  https://open-telemetry.github.io/opentelemetry-helm-charts

# Agent — un par node, collecte locale
helm install otel-agent open-telemetry/opentelemetry-collector \
  --namespace monitoring \
  --set mode=daemonset \
  --values otel-agent-values.yaml

# Gateway — centralisé, processing + export
helm install otel-gateway open-telemetry/opentelemetry-collector \
  --namespace monitoring \
  --set mode=deployment \
  --set replicaCount=3 \
  --values otel-gateway-values.yaml

Pour l’instrumentation des applications, l’auto-instrumentation couvre 80% des besoins sans modifier le code. En Java, c’est un agent JVM ; en Python, un wrapper CLI :

# Java — agent JVM
java -javaagent:opentelemetry-javaagent.jar -jar myapp.jar

# Python — wrapper CLI
opentelemetry-instrument \
  --traces_exporter otlp \
  --exporter_otlp_endpoint http://localhost:4317 \
  uvicorn main:app --port 8000

Pour la logique métier spécifique, ajoute de l’instrumentation manuelle :

from opentelemetry import trace

tracer = trace.get_tracer("order-service")

def process_order(order):
    with tracer.start_as_current_span("process_order") as span:
        span.set_attribute("order.id", order.id)
        span.set_attribute("order.total", order.total)
        payment_service.validate(order.payment)

🧠 À retenir : Commence toujours par l’auto-instrumentation. Ajoute du manuel uniquement pour les opérations métier que l’auto ne capture pas.

⚠️ Attention : Ne mets jamais de données sensibles dans les attributs de span (passwords, tokens, PII). Les traces sont stockées longtemps et accessibles à toute l’équipe.


Bonnes pratiques et pièges à éviter

Bonnes pratiques :

  • Respecte les semantic conventions OTel (http.method, db.system) — tes dashboards seront auto-générés par les backends
  • Applique les frameworks RED (Rate, Errors, Duration) pour les services et USE (Utilization, Saturation, Errors) pour les ressources
  • Sépare Agent et Gateway quand le volume dépasse 10 000 spans/s
  • Monitore le Collector lui-même via ses métriques internes (port 8888)
  • Chiffre les connexions Collector → backends avec TLS

Pièges courants :

  • Envoyer les apps directement aux backends → vendor lock-in recréé
  • Oublier le memory_limiter → OOM du Collector pendant un incident
  • Sampler à 100% en production → coûts de stockage explosifs (un service à 1000 req/s avec 5 spans/requête = ~400 GB/jour)
  • Placer le tail sampling dans les agents DaemonSet → décisions incohérentes car chaque agent ne voit qu’une partie de la trace
  • Logger tout en DEBUG en production → gigaoctets/jour de logs inutiles

🔥 Cas réel : Le choix du backend impacte massivement les coûts. Un client avec 50 microservices payait ~120 000€/an avec Datadog. Migration vers Grafana Cloud : ~35 000€/an. Self-hosted Grafana sur K8s : ~8 000€/an. OpenTelemetry rend ces migrations possibles sans toucher au code.


Résumé

OpenTelemetry est le standard d’observabilité qui te libère du vendor lock-in. Son architecture en trois couches (SDK → Collector → Backend) découple l’instrumentation du stockage. Les trois piliers — traces, métriques, logs — se corrèlent via le traceId pour une visibilité complète. Le Collector, avec son modèle de pipeline Receivers → Processors → Exporters, est le composant central qui transforme, filtre et route ta télémétrie. En production, le memory_limiter et le tail sampling sont indispensables pour la stabilité et le contrôle des coûts.

🧠 À retenir : Instrumente avec OTel dès le jour 1. Même si tu utilises Datadog aujourd’hui, tu pourras migrer demain sans réécrire une ligne de code applicatif.

Articles liés