🟡 Intermediate

Grafana Tempo — Distributed Tracing

Grafana Tempo je open-source backend pre distribuovaný tracing, ktorý sa zameriava na jediný cieľ: ukladať a slúžiť 100 % traces za zlomok ceny iných riešení. Dosahuje to rovnakou filozofiou ako Loki — neindexuje obsah, ukladá traces komprimované do objektového storage (S3, GCS, Azure Blob) a indexuje iba trace_id.

Tempo tvorí tretí pilier Grafana LGTM stacku (Loki pre logs, Grafana pre vizualizáciu, Tempo pre traces, Mimir pre metrics).


Prečo distribuované tracing?

V monolite vám stačí stack trace. V mikroservisovej architektúre jedna HTTP requesta prechádza cez 5-10 služieb, každá volá DB, cache, externú API. Keď niečo zlyhá alebo spomalí, z logov nezistíte, kde je problém — logy sú samostatné riadky v samostatných službách.

Distribuovaný tracing sleduje request naprieč službami ako strom spánov (spans):

Trace: GET /checkout  (850ms)
├── span: api-gateway              (30ms)
├── span: auth-service             (45ms)
│   └── span: redis.get            (2ms)
├── span: cart-service             (120ms)
│   ├── span: postgres.SELECT      (80ms)
│   └── span: redis.get            (5ms)
└── span: payment-service          (650ms)  ← bottleneck!
    ├── span: stripe.charge        (580ms)
    └── span: postgres.INSERT      (40ms)

Okamžite vidíte, že 76 % času trávi čakaním na Stripe API. Bez tracingu by ste debugovali hodiny.


Tempo vs Jaeger vs Zipkin

Aspekt Jaeger Zipkin Tempo
Storage Cassandra, Elastic, Badger MySQL, Cassandra, Elastic S3, GCS, Azure, local
Indexovanie Trace search (full index) Trace search Len trace_id + TraceQL
Sampling potrebný Áno (head sampling) Áno Nie — 100 % traces možné
Cena/TB Vysoká (DB) Vysoká (DB) Nízka (object storage)
Query UI Natívne Natívne Cez Grafana
Protokoly OTLP, Jaeger, Zipkin Zipkin OTLP, Jaeger, Zipkin

Najväčšia výhoda Tempa: nemusíte znižovať sampling rate na 1 % z obáv o cenu. Môžete ukladať 100 % traces a problémy nájdete aj tie, ktoré sa dejú raz za deň.

Kompromis: full-text vyhľadávanie naprieč všetkými traces je v Tempe obmedzené (TraceQL funguje, ale nie je to Elastic). Ak poznáte trace_id (napr. z logu cez TraceID label v Loki), Tempo je neporaziteľné. Ak chcete nájsť všetky traces, kde sa stalo X, TraceQL si poradí, ale pomalšie než Jaeger s full indexom.


Architektúra Tempo

OTel Collector / Agent
        │ OTLP/gRPC
        ▼
┌──────────────┐
│ Distributor  │  validate, shard by trace_id
└──────┬───────┘
       ▼
┌──────────────┐
│  Ingester    │  in-memory → compacted blocks → S3
└──────┬───────┘
       ▼
┌──────────────┐
│ Object Store │  (S3, GCS, Azure, filesystem)
└──────┬───────┘
       ▲
┌──────────────┐
│   Querier    │  reads blocks + ingesters
└──────┬───────┘
       ▼
┌──────────────┐
│ Query Front. │  splits queries, caches results
└──────────────┘
  • Distributor — prijíma spany, validuje, shard-uje podľa trace_id.
  • Ingester — agreguje spany do trace, drží v pamäti, pravidelne flushuje ako komprimovaný block.
  • Compactor — merguje malé blocks do väčších, aplikuje retention.
  • Querier — obsluhuje HTTP endpoint pre trace fetch a TraceQL search.
  • Metrics generator (voliteľné) — generuje service graph a RED metriky z traces do Prometheu.

TraceQL — query language

TraceQL (od Tempo 2.0+) je jazyk na vyhľadávanie trace-ov:

# Všetky traces, kde bola HTTP status 500
{ status = error }

# Traces v service "checkout" s duration > 1s
{ resource.service.name = "checkout" && duration > 1s }

# Traces obsahujúce span, kde Stripe volal longer than 500ms
{ name = "stripe.charge" && duration > 500ms }

# Kombinácia
{ resource.service.name = "api" } >> { resource.service.name = "payment" && duration > 1s }

Operátor >> znamená "descendant span". Query sa vyhodnocuje nad blocks v object storage — pre dlhé obdobia je pomalšie než Jaeger, pre krátke (posledné hodiny) rýchle.


OpenTelemetry → Tempo integrácia

Tempo prijíma traces cez OTLP (OpenTelemetry protocol), nie je viazané na konkrétny SDK. Typický pipeline:

App (otel-sdk) ──► OTel Collector ──► Tempo

Tempo config (minimálny)

server:
  http_listen_port: 3200

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318

ingester:
  trace_idle_period: 10s
  max_block_duration: 5m

compactor:
  compaction:
    block_retention: 336h   # 14 dni

storage:
  trace:
    backend: s3
    s3:
      bucket: tempo-traces
      endpoint: s3.eu-central-1.amazonaws.com
      region: eu-central-1

Aplikačná inštrumentácia (Python OTel SDK)

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

provider = TracerProvider()
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(endpoint="http://tempo:4317", insecure=True)
    )
)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

@app.route("/checkout")
def checkout():
    with tracer.start_as_current_span("checkout") as span:
        span.set_attribute("user_id", request.user.id)
        charge_card()
        write_order()
        return "ok"

Request → spans → OTel SDK → Tempo → Grafana dashboard.


Prepojenie traces ↔ logs ↔ metrics v Grafana

Toto je killer-feature celého LGTM stacku. V Grafana data sources:

  • Loki → Tempo: nastavíte derivedFields v Loki data source tak, že každý log so trace_id=abc123 vygeneruje klikateľný odkaz, ktorý vás v Tempo otvorí presne na tom trace.
  • Tempo → Loki: v Tempo data source nastavíte "Trace to logs", čo v trace view zobrazí tlačidlo "View logs for this span". Otvorí Loki s filtrom {pod="X"} |= "trace_id=abc123".
  • Tempo → Metrics: Service Graph panel v Grafana vizualizuje hovory medzi službami s RED metrikami (Rate, Errors, Duration), ktoré Tempo metrics_generator zapíše do Mimir/Prometheus.

Výsledok: z alertu (metrika) → trace (prečo) → log (detail) za 3 kliknutia.


Sampling stratégie

Aj keď Tempo dokáže ukladať 100 % traces, pri extrémnych objemoch (> 100k spans/sec) budete chcieť sampling:

Head sampling

Rozhoduje sa na začiatku (SDK). Jednoduché, lacné, ale stratíte pomalé request, ak sa na ne nenarazilo.

Tail sampling

Rozhoduje sa po zozbieraní celého trace. OTel Collector má tail_sampling processor:

processors:
  tail_sampling:
    policies:
      - name: errors
        type: status_code
        status_code: { status_codes: [ERROR] }
      - name: slow
        type: latency
        latency: { threshold_ms: 1000 }
      - name: random
        type: probabilistic
        probabilistic: { sampling_percentage: 5 }

Týmto spôsobom ukladáte 100 % chýb a pomalých requestov, ale iba 5 % zvyšku.


Produkčné tipy

  • Block retention — default 14 dní je rozumný kompromis medzi históriou a nákladmi.
  • Search limits — TraceQL nad dlhým oknom (týždne+) je pomalé, preto nastavte max_search_duration.
  • Compactor — musí bežať, inak sa z ingester blocks nikdy neflušne do konsolidovaných blockov.
  • Multi-tenancyX-Scope-OrgID header, rovnako ako Loki a Mimir.
  • Trace → metrics — zapnite metrics_generator a dostanete service graph zadarmo.
  • Retention warning — object storage je lacné, ale stovky TB traces vám tiež zaplnia S3. Nastavte lifecycle rules.

Zhrnutie

Tempo prináša distribuovaný tracing do éry, kde je cena za GB rozhodujúca. Ak používate Grafana, Prometheus a Loki, Tempo je logický doplnok — tým istým spôsobom uvažovania (labels, LogQL/TraceQL, object storage) a tým istým UI. Pre tímy, ktoré zvažujú Jaeger, je hlavná otázka: "Chceme 100 % traces za lacný peniaz alebo full-text search za drahý?" Tempo je odpoveď na prvú.

Spolu s OpenTelemetry (standard) a Grafana (UI) tvoria dnes triáda, ktorá definuje cloud-native observability.

Odkazy