ITSkillsCenter
Business Digital

Configurer un OpenTelemetry Collector pas-a-pas (receivers, processors, exporters OTLP)

12 min de lecture

📍 Article principal : Observabilité applicative en 2026 : OpenTelemetry, traces distribuées et stack LGTM — pour le contexte conceptuel et l’architecture d’ensemble.

À quoi sert le Collector et pourquoi il n’est pas optionnel

Le réflexe naïf, quand on découvre OpenTelemetry, est d’exporter directement depuis l’application vers le backend final : SDK Node qui parle directement à Tempo, SDK Python qui parle directement à Mimir. C’est techniquement possible, mais opérationnellement médiocre. Une coupure réseau temporaire entre l’application et Tempo fait perdre les spans en attente. Un changement d’endpoint demande de redéployer toutes les applications. Une politique d’enrichissement (ajout d’un attribut cluster=eu-west-1 partout) doit être implémentée dans chaque SDK.

L’OpenTelemetry Collector est le composant qui résout ces problèmes en un seul endroit. C’est un binaire écrit en Go qui reçoit des données via plusieurs protocoles, les traite (filtre, enrichit, redacte, sample), puis les redistribue vers un ou plusieurs backends. Ce tutoriel le configure pas-à-pas pour la chaîne typique : recevoir OTLP, batcher, ajouter des attributs de ressource, et envoyer vers Tempo, Loki et Mimir.

Prérequis

  • Une machine Linux ou macOS — Windows fonctionne aussi mais les exemples utilisent un shell Unix
  • Docker installé (méthode la plus simple) ou possibilité d’exécuter un binaire Go
  • Au moins un service applicatif instrumenté OTel pour tester (cf. tutoriels Node.js, Python, Go)
  • Notions de YAML
  • Temps estimé : 35 à 50 minutes

Étape 1 — Choisir une distribution

Il existe plusieurs distributions du Collector, qui ne diffèrent que par la liste des composants pré-compilés. La core distribue uniquement les composants stables maintenus dans le dépôt principal — minimaliste. La contrib ajoute des centaines de receivers, processors et exporters tiers (Loki, Tempo, Mimir, Kafka, etc.) maintenus par la communauté. Pour un usage typique avec la stack LGTM, la version otelcol-contrib est nécessaire car les exporters Loki et Tempo n’existent pas dans la core.

On peut soit télécharger un binaire depuis les releases GitHub, soit utiliser l’image Docker officielle. La voie Docker est plus simple à reproduire entre développeurs et environnements.

docker pull otel/opentelemetry-collector-contrib:latest

L’image est légère (autour de 200 Mo) et démarre en moins d’une seconde. Pour figer la version, remplacer :latest par une étiquette précise comme :0.151.0. En production, on fige toujours.

Étape 2 — Écrire la configuration minimale

La configuration du Collector est un fichier YAML structuré en quatre sections : receivers qui décrit comment les données entrent, processors qui décrit les transformations appliquées en chaîne, exporters qui décrit où les données sortent, et service qui orchestre tout en pipelines distincts pour traces, métriques et logs.

On commence par la version la plus simple qui valide la chaîne : on reçoit en OTLP, on batch, on logge en sortie. Cette config sert à confirmer que l’application instrumentée envoie bien quelque chose avant de connecter les vrais backends.

# otelcol.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: 1024

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]

Cette configuration ouvre les deux ports OTLP standards (4317 gRPC, 4318 HTTP), accumule les batchs jusqu’à 1024 éléments ou 5 secondes, puis imprime tout ce qui passe sur stdout. C’est une boucle de validation idéale en début de mise en place : si on voit les spans dans les logs du Collector, c’est que l’application exporte correctement.

Étape 3 — Lancer le Collector

On lance le conteneur en montant la configuration et en exposant les ports OTLP. Le binaire interne du Collector lit la config au démarrage, vérifie sa validité (chaque composant doit être disponible et bien typé), puis démarre les pipelines.

docker run --rm \
  -v "$(pwd)/otelcol.yaml":/etc/otelcol-contrib/config.yaml \
  -p 4317:4317 -p 4318:4318 \
  otel/opentelemetry-collector-contrib:latest \
  --config=/etc/otelcol-contrib/config.yaml

Au démarrage, le Collector écrit en quelques lignes ce qu’il a chargé : ses pipelines, leurs composants, et les ports en écoute. Une fois prêt, on déclenche du trafic depuis une application instrumentée et on doit voir des blocs InstrumentationScope apparaître dans les logs avec le nom du service, les spans, et leurs attributs. Si rien n’arrive, vérifier dans cet ordre : connectivité réseau côté application, alignement gRPC/HTTP avec le port, et logs du Collector qui peuvent signaler une erreur de désérialisation OTLP.

Étape 4 — Ajouter des processors d’enrichissement

Le Collector devient utile quand on lui ajoute de la valeur côté processors. Le resource processor injecte des attributs communs à tout ce qui passe (par exemple l’environnement, le datacenter, le cluster), ce qui évite de demander à chaque application de connaître ces métadonnées. Le memory_limiter protège le binaire d’une explosion de mémoire si le débit dépasse ce que les exporters absorbent.

processors:
  memory_limiter:
    check_interval: 2s
    limit_mib: 512
    spike_limit_mib: 128
  resource:
    attributes:
      - key: deployment.environment
        value: production
        action: upsert
      - key: cluster
        value: eu-west-1
        action: upsert
  batch:
    timeout: 5s
    send_batch_size: 1024

L’ordre des processors dans le pipeline est sémantique : on applique d’abord memory_limiter (qui rejette si saturé), puis resource (qui enrichit), puis batch (qui groupe pour l’export efficace). Cet ordre est typique et conseillé. On référence ces processors dans la section service :

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [debug]

Chaque trace reçue ressort enrichie de deployment.environment=production et cluster=eu-west-1. Cette homogénéisation est précieuse côté backend : on filtre par environnement sans devoir trust que chaque application l’ait positionné.

Étape 5 — Exporter vers les backends LGTM

Maintenant que la chaîne est validée, on remplace debug par les vrais exporters. Tempo, Loki et Mimir acceptent tous OTLP en entrée — ce qui rend la configuration directe. Adresses et tenant_id à adapter selon le déploiement.

exporters:
  otlp/tempo:
    endpoint: tempo:4317
    tls:
      insecure: true
  otlphttp/loki:
    endpoint: http://loki:3100/otlp
  otlphttp/mimir:
    endpoint: http://mimir:9009/otlp
    headers:
      X-Scope-OrgID: anonymous

Trois exporters distincts pour trois pipelines distincts. Tempo accepte OTLP gRPC nativement. Loki expose un endpoint OTLP HTTP qui ingère les LogRecords directement avec mapping automatique vers ses labels. Mimir accepte OTLP HTTP avec un en-tête X-Scope-OrgID qui identifie le tenant en mode multi-tenants ; sur un Mimir mono-tenant en dev, la valeur anonymous convient.

On câble ces exporters dans les pipelines :

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [otlp/tempo]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [otlphttp/mimir]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [otlphttp/loki]

À ce stade, chaque type de signal va à son backend. Pour un troubleshooting facilité, on peut garder l’exporter debug en parallèle dans la liste — le Collector exporte vers tous les exporters d’un pipeline, on aura à la fois les vrais backends et le log stdout.

Étape 6 — Filtrage et redaction de données sensibles

La gateway Collector est l’endroit naturel pour appliquer les politiques de confidentialité : retirer les en-têtes Authorization, masquer les numéros de téléphone qui auraient pu se retrouver dans des attributs, supprimer entièrement certains attributs jamais souhaités hors du périmètre. Le processor attributes permet ces transformations.

processors:
  attributes/redact:
    actions:
      - key: http.request.header.authorization
        action: delete
      - key: http.request.header.cookie
        action: delete
      - key: db.statement
        action: hash
      - key: user.email
        action: extract
        pattern: ^.+@(?P.+)$

L’action delete retire l’attribut. hash remplace la valeur par son SHA-256, ce qui permet d’identifier deux occurrences identiques sans révéler le contenu — utile pour les requêtes SQL où l’on veut grouper par template sans exposer les valeurs. extract avec une regex à groupes nommés ((?P<nom>...)) crée de nouveaux attributs à partir des captures (ici, user_email_domain contient le domaine de l’email sans la partie locale ; l’attribut original user.email n’est pas modifié — pour le supprimer ensuite, ajouter une action delete sur user.email). Ces transformations s’appliquent uniformément à toutes les données traversant le Collector, ce qui rend la politique vérifiable au niveau de l’infrastructure plutôt qu’auditée dans des dizaines de codebases.

Étape 7 — Échantillonnage côté Collector

Quand le volume de traces explose, on bascule vers un sampling tail-based, qui examine la trace complète avant de décider. Ce processor exige une topologie en deux couches dans une architecture distribuée (loadbalancer en amont qui groupe par trace_id, puis tail_sampling en aval). Pour un déploiement modeste mono-instance, on peut activer directement le processor.

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 100000
    expected_new_traces_per_sec: 1000
    policies:
      - name: errors-policy
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow-traces-policy
        type: latency
        latency:
          threshold_ms: 500
      - name: sample-rest
        type: probabilistic
        probabilistic:
          sampling_percentage: 5

Cette configuration garde 100 % des traces en erreur, 100 % des traces dépassant 500 ms, et 5 % des autres. L’effet sur le volume est typiquement une réduction de 80-90 % sans perte significative pour les investigations. Le tutoriel Tail-based sampling pour maîtriser les coûts creuse l’architecture en deux couches nécessaire à grande échelle.

Étape 8 — Vérifier la santé du Collector

Le Collector expose lui-même des métriques internes via une extension. On les active pour superviser la chaîne d’observabilité — un Collector en surcharge ou qui rejette des données doit alerter au même titre qu’une application en production.

extensions:
  health_check:
    endpoint: 0.0.0.0:13133
  pprof:
    endpoint: 0.0.0.0:1777
  zpages:
    endpoint: 0.0.0.0:55679

service:
  extensions: [health_check, pprof, zpages]
  telemetry:
    metrics:
      level: detailed
      readers:
        - pull:
            exporter:
              prometheus:
                host: 0.0.0.0
                port: 8888

L’endpoint :8888/metrics expose les compteurs internes au format Prometheus : otelcol_receiver_accepted_spans, otelcol_exporter_send_failed_spans, otelcol_processor_dropped_spans. Ces métriques alimentent un tableau de bord de supervision qui révèle une erreur d’export ou une saturation mémoire bien avant qu’elle ne fasse perdre des données.

Erreurs fréquentes

Utiliser la distribution core alors qu’il faut contrib

Erreur la plus fréquente. La core ne contient pas les exporters Loki, Tempo en mode HTTP, ni les processors avancés comme tail_sampling ou transform. Au démarrage, le Collector affiche unknown component et refuse de démarrer. Toujours utiliser otel/opentelemetry-collector-contrib pour la stack LGTM.

Pas de memory_limiter en production

Sans ce processor, un pic de débit ou un backend lent peut faire grossir la mémoire jusqu’à OOM. Le memory_limiter arrête le traitement quand la limite est atteinte et signale aux receivers de rejeter les nouvelles données plutôt que de les empiler en mémoire. C’est le garde-fou le plus important du Collector.

Confondre les ports gRPC et HTTP

4317 gRPC, 4318 HTTP. Pointer un client gRPC vers 4318 produit des erreurs de désérialisation. Vérifier le couple protocole-port côté SDK et côté receiver.

Mettre send_batch_size trop petit

Un batch de 100 sur du gros débit déclenche des milliers d’envois par seconde aux backends, ce qui sature les connexions et fait monter la latence de la chaîne. La valeur 1024 à 8192 est une zone confortable pour la majorité des charges. À très haut débit, monter encore avec un timeout ajusté pour ne pas garder les données trop longtemps.

Croire que le Collector est stateful sans le vouloir

Le Collector standard est sans état persistant : à un redémarrage, les données en buffer sont perdues. Pour une garantie de livraison sur incident, on active le file_storage extension qui persiste les batchs sur disque, ou on déploie une queue (Kafka, Redis Streams) entre le Collector et les backends. Sans ces options, accepter qu’un crash perde quelques secondes de données.

Tutoriels associés

Ressources et références officielles

FAQ

Agent ou gateway ?

Les deux. L’agent tourne en sidecar du pod ou en daemonset sur l’hôte ; il décharge l’application du transport et enrichit avec les attributs locaux. La gateway centralise les politiques (sampling, redaction, routage). En production sérieuse, on déploie les deux en chaîne : application → agent → gateway → backends.

Le Collector remplace-t-il Fluent Bit pour les logs ?

De plus en plus, oui. Le Collector contrib a un filelog receiver mature qui suit les fichiers, gère les rotations, et peut parser le JSON. Pour des pipelines logs complexes, Fluent Bit reste plus rapide et plus léger sur du gros débit, mais le Collector unifie les pipelines et c’est souvent décisif pour la maintenance.

Comment tester une config sans la déployer ?

Le binaire accepte --dry-run qui valide la syntaxe et la disponibilité des composants sans démarrer. Combiné avec un test d’export debug en local, on attrape la majorité des erreurs avant déploiement.

Combien coûte le Collector en ressources ?

Pour un débit modeste (10k spans/s, 100k metrics/s, 50k logs/s), une instance avec 1 vCPU et 1 Go RAM suffit largement. À très grande échelle (100k+ spans/s), on passe à plusieurs réplicas derrière un load balancer pour le sharding par trace_id dans le sampling tail-based.

La config peut-elle être segmentée en plusieurs fichiers ?

Oui. Depuis 2024, le Collector accepte plusieurs --config et merge les YAML. C’est utile pour séparer les bouts génériques (receivers, processors communs) des bouts spécifiques (exporters par environnement). En Kubernetes, l’opérateur OTel facilite encore cette gestion via des CRD dédiés.

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité