多租户可观测性:通过API提供客户数据
在你的产品界面中,让每位客户查看其自身应用程序的遥测数据。你的后端服务使用独立的 API 密钥为每个租户(即你平台的每位客户)查询 SigNoz。SigNoz 会拒绝任何跨租户的数据查询请求。
你的客户无需登录 SigNoz,且你自有平台的遥测数据绝不会出现在客户的视图里。
前置条件
- 一个 SigNoz Cloud 工作区。
signoz-admin角色权限,以便创建角色和服务账号。- 你产品中用于存储密钥并向 SigNoz 发起 HTTP 请求的后端服务。
配置
第 1 步:为每个租户创建摄入密钥
- 为每个租户(例如
tenant-acme)创建一个 摄入密钥。 - 在 Settings > Ingestion Settings 中,复制密钥值和密钥 ID。应用程序通过密钥值发送数据,而角色和查询则使用 ID。
- 在租户每个应用程序的环境配置中设置该密钥值:
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 中的密钥 ID,参考 Scope Telemetry Access by Ingestion Key 进行操作。
- 为该租户创建服务账户,例如
tenant-acme。详见 Service Accounts。 - 仅为该服务账户分配上述限定范围的角色。
- 为服务账户创建 API Key,并连同租户的密钥 ID 一起存储在你的 Secrets Manager 中。
切勿给服务账户分配 signoz-viewer 等受管角色。角色权限是叠加的,受管角色将能够读取所有租户的数据。
若希望在无 SigNoz UI 的情况下添加新租户,可通过 SigNoz API 自动化执行步骤 1 和步骤 2 的操作。
Step 3: 从后端查询租户数据
使用租户的 API Key 向 /api/v5/query_range 发送查询请求。每个查询都必须依据租户的密钥 ID 进行过滤。以下示例用于统计该租户每个服务的 Span 数量。
{
"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" }]
}
}
]
}
}
Copycurl -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 的调用应保留在后端。前端每发来一个请求,后端执行以下步骤:
- 确定当前登录用户所属的租户。
- 从密钥管理系统中取出该租户的 key ID 和 API key。
- 构建查询,把该租户的 key ID 放入过滤条件。想复用 SigNoz dashboard 中的面板,请参阅将 SigNoz dashboard 用作面板模板。
- 调用
/api/v5/query_range,把结果返回给前端。
切勿把 SigNoz API key 发送到浏览器。拿到 key 的用户可以执行其角色允许的任意查询。
如果后端传入了不属于该租户的 key ID,SigNoz 会返回 403 拒绝查询,其他租户的数据不会离开 SigNoz。
验证
- 在 SigNoz 中打开 Traces Explorer,使用
signoz.workspace.key.id = '<tenant-a-key-id>'过滤,可以看到租户 A 的 spans。 - 用租户 A 的 API key 和 key ID 发送第 3 步中的查询,响应为
200,且其中的行与第 1 步看到的服务一致。 - 用租户 B 的 key ID 发送同样的查询,响应为
403:
{
"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 仪表盘中设计一次面板,后端随后即为每个租户执行相同的面板查询。- 在每个面板中,使用
$key变量进行过滤,例如signoz.workspace.key.id IN $key。 - 为各租户角色授予仪表盘的
read权限。参见 限制仪表盘访问权限。 - 通过
GET /api/v2/dashboards/<dashboard-id>获取仪表盘。面板查询位于data.spec.panels.<panel-id>.spec.queries[0]。 - 根据该查询的
spec.plugin构建compositeQuery:
spec.plugin.kind | compositeQuery |
|---|---|
signoz/BuilderQuery | {"queries": [{"type": "builder_query", "spec": <spec.plugin.spec>}]} |
signoz/CompositeQuery | 直接使用 <spec.plugin.spec> |
signoz/PromQLQuery 或 signoz/ClickHouseSQL | 不支持使用基于密钥范围的角色 |
- 将租户的密钥 ID 放在
variables中,发送请求至/api/v5/query_range:
{
"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。
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。