Tempo接入云原生日志平台变更门禁后的权限边界和审计链路 - 第2节
2.1 Tempo 架构概览:从数据采集到查询的端到端链路
Tempo 是 Grafana 实验室推出的高可用、低成本、只追加的分布式追踪后端。其核心设计哲学是“将存储与索引解耦”,通过对象存储(如 S3、GCS、Azure Blob)作为主要数据载体,并依赖外部组件(如 Loki、Prometheus)完成索引与查询加速。以下架构图展示了 Tempo 的核心组件与数据流。
架构要点: Tempo 不构建自己的全文索引,而是利用 Trace ID 进行块查找,大幅降低写入路径的 CPU 与内存开销。
2.1.1 核心组件职责
| 组件 | 职责 | 关键特性 |
|---|---|---|
| Distributor | 接收来自 SDK 的 span 批次,验证、采样、哈希分片后转发给 Ingester。 | 无状态,水平扩展;支持概率/速率采样。 |
| Ingester | 将 span 数据缓冲在内存中,按 Trace ID 构建块(block),定期 flush 到对象存储。 | 有状态,建议使用持久化磁盘;块大小可配置(默认 512MB)。 |
| Querier | 接收查询请求,从对象存储中拉取对应块,合并后返回完整 Trace。 | 无状态;支持缓存(memcached/redis)加速块查找。 |
| Query Frontend | 查询路由与聚合层,支持分片查询、结果合并、缓存。 | 可选组件;大幅提升高并发查询性能。 |
| 对象存储 | 持久化存储所有 trace 块,以及索引元数据(如 bloom filter)。 | 推荐 S3/GCS;成本低,无限扩展。 |
架构设计启示: Tempo 的“写放大”极低——写入时只做一次校验和分片,不做索引构建。这使得单机写入吞吐可达 10万+ spans/s,非常适合云原生场景下的高并发日志平台。
2.2 数据模型:Span、Trace 与块结构
Tempo 的数据模型严格遵循 OpenTelemetry (OTel) 规范,核心概念包括 Span、Trace 和 Block。理解这三层结构是掌握查询语言和权限边界的基础。
2.2.1 Span 与 Trace 的层次关系
- Span:一次操作的最小单元,包含操作名称、开始/结束时间、属性(Attributes)、事件(Events)、状态(Status)以及 Trace ID 和 Span ID。
- Trace:由一组具有相同 Trace ID 的 Span 组成,通过 Parent Span ID 形成有向无环图(DAG),代表一次完整的请求链路。
- Block:Tempo 内部存储单元,包含多个 Trace 的压缩数据。每个 Block 附带一个 Bloom Filter 和 索引,用于快速判断 Trace ID 是否存在。
2.2.2 关键数据字段与权限边界
在云原生日志平台中,Tempo 数据模型中的字段直接决定了 权限边界 的粒度。以下表格列出了与门禁控制最相关的字段。
| 字段 | 类型 | 权限控制意义 |
|---|---|---|
resource.service.name | 字符串 | 标识微服务名称,常用于按服务划分访问权限。 |
span.attributes.environment | 字符串 | 标识环境(prod/staging/dev),用于环境级隔离。 |
span.attributes.team | 字符串 | 标识所属团队,用于团队级 RBAC。 |
trace_id | 16字节 | 全局唯一标识,查询入口;权限校验时需验证用户是否有权访问该 Trace 所属的服务。 |
span.status.code | 整数 | 状态码(OK=1, ERROR=2);可用于审计链路中过滤异常 span。 |
权限边界设计要点: 在变更门禁场景中,必须确保用户只能查询到其有权访问的
resource.service.name 和 span.attributes.environment 组合。Tempo 本身不提供行级权限,需要在 Query Frontend 或代理层注入过滤条件。
2.3 查询语言基础:TraceQL 入门
TraceQL 是 Tempo 的原生查询语言,语法类似 PromQL 和 LogQL,但专门针对 Trace 结构设计。它允许用户通过 Span 属性、资源属性 和 时间范围 来筛选 Trace。
2.3.1 基本查询结构
# 查询所有 service.name 为 "order-service" 的 Trace
{ resource.service.name = "order-service" }
# 查询包含错误状态 span 的 Trace
{ span.status.code = 2 }
# 组合条件:查询 order-service 中耗时超过 1s 的 Trace
{ resource.service.name = "order-service" && span.duration > 1s }
# 使用正则:查询 service 名称以 "pay" 开头的 Trace
{ resource.service.name =~ "pay.*" }
2.3.2 查询结果与审计链路
TraceQL 查询结果返回一组匹配的 Trace 列表。每个 Trace 包含完整的 Span 树。在审计链路中,我们通常关注以下指标:
- Trace 数量:匹配条件的请求总数。
- 错误率:包含
span.status.code = 2的 Trace 占比。 - P95/P99 延迟:通过
span.duration计算。 - 服务依赖图:通过 Trace 中的 Parent Span ID 关系生成。
审计场景示例: 当变更门禁触发时,自动执行 TraceQL 查询:
{ resource.service.name = "order-service" && span.duration > 5s },若返回 Trace 数量超过阈值(如 10 个),则判定为“性能劣化”,阻止变更并通知 SRE。
2.3.3 TraceQL 与权限过滤的结合
在云原生日志平台中,TraceQL 查询必须经过 权限注入。例如,用户 A 只允许访问 team = "payment" 的 Trace,则实际执行的查询会被改写为:
# 用户原始查询
{ span.duration > 2s }
# 权限注入后的实际查询
{ span.duration > 2s && resource.service.name =~ "payment-.*" }
这种改写发生在 Query Frontend 的 权限中间件 中,确保审计链路中记录的是改写后的完整查询,而非用户原始输入。
学习建议: 建议在本地部署 Tempo 并导入示例数据,使用 Grafana Explore 的 TraceQL 编辑器进行交互式练习。重点关注
resource 和 span 两个命名空间下的常用属性。