Jaeger est une plateforme open source de tracing distribué qui suit une requête à travers tous les microservices qu'elle traverse, pour révéler où le temps se perd et où les erreurs naissent. Créé par Uber, c'est un projet CNCF Graduated, le plus haut niveau de maturité de la fondation cloud-native. Sa version 2, actuelle, est entièrement rebâtie sur l'OpenTelemetry Collector.
Ce guide s'adresse aux profils intermédiaires et avancés qui exploitent des microservices et veulent diagnostiquer les requêtes lentes. Vous allez comprendre les concepts du tracing, ce qui change entre Jaeger v1 et v2, démarrer Jaeger en local, instrumenter une application avec OpenTelemetry et configurer un stockage persistant.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Définir Jaeger et les concepts de span, trace et baggage.
- Distinguer Jaeger v1 de la v2 fondée sur OpenTelemetry.
- Démarrer Jaeger en local avec l'image all-in-one.
- Instrumenter une application via le SDK OpenTelemetry.
- Configurer un stockage persistant et le sampling.
Qu'est-ce que Jaeger ?
Section intitulée « Qu'est-ce que Jaeger ? »Jaeger répond à une question que les logs et les métriques laissent sans réponse : par où est passée cette requête, et qui l'a ralentie ? Dans une architecture microservices, un simple clic déclenche une cascade d'appels entre services. Sans traçage, chaque service ne voit que sa propre part ; personne n'a la vue d'ensemble.
Le tracing distribué reconstitue ce trajet complet. Chaque service ajoute sa contribution à une trace commune, identifiée par un trace_id propagé d'appel en appel. Jaeger collecte ces fragments, les recompose et les affiche dans une interface web où l'on lit la chronologie exacte de la requête. C'est l'outil de référence pour ce besoin, aux côtés de Grafana Tempo et de Zipkin.
Les concepts du tracing : span, trace, baggage
Section intitulée « Les concepts du tracing : span, trace, baggage »Trois notions suffisent à lire n'importe quelle trace. Les maîtriser rend l'interface immédiatement compréhensible.
- Le span est l'unité de base : une opération avec un début, une fin et une durée. Un appel HTTP, une requête SQL ou un envoi de message sont autant de spans. Chaque span porte un nom, un statut et des attributs (code HTTP, table interrogée).
- La trace est l'arbre de spans reliés par un même
trace_id. Le span racine est souvent la requête entrante ; ses enfants sont les appels qu'elle a déclenchés. La trace montre lesquels se déroulent en parallèle et lesquels s'enchaînent. - Le baggage transporte des métadonnées le long de la trace, par exemple un
user_idou untenant_id, pour retrouver toutes les traces d'un même client.
Concrètement, une trace frontend GET /order de 450 ms peut contenir un span order-service de 120 ms et un span stock-service de 80 ms, le premier appelant lui-même un span db-query de 45 ms. On voit d'un coup d'oeil quel maillon consomme le temps.
Jaeger v1 ou v2 : ce qui a changé
Section intitulée « Jaeger v1 ou v2 : ce qui a changé »La version 2 de Jaeger est une refonte majeure, à connaître avant toute installation. Elle abandonne la constellation de binaires de la v1 (agent, collector, query) et la configuration par variables d'environnement, au profit d'une base commune : l'OpenTelemetry Collector.
| Aspect | Jaeger v1 | Jaeger v2 |
|---|---|---|
| Fondation | Binaires propres à Jaeger | OpenTelemetry Collector |
| Configuration | Variables d'environnement (SPAN_STORAGE_TYPE…) | Fichier YAML unique |
| Composant agent | Séparé (jaeger-agent) | Supprimé, remplacé par OTLP |
| Format d'entrée | Jaeger, Zipkin, OTLP | OTLP en priorité, Jaeger et Zipkin |
Conséquence pratique : les tutoriels qui configurent Jaeger avec SPAN_STORAGE_TYPE=elasticsearch décrivent la v1. En v2, tout passe par un fichier de configuration au format Collector, comme montré plus bas. Ce guide cible la v2, la version maintenue.
L'architecture de Jaeger v2
Section intitulée « L'architecture de Jaeger v2 »Même unifiée dans un seul binaire, l'architecture logique reste lisible. Comprendre ces rôles aide à dimensionner un déploiement de production, où on les sépare à nouveau pour scaler.
| Composant | Rôle |
|---|---|
| Récepteurs (receivers) | Acceptent les traces, surtout en OTLP (ports 4317/4318) |
| Pipeline | Traite et exporte les spans vers le stockage |
| Storage | Persiste les traces (mémoire, Badger, Elasticsearch, Cassandra) |
| Query + UI | Interroge le stockage et sert l'interface web (port 16686) |
En mode all-in-one, ces rôles tournent dans un seul conteneur avec un stockage en mémoire : parfait pour découvrir ou développer. En production, on déploie collector et query séparément, adossés à un stockage externe.
Démarrer Jaeger en local
Section intitulée « Démarrer Jaeger en local »L'image all-in-one lance Jaeger complet en une commande, récepteur OTLP et interface inclus. On épingle la version pour un résultat reproductible.
docker run -d --name jaeger \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ jaegertracing/jaeger:2.19.0Cette image expose trois ports utiles : l'interface web sur 16686, et les récepteurs OTLP gRPC (4317) et HTTP (4318), actifs par défaut sans configuration.
Vérification : ouvrez http://localhost:16686, l'interface Jaeger s'affiche. Pour confirmer l'ingestion sans instrumenter d'application, envoyez une trace de test en OTLP HTTP :
curl -X POST http://localhost:4318/v1/traces \ -H "Content-Type: application/json" \ -d '{ "resourceSpans": [{ "resource": {"attributes": [{"key": "service.name", "value": {"stringValue": "checkout"}}]}, "scopeSpans": [{ "spans": [{ "traceId": "5b8efff798038103d269b633813fc60c", "spanId": "eee19b7ec3c1b174", "name": "GET /order", "kind": 2, "startTimeUnixNano": "'$(date +%s)'000000000", "endTimeUnixNano": "'$(date +%s)'500000000" }] }] }] }'La réponse {"partialSuccess":{}} confirme l'acceptation. Le service checkout apparaît alors dans la liste de l'interface, et sa trace est consultable. L'horodatage au présent (date +%s) garantit qu'elle tombe dans la fenêtre de recherche par défaut.
Envoyer des traces avec OpenTelemetry
Section intitulée « Envoyer des traces avec OpenTelemetry »En pratique, ce sont vos applications qui émettent les traces, via le SDK OpenTelemetry. C'est la méthode recommandée : le code n'est plus lié à Jaeger, il parle OTLP, et pourrait pointer vers Tempo ou un autre backend sans rien changer.
from opentelemetry import tracefrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessorfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
trace.set_tracer_provider(TracerProvider())exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(exporter))
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("process_order") as span: span.set_attribute("order.id", 12345) # ... traitement métierimport ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/sdk/trace")
func initTracer() (*trace.TracerProvider, error) { exporter, err := otlptracegrpc.New(context.Background(), otlptracegrpc.WithEndpoint("localhost:4317"), otlptracegrpc.WithInsecure(), ) if err != nil { return nil, err } tp := trace.NewTracerProvider(trace.WithBatcher(exporter)) otel.SetTracerProvider(tp) return tp, nil}Pour de nombreux frameworks, l'auto-instrumentation évite d'écrire ce code. En Python, un seul wrapper suffit à tracer une application entière :
pip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a installopentelemetry-instrument \ --traces_exporter otlp \ --exporter_otlp_endpoint http://localhost:4317 \ --service_name mon-service \ python app.pyConfigurer un stockage persistant
Section intitulée « Configurer un stockage persistant »Le mode all-in-one perd toutes les traces à l'arrêt du conteneur, puisqu'il stocke en mémoire. Pour conserver l'historique, on fournit un fichier de configuration v2 qui déclare un backend de stockage. L'exemple ci-dessous utilise Badger, une base de données locale sur fichier, idéale pour un poste ou un petit environnement.
service: extensions: [jaeger_storage, jaeger_query] pipelines: traces: receivers: [otlp] exporters: [jaeger_storage_exporter]
extensions: jaeger_query: storage: traces: main_storage jaeger_storage: backends: main_storage: badger: directories: keys: /tmp/jaeger/keys values: /tmp/jaeger/values ephemeral: false
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318
exporters: jaeger_storage_exporter: trace_storage: main_storageOn lance ensuite Jaeger en montant ce fichier :
docker run -d --name jaeger \ -p 16686:16686 -p 4317:4317 -p 4318:4318 \ -v "$(pwd)/config.yaml:/etc/jaeger/config.yaml" \ jaegertracing/jaeger:2.19.0 \ --config /etc/jaeger/config.yamlLes traces survivent désormais aux redémarrages. Pour la production à fort volume, on remplace le bloc badger par un backend distribué (elasticsearch, opensearch ou cassandra) au même niveau backends, avec les paramètres de connexion correspondants ; la structure du pipeline, elle, ne change pas.
Maîtriser le volume avec le sampling
Section intitulée « Maîtriser le volume avec le sampling »Tracer 100 % des requêtes est ruineux en stockage sur un service à fort trafic. Le sampling (échantillonnage) ne conserve qu'une fraction représentative des traces. Deux stratégies coexistent.
- Head sampling : la décision est prise au début de la trace, côté application, via le SDK OpenTelemetry. Simple et peu coûteux, mais aveugle au contenu de la trace.
- Tail sampling : la décision est prise après avoir vu toute la trace, dans un OpenTelemetry Collector. Plus intelligent, il garde par exemple toutes les traces en erreur et échantillonne le reste, au prix d'une mémoire tampon.
| Type de décision | Où | Compromis |
|---|---|---|
| Probabiliste (ex. 10 %) | Head, SDK | Simple, mais rate des erreurs rares |
| Rate limiting (N/s) | Head, SDK | Protège le backend d'un pic |
| Tail sampling (garde les erreurs) | Collector | Précis, coûte de la mémoire |
Un réglage courant : probabiliste à 100 % sur une API critique peu sollicitée, 10 % par défaut ailleurs, et un tail sampling qui rattrape systématiquement les traces contenant une erreur.
Jaeger ou Tempo ?
Section intitulée « Jaeger ou Tempo ? »Les deux stockent des traces et se pilotent en OTLP, mais leur philosophie diffère. Jaeger offre une interface de recherche riche et indexe les traces : on cherche facilement par service, opération ou tag. Grafana Tempo n'indexe que le trace ID et mise sur un stockage objet très bon marché, au prix d'une recherche par attribut plus limitée.
Le choix dépend du contexte. Si vous vivez déjà dans Grafana avec Loki et Prometheus, Tempo s'y fond naturellement. Si vous voulez une UI de traces autonome et puissante en recherche, Jaeger reste une valeur sûre. Les deux acceptant OTLP, migrer de l'un à l'autre ne touche pas votre instrumentation.
Dépannage
Section intitulée « Dépannage »Les incidents Jaeger se concentrent sur la propagation et le stockage. Voici les symptômes les plus fréquents.
| Symptôme | Cause probable | Solution |
|---|---|---|
| Traces incomplètes | Contexte non propagé entre services | Vérifier la transmission des en-têtes traceparent |
| Aucun service dans l'UI | Trace hors fenêtre de recherche | Contrôler l'horodatage et le lookback |
| Spans orphelins | Horloges désynchronisées | Synchroniser les serveurs en NTP |
| Conteneur qui redémarre | Chemin de stockage non inscriptible | Utiliser un dossier accessible en écriture |
# Lister les services vus par Jaegercurl http://localhost:16686/api/services
# Rechercher les traces d'un servicecurl "http://localhost:16686/api/traces?service=checkout&lookback=1h&limit=5"FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »SPAN_STORAGE_TYPE décrivent la v1.À retenir
Section intitulée « À retenir »- Jaeger trace les requêtes de bout en bout dans les microservices : span, trace et baggage sont les trois notions à connaître.
- La v2 repose sur l'OpenTelemetry Collector : configuration en YAML, format OTLP par défaut, plus de variables d'environnement à la v1.
- L'image all-in-one démarre Jaeger complet en une commande, récepteur OTLP et UI inclus.
- On instrumente avec le SDK OpenTelemetry, jamais avec une bibliothèque propre à Jaeger, pour rester libre du backend.
- En production, stockage distribué et sampling : Elasticsearch ou Cassandra pour le volume, échantillonnage pour le coût.