Grafana Tempo 工具手册
Tempo 3.0 先拆开近期与历史
Grafana Tempo 把长期 Trace 保存为对象存储中的 Parquet 块,TraceQL 再按 trace ID、属性和结构查询这些数据。Tempo 3.0 重做了写入架构:微服务模式由 Distributor 写入 Kafka 兼容缓冲,live-store 服务近期查询,block-builder 构建长期块,backend scheduler/worker 负责压实与保留。它不再使用 2.x ingester 和 compactor 作为主路径。
官方 Tempo 3.0 release notes 和迁移指南明确说明这不是无感升级。本文固定 3.0.2 为实验基线,不能拿 2.x 配置字段拼接成 3.0 生产配置。
单体模式也要证明对象块存在
Tempo 没有独立查询 UI,通常由 Grafana 访问其 HTTP API。实验使用 Linux/Bash、Docker、curl 和 OpenSSL,先建立独立目录、容器名和卷名,再保存 $TRACE_LAB_DIR/tempo.yaml。本地 backend 只适用于开发验证:
set -euo pipefail
TRACE_LAB_DIR=$(mktemp -d -t tempo-lab-XXXXXXXX)
TEMPO_NAME=$(basename "$TRACE_LAB_DIR")
TEMPO_VOLUME="${TEMPO_NAME}-data"target: all
stream_over_http_enabled: true
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
zipkin:
endpoint: 0.0.0.0:9411
storage:
trace:
backend: local
wal:
path: /var/tempo/wal
local:
path: /var/tempo/blocks
live_store:
flush_check_period: 1s
max_trace_idle: 2s
max_trace_live: 5s
max_block_duration: 5s
overrides:
defaults:
compaction:
block_retention: 24h短时间参数只为让实验快速成块,不是生产容量建议。保留期使用 Tempo 配置参考中的 overrides.defaults.compaction;compaction 不能直接放在 YAML 顶层。准备一条 Zipkin v2 兼容 Span,并启动命名卷:
TRACE_ID=$(openssl rand -hex 16)
SPAN_ID=$(openssl rand -hex 8)
NOW_US=$(($(date +%s) * 1000000))
cat > "$TRACE_LAB_DIR/span.json" <<JSON
[{"traceId":"$TRACE_ID","id":"$SPAN_ID","kind":"SERVER","name":"POST /checkout","timestamp":$NOW_US,"duration":820000,"localEndpoint":{"serviceName":"checkout-api"},"tags":{"deployment.environment":"trace-lab"}}]
JSON
docker volume create --label "s04.lab=$TEMPO_NAME" "$TEMPO_VOLUME"
docker run -d --name "$TEMPO_NAME" --label "s04.lab=$TEMPO_NAME" \
-p 127.0.0.1:3200:3200 \
-p 127.0.0.1:14317:4317 \
-p 127.0.0.1:14318:4318 \
-p 127.0.0.1:19411:9411 \
-v "$TRACE_LAB_DIR/tempo.yaml:/etc/tempo.yaml:ro" \
-v "$TEMPO_VOLUME:/var/tempo" \
grafana/tempo:3.0.2 -config.file=/etc/tempo.yaml
tempo_http() { curl -q --noproxy '*' --fail-with-body --max-time 5 "$@"; }
tempo_ready() { tempo_http --retry 20 --retry-connrefused --retry-delay 2 http://127.0.0.1:3200/ready; }
tempo_query() { tempo_http --retry 15 --retry-all-errors --retry-delay 2 "http://127.0.0.1:3200/api/traces/$TRACE_ID" "$@"; }
tempo_ready
tempo_http -X POST http://127.0.0.1:19411/api/v2/spans \
-H 'Content-Type: application/json' --data-binary @"$TRACE_LAB_DIR/span.json"
tempo_queryready 只证明进程可服务,第一次按 ID 取回可能命中 live-store 或 WAL。长期持久化要看到 flush 计数增长、完成队列归零、对象目录出现 meta.json 和 data.parquet,重启后还要能查询同一 trace ID。
# Tempo 镜像没有 Shell。辅助容器只读挂载本次卷,不接入网络。
docker run --rm --network none --read-only \
-v "$TEMPO_VOLUME:/evidence:ro" node:24-bookworm-slim node -e '
const fs = require("node:fs");
let attempts = 0;
const timer = setInterval(() => {
const files = fs.readdirSync("/evidence/blocks", {recursive:true});
if (files.some(p => p.endsWith("/meta.json")) && files.some(p => p.endsWith("/data.parquet"))) {
console.log(files.filter(p => /\/(meta.json|data.parquet)$/.test(p)).join("\n"));
clearInterval(timer);
} else if (++attempts === 30) { console.error("等待对象块超时"); process.exit(1); }
}, 1000);'
docker restart "$TEMPO_NAME"
tempo_ready
tempo_query近期可查但重启后丢失时,检查 /var/tempo/blocks 是否实际挂载、failed flush 指标、目录权限和生效配置路径。单机本地盘不适用于分布式模式,因为不同组件看不到同一历史块。
TraceQL 不是零成本扫描
对象块按 tenant 和 block 分隔,块中包含元数据、Parquet 数据与用于缩小候选范围的结构。按 trace ID 可以利用块元数据和过滤结构,任意属性搜索仍可能读取大量 Parquet 页。高频属性可配置 dedicated columns,但列数、基数和查询模式都要从真实负载测量。
每日写入字节 ≈ 请求数 × 采样率 × 每 Trace Span 数 × 平均 Span 字节
长期存储 ≈ 每日写入字节 × 保留天数 × 压实系数
查询成本 ≈ 候选块数 × 每块读取字节 + 对象请求与出网超大 Trace、长字符串属性、TraceQL metrics、Kafka 保留、block-builder scratch disk 和跨区对象访问都要单独预算。对象存储便宜不等于查询免费;宽时间窗和高并发会把 GET、LIST、CPU 与出网成本集中放大。
Tempo 3.0 微服务写路径使用 durable buffer 解耦接收和成块,live-store 与 block-builder 消费各自数据。查询端必须同时看近期和历史;Kafka lag 覆盖查询时间范围时,默认可选择失败而不是返回静默不完整结果。这个取舍应进入 SLO 和告警,而不是被 UI 空白掩盖。
X-Scope-OrgID 不是认证
默认配置未启用多租户。按官方多租户配置,在实验 tempo.yaml 顶层增加 multitenancy_enabled: true,执行 docker restart "$TEMPO_NAME" 和 tempo_ready。原先无租户写入的数据属于单租户存储空间,不会自动迁入 team-a;必须带头重新写入一次才能验证隔离。
启用后写入和查询携带 X-Scope-OrgID,对象也按 tenant 隔离。但客户端可以自行伪造 header,Tempo 不验证调用者身份。生产入口必须由可信网关认证用户或工作负载,丢弃外部传入值,再注入授权后的租户头。
tempo_http -X POST http://127.0.0.1:19411/api/v2/spans \
-H 'X-Scope-OrgID: team-a' -H 'Content-Type: application/json' \
--data-binary @"$TRACE_LAB_DIR/span.json"
tempo_query -H 'X-Scope-OrgID: team-a'
NO_TENANT=$(curl -q --noproxy '*' --max-time 5 -sS -o /dev/null -w '%{http_code}' \
"http://127.0.0.1:3200/api/traces/$TRACE_ID")
OTHER_TENANT=$(curl -q --noproxy '*' --max-time 5 -sS -o /dev/null -w '%{http_code}' \
-H 'X-Scope-OrgID: team-b' "http://127.0.0.1:3200/api/traces/$TRACE_ID")
test "$NO_TENANT" = 401 && test "$OTHER_TENANT" = 404 || {
printf '租户验证失败:missing=%s other=%s\n' "$NO_TENANT" "$OTHER_TENANT" >&2; exit 1;
}缺头应被拒绝,错误 tenant 应查不到其他租户数据。Collector 的写身份、Grafana 数据源的读身份、对象存储读写身份和删除权限要分开。租户 overrides 能限制写入速率、Trace 大小、查询并发与保留,但修改时要确认未显式字段不会意外回到零值。
从空查询判断故障层
所有查询都空时,先查应用采样、Collector accepted/refused、exporter send_failed 与 Distributor 拒绝。按 ID 可查而 TraceQL 属性查询为空时,检查属性 scope、时间范围、dedicated columns 与查询限制。近期可查、历史为空时,检查 block-builder lag、对象存储权限、blocklist、backend jobs 和新块格式。
零散根 Span 多半来自 W3C/B3 上下文传播或异步线程,不属于 Tempo 存储问题。Span 中的令牌、Cookie、SQL 参数、完整正文与个人信息应在 SDK 或 Collector 侧删除;对象存储持久化后再治理成本更高。
3.0 迁移不能原地降级
单体部署可以迁移配置后升级,微服务部署应并行运行 2.x 与 3.0,逐步切写入和查询。3.0 移除了 ingester、compactor 及多项旧字段,旧告警和 dashboard 也要同步调整。对象块格式、Kafka offset 和查询完整性验证通过前,不得销毁旧组件。
新旧后端短期双写会增加 Collector 队列、出口带宽和存储费用。比较接收数、完整 Trace 比例、近期与历史查询、错误租户隔离和单位成本后再扩大。回滚是把流量切回仍在运行的 2.x,不是把 3.0 数据卷或对象桶删除。
(
set -euo pipefail
: "${TRACE_LAB_DIR:?}" "${TEMPO_NAME:?}" "${TEMPO_VOLUME:?}"
test "$(basename "$TRACE_LAB_DIR")" = "$TEMPO_NAME" || exit 1
test "$TEMPO_VOLUME" = "${TEMPO_NAME}-data" || exit 1
test "$(docker inspect -f '{{index .Config.Labels "s04.lab"}}' "$TEMPO_NAME")" = "$TEMPO_NAME" || exit 1
test "$(docker volume inspect -f '{{index .Labels "s04.lab"}}' "$TEMPO_VOLUME")" = "$TEMPO_NAME" || exit 1
docker rm -f "$TEMPO_NAME"
docker volume rm "$TEMPO_VOLUME"
)
# 配置与合成 Span 留在 $TRACE_LAB_DIR,核对路径后手动移入回收站。清理只属于唯一命名的本地实验。生产 Tempo 的最低证据包括近期查询、对象块落盘、重启历史查询、Kafka lag、backend job、错误租户隔离和保留执行;缺少其中任何一项,都不能仅凭 Grafana 里出现一条 Trace 判断系统已经可靠。
权威资料与规范地址
- Tempo 3.0 release notes:https://grafana.com/docs/tempo/latest/release-notes/v3-0/
- 迁移指南:https://grafana.com/docs/tempo/latest/set-up-for-tracing/setup-tempo/migrate-to-3/
- Tempo 配置参考:https://grafana.com/docs/tempo/latest/configuration/
- 多租户配置:https://grafana.com/docs/tempo/latest/operations/manage-advanced-systems/multitenancy/
