【云原生监控】 Grafana/Prometheus/Promtail/Loki/Tempo

Grafana/Prometheus/Promtail/Loki/Tempo

监控栈文档:Grafana / Prometheus / Promtail / Loki / Tempo

loop-agent 的本地可观测性栈:链路(trace) + 日志(log) + 指标(metric) 三支柱统一在 Grafana 查询。Jaeger 已下线,由 Tempo 接管链路。


1. 组件与职责

组件 版本 职责 地址 容器内数据目录
Grafana 11.0.0 统一查询/可视化 UI(三种数据源) http://localhost:3000 /var/lib/grafana
Tempo 2.5.0 链路(trace)后端,收 OTLP 查询 :3200 / OTLP :4317,:4318 /var/tempo
Loki 2.9.8 日志(log)后端 :3100 /loki
Promtail 2.9.8 日志采集, 推给 Loki (无 UI) :9080 /promtail
Prometheus 2.52.0 指标(metric)后端,抓应用指标 :9090 /prometheus
Pushgateway 1.9.0 短命 CLI 批处理任务推指标的中转 :9091 (无状态)

角色一句话:Tempo 存链路、Loki 存日志、Prometheus 存指标、Promtail 送日志、Grafana 统一看


2. 数据流图


3. 组件如何相互作用

  1. 应用 → Tempo(链路) :应用用 OpenTelemetry 走 OTLP gRPC 发到 localhost:4317OTEL_EXPORTER_OTLP_ENDPOINT)。每个 span 带 gen_ai.*(模型/token/prompt/completion)等属性;根 span turn.Nrun_id/session_id/user_id/model。trace_id 带年月日(见第 7 节)。
  2. 应用 → trace_archive → Promtail → Loki(日志) :应用把每步事件写成 ./trace_archive/<run_id>.jsonl(每行一个 JSON 事件)。Promtail 挂载该目录、tail 新增行,解析 JSON 后把 run_idetype(事件类型)提为标签,推到 Loki 的 /loki/api/v1/push
  3. Kong → Prometheus(指标) :应用本身不推业务指标;有价值的是 Kong 的 /metrics(各 key 的请求数/延迟/带宽,按 consumer 拆分)。Prometheus 按 prometheus.yml 抓取。
  4. Grafana → 三个数据源 :Grafana 用 provisioning 预置了 Tempo / Loki / Prometheus 三个数据源(observability/grafana/provisioning/datasources/),开箱即用。
  5. trace ↔ log 关联 :关联键是 run_id ------它既是 Tempo 里 turn.N span 的属性,又是 Loki 里日志的标签。Tempo 数据源配了 tracesToLogsV2,在一条 trace 的根 span 上点 "Logs for this span" 会自动用 {run_id="..."} 跳到 Loki。

4. 快速部署

前置

  • Docker Desktop。

  • 镜像(国内 Docker Hub 常拉不动,用 daocloud 镜像再 retag):

    复制代码
    for i in grafana/tempo:2.5.0 grafana/loki:2.9.8 grafana/promtail:2.9.8 \
             grafana/grafana:11.0.0 prom/prometheus:v2.52.0 prom/pushgateway:v1.9.0; do
      docker pull docker.m.daocloud.io/$i && docker tag docker.m.daocloud.io/$i $i
    done

启动

复制代码
cd <repo>
# 只起监控组件(不启动没在用的 kong-dp-ai)
docker compose up -d tempo loki promtail prometheus grafana

# 应用把链路发到 Tempo:确认 .env
#   OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
#   LOOP_TRACE_DIR=./trace_archive
# 启动应用
uv run --env-file .env python -m service.app

验证

复制代码
docker compose ps                                   # 组件是否 Up
curl -s http://localhost:3200/ready                 # Tempo ready
curl -s http://localhost:3100/ready                 # Loki ready
curl -s "http://localhost:3200/api/search?q=%7B%7D&limit=3"     # 有没有 trace
curl -s -G "http://localhost:3100/loki/api/v1/query_range" \
     --data-urlencode 'query={service="catalystm-loop-agent"}'  # 有没有 log

停止 / 清理

复制代码
docker compose stop tempo loki promtail prometheus grafana   # 停,保留数据
docker compose down -v                                       # 停并删卷(清空数据,谨慎)

5. 数据持久化

本地(当前这套)

用 docker 具名卷 落盘,stop/restart 不丢数据:

组件 存什么 保留期
tempo-data Tempo trace 块(parquet) + WAL 48h(block_retention)
loki-data Loki 日志 chunks + 索引(boltdb-shipper) 30 天(retention_period)
prometheus-data Prometheus TSDB 块 15 天(默认)
grafana-data Grafana SQLite(仪表盘/设置) 永久
promtail-positions Promtail 读取位点(读到哪) ---

保留期各自在 config 里调:observability/tempo/tempo.yamlobservability/loki/loki-config.yaml、Prometheus 启动参数 --storage.tsdb.retention.time

备份某个卷:

复制代码
docker run --rm -v catalystm-loop-agent_loki-data:/d -v "$PWD/backup":/b \
  alpine tar czf /b/loki-data.tgz -C /d .

生产上正常怎么存

  • Tempo / Loki :把块和索引放对象存储(S3 / GCS / Azure Blob) ,本地只留 WAL。→ 天然持久、可水平扩展、成本低。Tempo 配 storage.trace.backend: s3|gcs|azure;Loki 配 storage_config + schema_config 指向对象存储(新版用 TSDB + common.storage.<object>)。
  • Prometheus :数据盘用 PV/PVC ;长期存储 + 降采样用 remote_write → Thanos / Mimir / Cortex(底层也是对象存储)。
  • Grafana :用外部数据库(Postgres/MySQL) 替代 SQLite,支持多副本共享;数据源/仪表盘用 provisioning 版本化(本仓库已这么做)。
  • k8s:各组件挂 PVC,或直接用官方 Helm chart(Tempo/Loki 的 distributed 版 + 对象存储 backend)。

6. 如何使用

Grafana 匿名是 Viewer,看不到 Explore 。先右上角 Sign in → admin / admin

6.1 查链路 trace(Explore → Tempo,TraceQL)

复制代码
{ resource.service.name = "catalystm-loop-agent" }        # 全部 agent 链路
{ span.run_id = "run-20260717-151046-141652900-20f4b8" }  # 某次 run
{ span.session_id = "sess-20260717-..." }                 # 某个会话(多轮)
{ name = "llm.chat" && span.gen_ai.request.model = "gpt-4o" }  # 某模型的 LLM 调用
{ span.gen_ai.usage.output_tokens > 500 }                 # 大输出的调用
{ name =~ "tool\\..*" }                                    # 所有工具检索

点开 trace 看瀑布图 → 点 llm.chat span → 右侧看 model / tier / input/output tokens / prompt / completion。

6.2 查日志 log(Explore → Loki,LogQL)

复制代码
{service="catalystm-loop-agent"}                          # 全部
{run_id="run-20260717-151046-141652900-20f4b8"}           # 某次 run 全过程
{run_id="run-...", etype="decide"}                        # 只看 decide 事件
{service="catalystm-loop-agent"} |= "美妆"                 # 全文过滤
{service="catalystm-loop-agent"} | json | line_format "{{.type}} {{.data}}"  # 解析 JSON

标签:run_id(带日期的 run 号)、etype(plan/tool/observe/reflect/decide/...)、servicefilename

6.3 查指标 metric(Explore → Prometheus,PromQL)

复制代码
kong_http_requests_total{service="llm-azure-openai"}                # 各路由请求数
sum by (consumer) (kong_http_requests_total)                        # 各 key(consumer)消耗
histogram_quantile(0.95, sum by (le) (rate(kong_request_latency_ms_bucket[5m])))  # p95 延迟

6.4 trace ↔ log 互跳

  • trace → log :Tempo 里打开 trace 的根 span turn.N (带 run_id)→ "Logs for this span" → 自动 {run_id="..."} 跳 Loki。
  • log → trace :Loki 日志行里点 run_id 派生链接,或复制 run_id 去 Tempo 查 { span.run_id = "..." }

7. trace_id / run_id 约定

  • OTEL trace_id (Tempo 里显示,32 hex)带年月日:YYYYMMDD(8)+HHMMSS(6)+微秒(6)+随机(12),如 2026071715104614165201d9ff16e960。每会话稳定不变。
  • run_id / session_id (业务号,trace_archive 文件名 / Loki 标签):run-YYYYMMDD-HHMMSS-<ns>-<rand>
  • 三者时间戳一致,可按时间对齐。

8. 配置文件位置

复制代码
docker-compose.yml                                           # 所有服务定义 + 卷
prometheus.yml                                               # Prometheus 抓取配置
observability/
  tempo/tempo.yaml                                           # Tempo
  loki/loki-config.yaml                                      # Loki
  promtail/promtail-config.yaml                              # Promtail(采集 ./trace_archive)
  grafana/provisioning/datasources/{tempo,loki,prometheus}.yml   # Grafana 数据源(含关联)
  dashboards/                                                # 预置仪表盘(挂载)

9. 排查

复制代码
docker compose ps                              # 谁没起来
docker logs catalystm-loop-agent-promtail-1    # promtail 有没有在推
docker logs catalystm-loop-agent-tempo-1
  • Explore 里没有 Tempo/Loki → 先登录 admin/admin(Viewer 看不到 Explore)。
  • Loki 查不到某次 run → 确认 ./trace_archive/<run>.jsonl 存在,且 LOOP_TRACE_DIR=./trace_archive(promtail 挂的就是这个目录)。
  • Tempo 刚跑完查不到 → 等 10--30 秒(ingester 切块后才可搜)。
  • 拉不到镜像 → 用 docker.m.daocloud.io/<image> 前缀再 retag(Docker Hub 在国内常 EOF)。
相关推荐
白驹过隙不负青春1 天前
OpenResty 1.29.2.5 部署 + Nginx平滑升级运维文档
nginx·openresty
Cry丶1 天前
一次 FTP 上传引发 504 Gateway Timeout 的排查复盘
java·nginx·ftp·openresty·被动模式·504 gateway·生产问题排查
吴懿不在7 天前
apisix~OpenResty各阶段的介绍
openresty
章老师说10 天前
NGINX官方谈Lua风险:这其实是两条网关技术路线之争
运维·nginx·负载均衡·lua·openresty
装不满的克莱因瓶1 个月前
基于 OpenResty 扩展开发实现动态服务注册与发现能力
java·开发语言·架构·openresty
wu8587734571 个月前
OpenResty 源站被扫描的排查与防御实战:Cloudflare + DOCKER-USER + Fail2Ban 最全方案解析
docker·容器·openresty
章老师说1 个月前
B站网关事故背后:OpenResty 与 Lua 的稳定性代价
nginx·云原生·负载均衡·lua·openresty
衣舞晨风2 个月前
运行时行为盲区:API7 AI 网关CPU打满故障的AI辅助事后复盘
lua·openresty·apisix·coroutine·cpu-saturation·socket-buffer
rannn_1113 个月前
【Redis|高级篇2】多级缓存|JVM进程缓存、Lua语法、多级缓存实现(OpenResty)、缓存同步(Canal)
java·redis·分布式·后端·缓存·lua·openresty