3. 租户标识策略:基于 Resource 属性的租户标记

在多租户数据管道中,租户标识是隔离与路由的基石。OpenTelemetry 的 Resource(资源)概念天然适合承载租户上下文——Resource 描述生成遥测数据的实体(如服务、主机、容器),而租户正是该实体的一个固有属性。本章将深入讲解如何通过 Resource 属性标记租户、使用 Attributes Processor 自动注入租户 ID,以及 Span 与 Metric 的标签设计规范。

3.1 基于 Resource 属性的租户标记策略

Resource 是 OpenTelemetry 数据模型中的第一级概念,与 Span、Metric、Log 并列。每个遥测数据点都关联一个 Resource,描述其来源。利用这一特性,我们可以将 tenant.id 或 tenant.name 作为 Resource 属性,实现全局租户标记。

属性键 类型 示例值 说明
tenant.id string acme-corp-001 全局唯一租户标识符,推荐 UUID 或业务编码
tenant.name string Acme Corporation 租户可读名称,用于 UI 展示
tenant.tier string premium 租户服务等级,用于 QoS 与限流策略
tenant.region string us-east-1 租户数据驻留区域,辅助合规路由
最佳实践: 始终使用 tenant.* 命名空间,避免与业务属性冲突。Resource 属性应在 SDK 初始化时从环境变量或配置文件注入,而非硬编码在代码中。

3.2 使用 Attributes Processor 注入租户 ID

在 OpenTelemetry Collector 中,Attributes Processor 是注入租户 ID 的核心组件。它可以在数据进入管道时,根据预定义规则向 Resource 或 Span 添加、更新或删除属性。对于多租户场景,我们通常将租户 ID 注入到 Resource 属性中,确保所有下游处理器和导出器都能感知租户上下文。

以下是一个典型的 Collector 配置片段,展示如何通过 Attributes Processor 注入租户 ID:

processors:
  attributes/tenant-injector:
    actions:
      # 从请求头中提取租户 ID 并注入到 Resource 属性
      - key: tenant.id
        value: "${env:TENANT_ID}"
        action: upsert
        # 指定作用域为 resource,确保影响所有遥测类型
        resource: true
      # 如果环境变量不存在,使用默认值
      - key: tenant.tier
        value: "standard"
        action: insert
        resource: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes/tenant-injector, batch]
      exporters: [otlp/backend]
    metrics:
      receivers: [otlp]
      processors: [attributes/tenant-injector, batch]
      exporters: [otlp/backend]
关键点: 使用 resource: true 确保属性被写入 Resource 层级,而非仅作用于单个 Span 或 Metric 点。这样,即使数据经过聚合或采样,租户上下文也不会丢失。

3.3 Span 与 Metric 的租户标签设计

除了 Resource 级别的租户标记,Span 和 Metric 本身也需要携带租户标签,以支持细粒度的查询与路由。设计原则是:Resource 属性用于全局标识,Span/Metric 属性用于局部上下文。

3.3.1 Span 租户标签设计

对于分布式追踪中的 Span,租户标签应遵循以下规范:

  • 必选标签: tenant.id — 与 Resource 中的值一致,用于快速过滤。
  • 可选标签: tenant.user_id — 当 Span 代表用户级操作时,记录具体用户。
  • 禁止标签: 避免在 Span 中重复 Resource 级别的静态属性(如 tenant.region),以减少数据冗余。
// 在 Span 创建时注入租户标签(Java 示例)
Span span = tracer.spanBuilder("processOrder")
    .setAttribute("tenant.id", "acme-corp-001")
    .setAttribute("tenant.user_id", "user-42")
    .startSpan();

3.3.2 Metric 租户标签设计

Metric 的租户标签设计略有不同,因为 Metric 通常聚合多个数据点。推荐做法是:

  • Resource 属性: 携带 tenant.id,作为 Metric 的第一级分组键。
  • Metric 属性: 仅在需要区分同一 Metric 内的不同租户维度时添加(如 tenant.region)。
  • 避免高基数标签: 不要将 tenant.user_id 直接作为 Metric 标签,会导致基数爆炸。
避坑提示: 不要将租户 ID 同时放在 Resource 和 Metric 的 DataPoint 属性中。这会导致数据重复,增加存储成本。优先使用 Resource 属性,仅在需要跨 Resource 聚合时使用 DataPoint 属性。

3.4 租户标识策略架构总览

下图展示了从数据采集到导出的完整租户标识流程:

租户标识策略架构图 应用服务 SDK 初始化注入 tenant.id 基础设施 环境变量配置 tenant.id OpenTelemetry Collector Attributes Processor 注入 tenant.id / tenant.tier 作用域: resource 支持 upsert / insert 数据管道 Span: tenant.id + tenant.user_id Metric: Resource.tenant.id Log: Resource.tenant.id 按租户路由与隔离 设计要点 1. Resource 属性:全局租户标识,影响所有遥测类型,推荐使用 tenant.id / tenant.tier 2. Span 属性:补充用户级上下文(tenant.user_id),避免重复 Resource 静态属性 3. Metric 属性:仅在高基数可控时使用,优先依赖 Resource 分组 4. Collector 注入:通过 Attributes Processor 统一注入,避免应用层重复代码

3.5 租户标签设计对比总结

遥测类型 推荐标签 注入层级 注意事项
Resource(所有类型) tenant.id, tenant.tier SDK 初始化 / Collector 注入 全局唯一,不可变
Span tenant.id, tenant.user_id 应用代码 / Collector 注入 避免重复 Resource 属性
Metric DataPoint tenant.id(仅必要时) Collector 注入 警惕高基数,优先用 Resource
Log Record tenant.id SDK 初始化 / Collector 注入 与 Resource 保持一致
核心原则: 租户标识策略的核心是“一次注入,全局可见”。通过 Resource 属性承载租户上下文,利用 Attributes Processor 在 Collector 层统一注入,避免应用层重复劳动。Span 和 Metric 的标签设计应遵循“最小必要”原则,只添加对查询和路由有实际价值的属性。