RousselTMLEARNING

Observabilité

Grafana Alloy et les traces : OTLP, Jaeger et Zipkin vers Tempo

Plongez dans le traitement des traces distribuées. Apprenez à ingérer des traces OTLP, Jaeger ou Zipkin avec Grafana Alloy, et à configurer l'échantillonnage avant l'envoi vers Tempo.

1. Ingestion des Traces #

Ingestion via OTLP ou protocoles tiers (Jaeger, Zipkin).

Recevoir les traces #

Disponible dans : ✓ OSS ✓ Enterprise ✓ Cloud

La chaîne des traces dans Alloy

Les applications instrumentées envoient leurs traces à Alloy, qui les traite puis les transmet à Tempo ou à un autre backend. Les ports standard de l'OTLP sont 4317 (gRPC) et 4318 (HTTP).

otelcol.receiver.otlp "default" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }
  http {
    endpoint = "0.0.0.0:4318"
  }
  output {
    traces = [otelcol.processor.memory_limiter.default.input]
  }
}

otelcol.processor.memory_limiter "default" {
  check_interval         = "1s"
  limit_percentage       = 80
  spike_limit_percentage = 20
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}

otelcol.processor.batch "default" {
  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}

otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "tempo:4317"
    tls {
      insecure = true
    }
  }
}

L'ordre recommandé #

ÉtapeComposantPourquoi
1memory_limiterEn premier : refuse les données avant que le processus ne manque de mémoire
2TraitementsAttributs, transformations, échantillonnage
3batchJuste avant l’envoi : regroupe et réduit le nombre de requêtes
4otelcol.exporter.otlpEnvoi
Erreur rencontrée : limit_percentage seul est refusé au démarrage (« limit_percentage and spike_limit_percentage must be greater than 0 »). Indiquez les deux : c'est pourquoi l'exemple précise aussi spike_limit_percentage.
Réglage de batchDéfaut
timeout200ms
send_batch_size2000
send_batch_max_size3000

Accepter les anciens formats #

Pendant une migration, Alloy reçoit aussi Jaeger et Zipkin, puis convertit tout vers le même pipeline. Les applications existantes n'ont donc rien à changer.

otelcol.receiver.jaeger "historique" {
  protocols {
    grpc {}
  }
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}

otelcol.receiver.zipkin "historique" {
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}

otelcol.processor.batch "default" {
  output {
    traces = []
  }
}

Vérifier #

Branchez temporairement otelcol.exporter.debug en sortie : il affiche chaque span dans les journaux d'Alloy. Ce composant est expérimental : lancez Alloy avec --stability.level=experimental. Les métriques otelcol_receiver_accepted_spans_total et otelcol_exporter_sent_spans_total permettent ensuite de comparer l'entrée et la sortie.

0.0.0.0:4318 rend le récepteur accessible aux autres machines. Sur un hôte partagé, limitez l'accès, ou ajoutez l'authentification avec otelcol.auth.*.

2. Transformation et Échantillonnage #

Adaptation des spans et échantillonnage (sampling).

Nettoyer les attributs #

Une trace peut contenir des données sensibles. otelcol.processor.attributes les retire ou les masque avant qu'elles ne quittent votre réseau.

otelcol.processor.attributes "nettoyage" {
  action {
    key    = "http.request.header.authorization"
    action = "delete"
  }
  action {
    key    = "user.email"
    action = "hash"
  }
  action {
    key    = "deployment.environment.name"
    value  = "prod"
    action = "upsert"
  }
  output {
    traces = []
  }
}
Attribut reçuAttribut envoyé (mesuré)
http.request.header.authorization: Bearer secretSupprimé
user.email: alice@exemple.fr182e2bdf10871ea9… (haché)
Absentdeployment.environment.name: prod

Le hachage garde une valeur stable : vous pouvez toujours compter les utilisateurs touchés, sans connaître leur adresse.

Normaliser avec OTTL #

otelcol.processor.transform exécute des instructions en OTTL. Ici, on regroupe GET /produits/42, GET /produits/43… sous un seul nom : sans cela, chaque identifiant crée une opération distincte, et les tableaux de bord deviennent illisibles.

otelcol.processor.transform "noms" {
  error_mode = "ignore"
  trace_statements {
    context    = "span"
    statements = [
      `set(attributes["http.route"], "/produits/{id}") where IsMatch(name, "^GET /produits/[0-9]+$")`,
      `set(name, "GET /produits/{id}") where IsMatch(name, "^GET /produits/[0-9]+$")`,
    ]
  }
  output {
    traces = []
  }
}

Résultat constaté : le span s'appelle GET /produits/{id} et porte http.route=/produits/{id}. L'ordre compte : on écrit l'attribut avant de renommer, sinon la condition ne correspond plus.

Échantillonner intelligemment #

Garder toutes les traces coûte cher. Il existe deux approches :

En tête (probabilistic_sampler)En queue (tail_sampling)
DécisionDès le premier spanAprès avoir vu la trace
Garde les erreurs ?Non, c’est le hasardOui
CoûtTrès faibleMémoire : les traces sont retenues
otelcol.processor.tail_sampling "decision" {
  decision_wait = "30s"
  num_traces    = 50000

  policy {
    name = "garder-les-erreurs"
    type = "status_code"
    status_code {
      status_codes = ["ERROR"]
    }
  }

  policy {
    name = "garder-les-lentes"
    type = "latency"
    latency {
      threshold_ms = 1000
    }
  }

  policy {
    name = "echantillon-du-reste"
    type = "probabilistic"
    probabilistic {
      sampling_percentage = 5
    }
  }

  output {
    traces = []
  }
}

Une trace est gardée dès qu'une politique l'accepte. Nous avons testé erreurs et lenteur (avec decision_wait réduit à 5 s) sur 3 traces :

TraceRésultat
GET /produits/42 en erreur, 20 msGardée
GET /sante réussie, 10 msÉcartée
POST /payer réussie, 2500 msGardée

otelcol_receiver_accepted_spans_total valait 3, et otelcol_exporter_sent_spans_total 2.

En cas de plusieurs instances : tous les spans d'une trace doivent arriver sur la même instance. Devant plusieurs Alloy, ajoutez otelcol.exporter.loadbalancing avec routing_key = "traceID". decision_wait (30 s par défaut) et num_traces (50000) déterminent la mémoire nécessaire.

Produire des métriques à partir des traces #

Pour ne pas perdre les taux réels malgré l'échantillonnage, générez les métriques avant celui-ci : forward les traces reçues à la fois vers ce connecteur et vers le pipeline.

otelcol.connector.spanmetrics "red" {
  histogram {
    explicit {
      buckets = ["100ms", "500ms", "2s"]
    }
  }
  output {
    metrics = [otelcol.exporter.prometheus.vers_prom.input]
  }
}

otelcol.exporter.prometheus "vers_prom" {
  forward_to = [prometheus.remote_write.mimir.receiver]
}

prometheus.remote_write "mimir" {
  endpoint {
    url = "http://mimir:9009/api/v1/push"
  }
}

Articles liés

Alloy et l'écosystème Grafana Labs : quel composant pour quel outil ?

La carte des intégrations d'Alloy avec Faro, Beyla, k6, Loki, Mimir, Tempo, Pyroscope, Database Observability…

Faro et Alloy : recevoir le RUM front-end avec faro.receiver

Configurer faro.receiver : CORS, clé d'API, limite de débit, source maps, puis envoyer erreurs, Web Vitals et…

Beyla et Alloy : métriques RED et traces sans code avec beyla.ebpf

Activer l'instrumentation eBPF de Beyla dans Alloy : découverte des services, métriques RED vers Mimir, trace…

Pour aller plus loin

Soutenir

♥ SOUTENIR

Soutenez RousselTM Learning

Les formations, articles, TP et attestations restent gratuits et sans publicité intrusive. Une contribution libre, du montant de votre choix, finance l’hébergement, les environnements de TP et l’écriture de nouveaux contenus.

Contribuer avec PayPal Montant libre · carte bancaire ou compte PayPalEn savoir plus
QR code PayPal
Ou scannez ce QR code avec votre téléphone

Merci, chaque contribution compte !