← 文章 / 云原生与基础设施
signoz 6小时前 · 2026-10-10 02:39:52 · 2 阅读

多租户可观测性:通过API提供客户数据

在你的产品界面中,让每位客户查看其自身应用程序的遥测数据。你的后端服务使用独立的 API 密钥为每个租户(即你平台的每位客户)查询 SigNoz。SigNoz 会拒绝任何跨租户的数据查询请求。

你的客户无需登录 SigNoz,且你自有平台的遥测数据绝不会出现在客户的视图里。

前置条件

  • 一个 SigNoz Cloud 工作区。
  • signoz-admin 角色权限,以便创建角色和服务账号。
  • 你产品中用于存储密钥并向 SigNoz 发起 HTTP 请求的后端服务。

配置

第 1 步:为每个租户创建摄入密钥

  1. 为每个租户(例如 tenant-acme)创建一个 摄入密钥。
  2. 在 Settings > Ingestion Settings 中,复制密钥值和密钥 ID。应用程序通过密钥值发送数据,而角色和查询则使用 ID。
  3. 在租户每个应用程序的环境配置中设置该密钥值:
Copy
OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<tenant-ingestion-key>"

请核对以下值:

  • <region>:你的 SigNoz Cloud 区域。
  • <tenant-ingestion-key>:本步骤中该租户的密钥值。

SigNoz Cloud 会为所有数据添加 signoz.workspace.key.id 属性。其值为发送数据所使用的密钥 ID,且应用程序无法篡改此值。

请使用另一个密钥发送你自有平台的遥测数据。这样,租户角色将无法读取这些数据。

一个租户可以拥有多个密钥,例如用于测试和生产环境。在步骤 2 中,将这些密钥 ID 都添加到角色中,并通过 signoz.workspace.key.id IN ('<key-id-1>', '<key-id-2>') 进行筛选。

第 2 步:为每个租户创建限定作用域的角色和服务账号

  1. 创建一个自定义角色,使其仅能读取该租户密钥对应的日志、链路追踪和指标。请结合步骤 1 中的密钥 ID,参考 Scope Telemetry Access by Ingestion Key 进行操作。
  2. 为该租户创建服务账户,例如 tenant-acme。详见 Service Accounts。
  3. 仅为该服务账户分配上述限定范围的角色。
  4. 为服务账户创建 API Key,并连同租户的密钥 ID 一起存储在你的 Secrets Manager 中。
Warning

切勿给服务账户分配 signoz-viewer 等受管角色。角色权限是叠加的,受管角色将能够读取所有租户的数据。

若希望在无 SigNoz UI 的情况下添加新租户,可通过 SigNoz API 自动化执行步骤 1 和步骤 2 的操作。

Step 3: 从后端查询租户数据

使用租户的 API Key 向 /api/v5/query_range 发送查询请求。每个查询都必须依据租户的密钥 ID 进行过滤。以下示例用于统计该租户每个服务的 Span 数量。

query.json
{
  "start": <start-unix-ms>,
  "end": <end-unix-ms>,
  "requestType": "scalar",
  "compositeQuery": {
    "queries": [
      {
        "type": "builder_query",
        "spec": {
          "name": "A",
          "signal": "traces",
          "aggregations": [{ "expression": "count()" }],
          "filter": { "expression": "signoz.workspace.key.id = '<tenant-key-id>'" },
          "groupBy": [{ "name": "service.name", "fieldContext": "resource" }]
        }
      }
    ]
  }
}
Copy
curl -X POST "https://<signoz-url>/api/v5/query_range" \
  -H "Content-Type: application/json" \
  -H "SIGNOZ-API-KEY: <tenant-api-key>" \
  -d @query.json

请校验以下参数值:

  • <start-unix-ms> 和 <end-unix-ms>:以 Unix 毫秒为单位的时间范围。
  • <tenant-key-id>:来自 Step 1 的该租户 Ingestion Key ID。
  • <signoz-url>:你的 SigNoz 工作区 URL,例如 example.<region>.signoz.cloud。
  • <tenant-api-key>:租户在第 2 步中创建的服务账号 API key。

响应中,租户的每个服务各占一行。更多查询示例请参阅 Traces API、Logs API 和 Metrics API 的文档。

如果请求包含多个查询(例如公式 A/B),需要为每个查询都加上 key 过滤条件。只要有一个查询缺少该条件,SigNoz 就会拒绝整个请求。

第 4 步:把数据提供给你的 UI

对 SigNoz 的调用应保留在后端。前端每发来一个请求,后端执行以下步骤:

  1. 确定当前登录用户所属的租户。
  2. 从密钥管理系统中取出该租户的 key ID 和 API key。
  3. 构建查询,把该租户的 key ID 放入过滤条件。想复用 SigNoz dashboard 中的面板,请参阅将 SigNoz dashboard 用作面板模板。
  4. 调用 /api/v5/query_range,把结果返回给前端。
Warning

切勿把 SigNoz API key 发送到浏览器。拿到 key 的用户可以执行其角色允许的任意查询。

如果后端传入了不属于该租户的 key ID,SigNoz 会返回 403 拒绝查询,其他租户的数据不会离开 SigNoz。

验证

  1. 在 SigNoz 中打开 Traces Explorer,使用 signoz.workspace.key.id = '<tenant-a-key-id>' 过滤,可以看到租户 A 的 spans。
  2. 用租户 A 的 API key 和 key ID 发送第 3 步中的查询,响应为 200,且其中的行与第 1 步看到的服务一致。
  3. 用租户 B 的 key ID 发送同样的查询,响应为 403:
Copy
{
  "status": "error",
  "error": {
    "type": "forbidden",
    "code": "authz_forbidden",
    "message": "service_account/<service-account-id> is not authorized to perform traces:read on resource \"builder_query/signoz.workspace.key.id/<tenant-b-key-id>\""
  }
}

将 SigNoz dashboard 用作面板模板

在 SigNoz 仪表盘中设计一次面板,后端随后即为每个租户执行相同的面板查询。
  1. 在每个面板中,使用 $key 变量进行过滤,例如 signoz.workspace.key.id IN $key。
  2. 为各租户角色授予仪表盘的 read 权限。参见 限制仪表盘访问权限。
  3. 通过 GET /api/v2/dashboards/<dashboard-id> 获取仪表盘。面板查询位于 data.spec.panels.<panel-id>.spec.queries[0]。
  4. 根据该查询的 spec.plugin 构建 compositeQuery:
spec.plugin.kindcompositeQuery
signoz/BuilderQuery{"queries": [{"type": "builder_query", "spec": <spec.plugin.spec>}]}
signoz/CompositeQuery直接使用 <spec.plugin.spec>
signoz/PromQLQuery 或 signoz/ClickHouseSQL不支持使用基于密钥范围的角色
  1. 将租户的密钥 ID 放在 variables 中,发送请求至 /api/v5/query_range:
panel-query.json
{
  "start": <start-unix-ms>,
  "end": <end-unix-ms>,
  "requestType": "<panel-query-kind>",
  "variables": {
    "key": { "type": "custom", "value": ["<tenant-key-id>"] }
  },
  "compositeQuery": <composite-query>
}

请核对以下数值:

  • <panel-query-kind>:取自 data.spec.panels.<panel-id>.spec.queries[0].kind 的值,例如 scalar、time_series 或 raw。
  • <composite-query>:第 4 步中构建的对象。
  • <tenant-key-id>:租户的摄取密钥 ID。若租户拥有多个密钥,需列出所有 ID。
SigNoz 会将变量值注入过滤器后,再检查角色权限。若角色无权读取某个密钥 ID,则返回 403。

故障排查

builder_query/* 返回 403

  • 可能原因:查询缺少 signoz.workspace.key.id 过滤器,或过滤器中使用了 OR 逻辑。
  • 修复:在每条查询中加入 signoz.workspace.key.id = '<tenant-key-id>' 条件,并使用 AND 连接。
  • 验证:请求返回 200。

builder_query/signoz.workspace.key.id/$key 返回 403

  • 可能原因:请求的 variables 中未提供 key 的值。
  • 修复:在请求中添加 "variables": {"key": {"type": "custom", "value": ["<tenant-key-id>"]}}。
  • 验证:请求返回 200。

400 "unsupported signal"

  • 可能原因:请求将 signoz/CompositeQuery 面板规格包裹在 builder_query 中。
  • 修复:直接以 compositeQuery 形式发送该规格,不要包裹。参见使用 SigNoz 仪表盘作为面板模板。
  • 验证:请求返回 200,并包含该面板中每条查询和公式的结果。

promql/* 或 clickhouse_sql/* 返回 403

  • 可能原因:Key 范围的角色只能运行 Query Builder 查询。
  • 修复:将面板写成 Query Builder 查询。
  • 验证:请求返回 200。

403 "only viewers/editors/admins can access this resource"

  • 可能原因:Key 范围的角色只能调用查询端点。无法调用 /api/v1/fields/values 或其他端点。
  • 修复:将过滤菜单的值存储在您的后端中。

局限性

  • Key 范围的角色只能运行 Query Builder 查询。PromQL 和 ClickHouse SQL 查询会返回 403。
  • 该角色仅负责允许或拒绝查询,不会自动添加 key 过滤器,因此您的后端必须为每条查询添加该过滤器。
  • 每个租户需要一个 ingestion key、一个角色、一个服务账户和一个 API key。
  • 本指南仅适用于 SigNoz Cloud。自托管的 SigNoz 没有 ingestion key。

后续步骤

原始来源: signoz

评论 (0)