Distributed tracing: cum urmărești un request prin zece servicii
Distributed tracing urmărește un request de la intrare până la baza de date prin toate serviciile. Spans, trace ID, context propagation, OpenTelemetry.
Cuprins
- Ce este o trace distribuită mai exact?
- Care sunt componentele de bază: spans, trace ID, context propagation?
- Cum se poziționează OpenTelemetry ca standard de facto?
- Cum adaugi tracing progresiv într-un sistem existent (la crawlerra)?
- Care sunt capcanele operaționale: sampling, cost de storage, PII în spans?
Distributed tracing este tehnica de urmărire a unui singur request de la intrarea în sistem până la ultimul serviciu atins, înregistrând fiecare pas ca un span cu durată, atribute și relația cu vecinii săi. Rezultatul este un grafic de tip arbore, vizualizat ca un Gantt chart, care arată exact unde s-a petrecut fiecare milisecundă dintr-o cerere lentă sau eșuată.
Termenul devine relevant odată ce un request traversează mai mult de un proces: un serviciu apelează altul, un job asincron produce un mesaj, un apel extern consumă timp. Fără tracing, știi că latența a crescut; cu tracing, știi care span anume a crescut-o și de ce.
Ce este o trace distribuită mai exact?
O trace este colecția ordonată de span-uri care descriu parcursul unui singur request prin sistem, identificată printr-un trace ID unic. Nu este o înregistrare continuă a tot ce se întâmplă în sistem, ci o fotografie a unui singur fir de execuție, de la primul byte primit până la ultimul byte trimis înapoi.
Diferența față de logs și metrics este perspectiva: log-urile sunt evenimente punctuale, metricile sunt agregate, trace-urile sunt relații. Un log îți spune că „query-ul a durat 300 ms"; trace-ul îți spune că acel query a fost declanșat de request-ul utilizatorului X, ca parte din span-ul Y, el însuși fiu al span-ului Z inițiat de serviciul frontal. Contextul este tot.
Care sunt componentele de bază: spans, trace ID, context propagation?
Trei concepte sunt fundamentale pentru orice implementare de tracing:
- Span. Unitatea de bază. Reprezintă o operație discretă: un apel HTTP, un query SQL, o operație de citire din cache, un apel extern. Are start, end, durată calculată și atribute descriptive. Span-urile se leagă prin relații „child-of" sau „follows-from", formând arborele trace-ului.
- Trace ID. Un identificator unic (128 de biți, generat la intrarea în sistem) care leagă toate span-urile unui singur request, indiferent prin câte servicii trece. Fără el, span-urile rămân fragmente izolate, imposibil de corelat după fapt.
- Context propagation. Mecanismul prin care trace ID-ul și span ID-ul curent se transmit între servicii. Standardul actual este W3C Trace Context (recomandat W3C din 2021): headerele HTTP
traceparentșitracestate. Dacă un serviciu intermediar nu propagă aceste headere, lanțul de trace se rupe.
Cele trei componente funcționează împreună. Fără span-uri, nu ai granularitate. Fără trace ID, nu ai corelație. Fără context propagation, ai corelație doar în interiorul unui singur proces. Tracing-ul parțial este mai frustrant decât niciun tracing, pentru că te lasă cu date incomplete în mijlocul unui incident care afectează direct SLA-ul.
Cum se poziționează OpenTelemetry ca standard de facto?
OpenTelemetry este proiectul CNCF (Cloud Native Computing Foundation) care unifică SDK-urile, formatele de date și protocolul de transport pentru toți cei trei piloni de telemetrie: logs, metrics și traces. Înainte de OpenTelemetry, fiecare vendor (Jaeger, Zipkin, Datadog, Honeycomb) impunea propriul SDK; instrumentai o dată pentru un vendor și te blocai acolo.
OpenTelemetry rezolvă problema prin separarea instrumentării de transport. SDK-ul tău emite date în formatul OTLP (OpenTelemetry Protocol); backend-ul ales (Tempo, Jaeger, Datadog, Honeycomb sau orice altceva compatibil) le primește prin același protocol. Schimbi backend-ul fără să atingi codul aplicației.
Câteva lucruri practice despre cum funcționează SDK-ul:
- Auto-instrumentarea acoperă bibliotecile populare fără cod suplimentar: Spring Boot, gRPC, JDBC, HTTP clients. Span-urile pentru query-uri SQL și apeluri HTTP apar automat.
- Instrumentarea manuală adaugă span-uri custom pentru operații specifice business-ului tău: procesarea unui eveniment, aplicarea unei reguli, execuția unui calcul intern.
- Exporterul trimite datele colectate la backend. Poți exporta direct (aplicație către Tempo) sau prin intermediul unui OpenTelemetry Collector, care poate face batch-ing, filtrare și routing către mai multe destinații simultan.
Rezultatul este că OpenTelemetry a devenit standardul de instrumentare indiferent de backend. Jaeger, Tempo și Datadog acceptă toate OTLP. Instrumentezi o singură dată și rămâi liber să schimbi backend-ul după ce înțelegi ce ai nevoie cu adevărat.
Cum adaugi tracing progresiv într-un sistem existent (la crawlerra)?
Pe stack-ul nostru de observabilitate pentru cele șase produse pe care le monitorizăm1, Tempo este componenta dedicată trace-urilor, parte din aceeași infrastructură care găzduiește metricile (Prometheus) și log-urile (Loki). Adăugarea de tracing pentru un serviciu nou înseamnă instrumentare cu OpenTelemetry SDK în aplicație și configurarea exporterului către Tempo.
Abordarea corectă pentru un sistem existent este progresivă, nu big-bang. Câțiva pași care funcționează indiferent de stack:
- Începe cu auto-instrumentarea. OpenTelemetry SDK pentru Spring Boot sau alt framework popular instrumentează automat apelurile HTTP de intrare și ieșire, query-urile JDBC și operațiunile comune. Primești span-uri fără să modifici codul aplicației.
- Adaugă context propagation la interfețele asincrone. Job-urile asincrone, mesajele dintr-un queue sau stream și callback-urile sunt locurile unde lanțul de trace se rupe cel mai des. Propagarea explicită a trace ID-ului acolo recuperează corectitudinea trace-ului.
- Instrumentare manuală pentru operațiile importante din perspectiva business-ului. Un span custom pe operațiile de import de date, procesarea unui webhook sau aplicarea unei reguli de preț îți dă context direct în trace, fără să sapi prin log-uri.
Honest-ul framing este că adoptarea per serviciu depinde de nevoie. Infrastructura există; decizia de când să instrumentezi un serviciu nou ține de frecvența incidentelor neexplicate și de complexitatea flow-ului. Serviciile cu latență impredictibilă sau cu dependențe externe multiple câștigă cel mai mult din tracing.
Care sunt capcanele operaționale: sampling, cost de storage, PII în spans?
Tracing-ul are propriile capcane, distincte de cele ale log-urilor și metricilor. Cele mai costisitoare:
- Sampling greșit. Head-based sampling (decizia se ia la intrare) la rate mici (1%) pierde exact trace-urile pe care le-ai vrea: erorile rare, request-urile lente ocazionale. Un punct de start mai bun: 100% pentru request-urile cu erori sau latență peste un prag, sampling agresiv pentru succesele rapide. Tail-based sampling (decizia după finalizarea trace-ului) rezolvă asta sistematic, dar necesită un OpenTelemetry Collector cu buffer.
- PII în atributele span-urilor. Auto-instrumentarea include automat tot ce poate: query SQL complet, URL complet, headere HTTP. Dacă query-ul SQL include
WHERE email = 'user@example.com'sau URL-ul include un token în query string, trace-ul tău conține PII. Revizuiește ce captează auto-instrumentarea și adaugă sanitizare la exporter sau la nivel de Collector. Este o cerință practică sub GDPR, nu doar bună practică. - Costul de stocare la volum mare. Fiecare span este un obiect structurat cu timestamp, atribute, relații și metadata, mai scump decât o valoare numerică de metrică. La volum mare fără sampling, un backend self-hosted se umple rapid. Retenție scurtă și sampling calibrat sunt singurele pârghii.
- Instrumentare fără context propagation la granițe. Un serviciu care nu propagă headerele W3C Trace Context rupe trace-ul. Dacă un singur serviciu din zece nu propagă trace ID-ul, toate trace-urile care îl traversează apar trunchiate. Validarea că fiecare interfață de intrare și ieșire propagă corect este un pas de integrare, nu o verificare opțională.
- Cardinalitate mare pe atribute. Un atribut
user_idcu milioane de valori distincte nu e o problemă pentru Tempo, care indexează după trace ID. Poate fi o problemă pentru backend-uri care indexează toate atributele. Verifică înainte să adaugi atribute cu cardinalitate înaltă.
Capcanele de mai sus sunt detectabile dacă ai o practică de site reliability engineering care include revizuirea periodică a costurilor de telemetrie și a calității trace-urilor. Un runbook pentru „trace-uri incomplete sau lipsă" este un instrument concret care previne diagnosticarea greșită a incidentelor.
- Stack-ul de observabilitate crawlerra acoperă șase produse în producție. Tempo este componenta dedicată trace-urilor din același cluster self-hosted care include Prometheus și Loki.
[ops.products_count]
Întrebări frecvente
Care e diferența dintre o trace și un log?
Un log descrie un eveniment izolat; o trace descrie călătoria unui singur request prin mai multe servicii. Log-ul îți spune că „serviciul A a returnat eroare la 14:03:22". Trace-ul îți spune că request-ul a petrecut 2 ms în serviciul A, 180 ms în serviciul B (din care 160 ms așteptând un query Postgres), și a eșuat la serviciul C. Fără trace, știi că e ceva stricat; cu trace, știi exact unde și de ce.
Am nevoie de distributed tracing dacă am un singur serviciu?
Nu în sens distribuit, dar tracing-ul în-process rămâne util. Chiar și cu un singur serviciu, un trace îți arată care query SQL sau care apel extern a consumat cel mai mult timp dintr-un request lent. Câștigul cel mai mare apare când ai cel puțin două servicii sau când un serviciu extern (o platformă de billing, un API de plăți) este parte din flow.
OpenTelemetry sau Jaeger sau Datadog?
OpenTelemetry este standard de instrumentare, nu backend de stocare. Jaeger și Datadog sunt backend-uri care acceptă date OpenTelemetry. Instrumentezi o singură dată cu OpenTelemetry SDK, apoi trimiți datele unde vrei: Jaeger, Tempo, Honeycomb, Datadog sau orice altceva compatibil cu protocolul OTLP. Nu ești blocat de un vendor.
Cât costă stocarea trace-urilor?
Mai mult decât te aștepți la volum mare, mai puțin decât crezi la volum mic. Un trace conține toate span-urile unui request, fiecare cu atribute, timestamps și metadata. La câteva sute de request-uri pe secundă, cu sampling de 1% pe trafic normal și 100% pe erori, retenția de 7 zile este gestionabilă pe un self-hosted Tempo. La mii de request-uri pe secundă fără sampling, costul de stocare devine rapid argumentul principal pentru a alege un backend SaaS cu tier de retenție.
Ce este W3C Trace Context?
Un standard HTTP pentru propagarea trace ID-ului între servicii, definit de W3C ca recomandare în 2021. Specifică două headere: traceparent (care conține trace ID, span ID și flags) și tracestate (pentru date vendor-specifice). OpenTelemetry îl implementează by default; serviciile care nu-l suportă rup lanțul de trace la interfața lor.