起因
distributed trace 工具:
- Jaeger:经典 + 自托管 + 存 Cassandra / ES
- Zipkin:更老
- Tempo(Grafana Labs):新选择,存 S3
trace 数据量大(每 request 多 span,TB / day),存 Cassandra / ES 贵。
Tempo 设计为"object storage native trace store",类似 Loki for log。
Tempo 特点
- 存 trace blob 到 S3 / GCS(极便宜)
- 只索引 trace ID(不索引 attribute)→ 不能按 service / tag 全文搜
- query 模式:先用 metric 找到时段 → 拿 trace ID → 查 trace
对应 metric/log/trace 思路:
metric (Prometheus / Mimir)
↓ 发现 spike 时段
log (Loki)
↓ 找 trace_id
trace (Tempo)
↓ 详细看 trace
装
# docker-compose
services:
tempo:
image: grafana/tempo:2.5.0
command: ['-config.file=/etc/tempo.yaml']
ports:
- 3200:3200 # HTTP
- 4317:4317 # OTLP gRPC
volumes:
- ./tempo.yaml:/etc/tempo.yaml
- tempo-data:/tmp/tempo
tempo.yaml:
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
storage:
trace:
backend: s3
s3:
bucket: my-traces
endpoint: s3.amazonaws.com
region: us-east-1
compactor:
compaction:
block_retention: 720h # 30 day
ingest
应用 OTEL SDK 发 trace 到 Tempo (or otel-collector → Tempo):
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(endpoint='tempo:4317')
或通过 otel-collector 中转(推荐生产)。
Grafana 查
Grafana 加 Tempo data source:
URL: http://tempo:3200
Explore → Tempo → "Search" tab:
- search by trace ID(直接 lookup)
- search by service name + tag(TraceQL)
- duration / status filter
{service.name="api" && duration > 500ms && status = error}
返回 trace 列表 → 点开看 span tree。
TraceQL (Tempo query language)
类 PromQL for trace:
# 时间窗口内 service=api 的 trace
{service.name="api"}
# 慢 trace
{duration > 1s}
# 错误 trace
{status = error}
# 跨 span 关系
{ name="GET /api/users" } >> { name="db.query" && duration > 100ms }
# 父 span 是 "GET /api/users" + 子 span 含慢 DB query
强大 + 不需要 fulltext index。
跟 metric 关联
Grafana panel:
metric: rate(http_requests{status="500"})
↓ click data point
trace search: status=error around timestamp
↓
list of error trace IDs
一键从 metric spike 跳到具体 trace。
debug 神器。
跟 log 关联
log 里写 trace_id:
import logging
from opentelemetry import trace
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] [trace_id=%(otelTraceID)s] %(message)s'
)
handler.setFormatter(formatter)
Loki 查 log:
{service="api"} |= "error"
# 看到 trace_id=abc123
Grafana 自动识别 trace_id → 点击跳 Tempo 拉 trace。
存储成本对比
| Jaeger (ES) | Tempo (S3) | |
|---|---|---|
| 1 TB trace/day | ~$2000/月(ES cluster) | ~$50/月(S3) |
| query latency | < 100ms | ~1s(拉 S3) |
| index 灵活 | 任 attr | trace ID + 部分 attr |
Tempo cost 1-2 个量级低。
trade-off:query 慢(拉 S3 + scan)+ 索引弱。
sampling
trace 量大 → 不全存。
两种 sampling:
- head sampling:应用端决定(如 1%)
- tail sampling:collector buffer 全 trace + 决定(如 100% error + 1% normal)
tail 更智能但需 collector 资源(otel-collector tail_sampling processor)。
与 Jaeger 对比
| Jaeger | Tempo | |
|---|---|---|
| 存储 | Cassandra / ES / Memory | S3 / GCS |
| query 强 | 强(全索引) | 中(trace ID) |
| 成本 | 高 | 低 |
| 集成 | Jaeger UI | Grafana |
| 部署 | 多组件 | 单 binary |
Jaeger 适合:低 volume + 重 ad-hoc query。
Tempo 适合:高 volume + 接受 metric/log-driven trace lookup。
OpenTelemetry → 后端无关
只要应用用 OTEL SDK,后端可换:
- Jaeger
- Tempo
- Honeycomb (SaaS)
- Datadog APM (SaaS)
- Splunk
代码不改。
真实部署
我们 prod:
- 100 微服务 + 10w QPS
- 50 GB trace/day(10% sampling)
- Tempo + S3 backend
- 30 day retention
- Grafana 主入口
成本:
- S3:50 GB × 30 day × $0.023/GB = $35/月
- Tempo compute (2 small instance):$30/月
- 总:< $100/月
Jaeger + ES 等价:> $1000/月。
体验 trade-off:
- query 1-3s(vs Jaeger < 200ms)
- 必须知道 trace_id 或者跨服务 search(不能全文 search trace 内容)
可接受。
metrics generator (Tempo extra)
Tempo 能从 trace 生成 metric:
metrics_generator:
registry:
external_labels:
source: tempo
processor:
service_graphs:
span_metrics:
自动生成:
traces_spanmetrics_calls_total{service, operation}(call rate)traces_spanmetrics_latency_*(latency histogram)- service graph (which service calls which)
替代 RED metric exporter,从 trace 推导。
与 Datadog APM 对比
| Tempo | Datadog APM | |
|---|---|---|
| 成本 | $100/月 | $1000-10000/月 |
| 部署 | self-host | SaaS |
| UX | Grafana 中 | 极好 |
| 集成 | OTEL + 自己 stack | Datadog 全生态 |
预算大 + 想 batteries-included → Datadog。
预算敏感 / 已有 Grafana → Tempo。
踩过的坑
-
S3 cost spike:put request 计费,频繁小 trace 飞速 → 加
compactor 合并 block。 -
query 超时:跨大时间窗口 search → S3 拉量大 → timeout。
缩窗口或者 use trace ID lookup。 -
OTEL 版本兼容:Tempo 升级时 OTLP schema 变 → 接收旧 SDK trace
失败。pin SDK + Tempo 版本。 -
head sampling 漏关键 trace:随机 1% 漏掉了 error trace → 没
trace 可看。tail sampling 解决。 -
trace too large:单 trace 几千 span → UI 慢。控制 cardinality。
登录后参与评论。