如何使用Docker搭建Tempo分布式追踪系统?

来源:搜索优化作者:泰国程序员头衔:程序员
导读:本期聚焦于泰国程序员创作的《如何使用Docker搭建Tempo分布式追踪系统?》,敬请观看详情。把生产环境S3配置直接复制到本地Docker环境,是Tempo单机搭建中最常见的失败原因之一。storage.trace.backend一旦设为s3,却没有提供对应的AWS凭据或MinIO地址,ingester会在启动阶段反复报错,trace写入根本不会发生。本文使用Docker Compose编排Tempo与Grafana,backend使用local,路径挂载到宿主机目录,并开启OTLP、Jaeger、Zipkin接收器。随后通过Zipkin接口写入一条演示span,再在Grafana Explorer中用TraceQL按服务名查询,验证整条链路是否落盘。重点解释单机配置中ingester的trace_idle_period和compactor的compaction_window如何影响数据可见性,以及Docker挂载目录权限为什么必须预置为10001用户。

Grafana Tempo 与 Loki、Mimir 同属 Grafana 可观测性套件,Tempo 专注分布式链路追踪,最大的不同是它只保存 trace 数据本身,不建立 span 级别的全文索引。查询时靠 TraceQL 从对象存储中按需扫描,这让它的写入成本大幅低于 Jaeger 和 Elastic APM 这类方案。对于本地开发、演示或者小型集群,用 Docker 启动一个单机 Tempo 完全够用,不需要额外部署 Cassandra 或 Elasticsearch。不过单机部署的坑往往集中在 backend 选型和数据目录权限上,本文会把这些步骤串成一条可复现的链路。

如何使用Docker搭建Tempo分布式追踪系统?

一、Tempo 单机版需要理解哪些关键配置

与 Jaeger 全家桶不同,Tempo 将数据写入与查询解耦。distributor 负责接收不同协议的 trace,切分成 batch 后交给 ingester;ingester 先写 WAL 再按时间窗口刷成 block;compactor 合并和保留历史 block;querier 与 query-frontend 处理 TraceQL 和按 trace ID 查询。单机部署时这些组件在同一个进程内运行,配置却仍然按模块划分,因此看 tempo.yaml 时会发现顶层同时出现 server、distributor、ingester、compactor、storage 等字段。

对 Docker 场景影响最大的是 storage.trace.backend。可选值有 local、s3、gcs、azure、bos 等。local 只适合单实例,因为 block 会落在本地文件系统,无法被多个 Tempo 实例共享。如果以后要横向扩展,必须迁移到 S3 或 MinIO。本文选择 local,路径指向容器内 /var/tempo/traces,并挂载到宿主机 ./tempo-data,这样即使容器被删除,本地磁盘上仍然保留 trace 数据。

另一个容易忽略的点是接收器。Tempo 的 OTLP gRPC 默认端口 4317、OTLP HTTP 4318、Jaeger thrift HTTP 14268、Zipkin 9411。Docker 映射端口时要与 tempo.yaml 中 distributor.receivers 保持一致,否则应用 SDK 能连上端口但 Tempo 不认协议。端口冲突很常见,尤其是在已经跑着 Jaeger 或 Prometheus 的机器上,启动前最好先检查一下监听状态。

二、编写 docker-compose.yml 与 tempo.yaml

下面给出完整可用的编排文件。先把 Tempo 和 Grafana 放进同一个自定义网络,Grafana 通过服务名 tempo 访问查询接口,避免宿主机 IP 变动带来的数据源失效。Tempo 镜像统一使用 grafana/tempo:latest,挂载配置文件与数据目录,并把常用的接收器端口暴露到宿主机。

services:
  tempo:
    image: grafana/tempo:latest
    container_name: tempo
    command: ["-config.file=/etc/tempo.yaml"]
    ports:
      - "3200:3200"
      - "4317:4317"
      - "4318:4318"
      - "14268:14268"
      - "9411:9411"
    volumes:
      - ./tempo.yaml:/etc/tempo.yaml:ro
      - ./tempo-data:/var/tempo
    networks:
      - tracing
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
    volumes:
      - ./grafana-provisioning:/etc/grafana/provisioning
    networks:
      - tracing
networks:
  tracing:
    driver: bridge

上面的 compose 文件把 Tempo 的 5 个端口绑定到宿主机。其中 3200 是 HTTP 查询接口,Grafana 数据源走容器网络 http://tempo:3200 即可;4317 和 4318 分别给 OpenTelemetry SDK 使用;14268 是 Jaeger 客户端通过 HTTP 上报;9411 是 Zipkin 接收器,后文验证时会用到。生产环境不一定需要暴露全部端口,但本地调试保留完整协议会比较方便。

接着创建 tempo.yaml。注意缩进必须是两个空格,不能使用 Tab,否则 YAML 解析会直接失败。该配置启用 OTLP、Jaeger、Zipkin 三套接收器,存储后端使用 local,WAL 和 block 均写入 /var/tempo 下。

server:
  http_listen_port: 3200

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318
    jaeger:
      protocols:
        thrift_http:
          endpoint: 0.0.0.0:14268
        grpc:
          endpoint: 0.0.0.0:14250
    zipkin:
      endpoint: 0.0.0.0:9411

ingester:
  trace_idle_period: 10s
  max_block_duration: 5m
  complete_block_timeout: 10m
  flush_check_period: 5s

compactor:
  compaction:
    block_retention: 1h
    compaction_window: 1h

storage:
  trace:
    backend: local
    local:
      path: /var/tempo/traces
    wal:
      path: /var/tempo/wal

querier:
  query_timeout: 30s

这里有几个参数值得单独说明。trace_idle_period 控制一个 trace 超过多久没有新 span 就认为可结束,设置 10 秒是为了本地验证能更快看到数据。max_block_duration 决定 ingester 最多累积多久就切一个新 block,设 5 分钟可以降低 block 碎片。compaction_window 是指压缩时按 1 小时窗口聚合,block_retention 为 1 小时,意味着超过 1 小时的 trace block 会被清理。这些值在 demo 环境尽量调小,生产环境则需要根据流量放大。

三、启动并验证 trace 写入与查询

在 docker-compose.yml 所在目录执行 docker compose up -d。第一次启动会先拉取镜像。执行 docker compose ps 确认两个容器都是 Up 状态,再执行 docker logs tempo 查看是否有 level=error 的日志。常见错误是宿主机目录权限不足,因为 Tempo 官方镜像默认使用 UID 10001 运行,如果 ./tempo-data 的所有者是 root,ingester 无法创建 WAL 目录。可通过 sudo chown -R 10001:10001 ./tempo-data 修复。

验证最简单的方式是使用 Zipkin 接收器。Tempo 原生支持 Zipkin v2 JSON,我们不需要在本地安装 OpenTelemetry SDK,直接用 curl 发送一个包含单个 span 的 trace 即可。示例中的 traceId 和 id 都是 16 位十六进制字符串,需要保证长度正确,否则接收器会返回 400。

curl -s -X POST http://localhost:9411/api/v2/spans \
  -H 'Content-Type: application/json' \
  -d '[
    {
      "traceId": "4d1e00c0db9010db86154a4ba6e91385",
      "id": "86154a4ba6e91385",
      "name": "docker-tempo-check",
      "timestamp": 1710000000000000,
      "duration": 100000,
      "localEndpoint": {
        "serviceName": "demo-service"
      },
      "tags": {
        "http.method": "GET",
        "component": "docker-tempo"
      }
    }
  ]'

若命令返回空且 HTTP 状态为 202,说明数据已经被 Zipkin 接收器受理。接下来等待 10 到 20 秒,让 ingester 完成 trace_idle_period 判断并产生可查询 block。然后打开 Grafana 左侧 Explore,数据源选择 Tempo,将查询类型切换为 TraceQL,输入 { resource.service.name = "demo-service" } 并执行。只能看到一条最近 1 小时内的 trace。也可以直接用 curl 查询 API:curl -s http://localhost:3200/api/traces/4d1e00c0db9010db86154a4ba6e91385。如果返回 JSON 中包含 batches 和 resource 信息,说明从写入到查询整条链路已经打通。

这里有一个容易混淆的点:Grafana 里数据源 URL 要写 http://tempo:3200,而不是 http://localhost:3200。因为 Grafana 运行在 Docker 网络内部,localhost 指向它自己的容器,不是宿主机。只有在宿主机上测试 Tempo API 时才使用 localhost。

四、常见问题与调优建议

问题一:trace 查询不到但写入无报错。先确认 span 时间戳是否在当前时间附近,Tempo 默认只查询最近 2 小时,过期数据即便存在也不会返回。再检查 ingester 是否已经 flush。可以 docker exec -it tempo ls /var/tempo/traces 查看是否有 block 目录,如果只有 WAL 没有 block,可以把 trace_idle_period 和 flush_check_period 进一步调小观察。

问题二:接收器端口连接拒绝。容器端口映射和 Tempo 配置必须同时存在,仅靠 Dockerfile EXPOSE 不会生效。执行 docker port tempo 可以快速看到映射结果,但容器内进程没监听时依然无法访问。此时先在容器内执行 wget -qO- http://127.0.0.1:3200/ready 检查就绪状态。若返回 503,通常是配置解析失败,需要查看完整启动日志。

问题三:磁盘增长过快。local backend 不会自动清理未压缩的小 block,需要 compactor 正常运行。可以注意 compactor 日志中的 compacted 信息。若长期运行,建议把 block_retention 设为合理天数,并按计划备份或迁移到 S3。对于生产环境,不要把 local backend 当作长期方案,因为容器重建后宿主机挂载目录虽然能恢复数据,但无法水平扩展。

最后,如果只是做本地演示,这些参数已经足够;如果要接入真正的 OpenTelemetry SDK,推荐优先使用 OTLP gRPC 或 HTTP 上报,并沿用官方推荐的 resource 属性 organization、service.name 和 deployment.environment。TraceQL 查询时先用 resource.service.name 过滤比直接按 span name 扫更高效。

DockerGrafana Tempo分布式追踪修改时间:2026-09-18 23:36:58

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0918/59008.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。