2. 租户模型设计:SaaS多租户架构中的租户定义、租户层级与身份标识设计

在SaaS多租户可观测性体系中,租户(Tenant)是隔离与计费的基本单元。租户模型设计的核心目标是:在共享基础设施上,为不同客户提供逻辑上完全隔离的可观测性数据视图,同时保证查询性能与成本可追溯。

2.1 租户定义:从业务实体到可观测性边界

可观测性语境下的租户,不等同于“用户”或“账号”,而是一个数据隔离边界。一个租户通常对应一个独立的业务组织或客户实例,拥有独立的指标、日志、链路数据存储空间与访问权限。

维度 传统IAM租户 可观测性租户
核心定义 资源访问控制边界 数据存储与查询隔离边界
数据范围 用户、角色、权限 指标、日志、链路、事件
隔离粒度 API/资源级 存储桶/索引/表级
计费依据 用户数/功能包 数据量/查询量/保留时长
生命周期 与组织账号绑定 可独立创建、冻结、归档
设计原则:可观测性租户应具备独立的数据生命周期管理能力。当业务租户被冻结或删除时,其对应的可观测性数据应能自动执行保留策略(归档或清除),避免产生“僵尸数据”占用存储成本。

2.2 租户层级:组织 / 项目 / 环境

单一扁平租户模型无法满足复杂企业架构的需求。我们采用三级租户层级:组织(Organization)→ 项目(Project)→ 环境(Environment),每一级对应不同的数据聚合与权限粒度。

层级定义:
  • 组织(Org):最高级租户,对应一个独立客户或业务线。拥有独立的计费账户、用户体系与全局配置。组织之间完全隔离。
  • 项目(Project):组织内的业务单元或应用组。一个组织可包含多个项目,项目之间共享组织级配置,但数据逻辑隔离。
  • 环境(Environment):项目下的部署环境(如生产、预发、测试)。环境是数据写入与查询的最小隔离单元。
三级租户层级架构图 三级租户层级架构 组织 (Organization) 租户ID: org_xxx | 计费主体 | 全局用户体系 | 数据保留策略 项目 (Project) 租户ID: proj_xxx | 应用分组 | 服务发现范围 | 告警规则集 环境 (Environment) 租户ID: env_xxx | 数据写入目标 | 最小查询单元 | 采样率配置 每个层级拥有独立的租户ID前缀,支持按层级进行数据聚合与权限继承

2.3 身份标识设计:租户ID体系

租户ID是数据路由与隔离的核心标识。我们采用分层前缀+UUID的编码方案,确保全局唯一性、可读性与可路由性。

2.3.1 租户ID编码规范

# 租户ID格式
{层级前缀}_{UUID}

# 示例
org_a1b2c3d4          # 组织级租户
proj_e5f6g7h8          # 项目级租户
env_i9j0k1l2           # 环境级租户

# 完整链路标识(用于日志/链路上下文)
tenant_id = "org_a1b2c3d4/proj_e5f6g7h8/env_i9j0k1l2"
层级 前缀 ID长度 示例 用途
组织 org_ 12字符 org_a1b2c3d4 计费、全局配置、用户管理
项目 proj_ 12字符 proj_e5f6g7h8 应用分组、告警规则、仪表盘
环境 env_ 12字符 env_i9j0k1l2 数据写入、查询隔离、采样配置
避坑指南:避免使用自增数字作为租户ID,否则在合并、迁移或分片时容易产生冲突。推荐使用UUID v4或Snowflake ID,保证全局唯一且无序。

2.3.2 上下文传播与数据路由

在微服务调用链中,租户ID必须通过W3C Trace Context或自定义HTTP头进行透传,确保所有下游服务都能识别当前请求所属的租户。

# HTTP请求头示例
X-Tenant-Id: org_a1b2c3d4/proj_e5f6g7h8/env_i9j0k1l2
X-Tenant-Org: org_a1b2c3d4
X-Tenant-Project: proj_e5f6g7h8
X-Tenant-Env: env_i9j0k1l2

数据写入时,存储层根据env_前缀将数据路由到对应的物理索引或存储桶;查询时,根据org_或proj_前缀进行数据过滤,确保跨租户数据不可见。

租户ID数据路由流程 租户ID数据路由流程 API 网关 解析 X-Tenant-Id 路由到对应存储分片 存储桶: org_a1b2 存储桶: org_c3d4 存储桶: org_e5f6 基于租户ID前缀实现数据分片路由,保证租户级数据物理隔离

2.4 租户模型与可观测性数据关联

租户ID必须嵌入到每一条可观测性数据中,作为强制标签(Mandatory Tag)。以下为不同数据类型中的租户ID承载方式:

数据类型 承载字段 示例
指标(Metrics) Label: tenant_id http_requests_total{tenant_id="org_a1b2/proj_e5f6/env_i9j0", status="200"}
日志(Logs) 结构化字段: tenant.id {"tenant.id": "org_a1b2/proj_e5f6/env_i9j0", "message": "request completed"}
链路(Traces) Span Attribute: tenant.id span.SetAttributes(attribute.String("tenant.id", "org_a1b2/proj_e5f6/env_i9j0"))
关键实践:在日志采集端(如Fluentd、Vector)或指标SDK中,自动注入tenant_id标签,避免业务代码手动传递。这可以通过环境变量或启动参数统一配置,降低接入成本。

2.5 租户模型设计检查清单

  • 唯一性:租户ID是否全局唯一?是否支持跨区域/跨集群合并?
  • 可路由:是否可以通过租户ID前缀直接定位到物理存储分片?
  • 可继承:组织级配置是否能自动下放到项目和环境?
  • 可审计:是否所有数据操作都携带了完整的租户上下文?
  • 可扩展:新增层级(如“机房”或“单元”)是否会影响现有ID体系?