Loki 多租户日志隔离
yaml auth_enabled: true
仅开启此开关后,Loki 会检查每个请求的 `X-Scope-OrgID` 头。如果缺失,请求将被拒绝,除非你配置了某种默认值或认证代理。
### 2. 使用反向代理设置租户头
生产环境中,你通常不会让客户端直接与 Loki 通信,而是在前面放置 Nginx、Grafana、或者自定义网关来注入租户 ID。这样可以在中央层进行身份验证,并根据登录用户动态设置该头。
#### Nginx 示例
```nginx
server {
listen 80;
server_name loki.example.com;
location / {
proxy_pass http://loki:3100;
# 根据请求路径提取租户 ID,或者从 JWT、认证头中提取
proxy_set_header X-Scope-OrgID $tenant_id; # 此处需自定义变量
proxy_set_header X-Real-IP $remote_addr;
}
}
更常见的做法是用鉴权服务(如 Dex、Keycloak)将用户组织信息映射为租户 ID,然后通过 Nginx 的 auth_request 注入。
Grafana 作为代理
Grafana 的 Loki 数据源支持多租户。在数据源配置中,可以开启 X-Scope-OrgID 的传递:

在数据源的 HTTP 头部设置中,新增键 X-Scope-OrgID,值填入固定的租户 ID 或使用 Grafana 的内置变量(如 ${USER:email})动态生成。这样,Grafana 中的所有 Loki 查询都会携带该头。
3. 使用默认租户(可选)
如果你希望未提供租户 ID 的请求自动归入某个默认租户(类似 fake),可以通过 frontend 和 limits_config 配置:
auth_enabled: true
frontend:
multi_tenant_queries_enabled: false # 禁止跨租户查询
limits_config:
default_tenant_id: "default" # 未提供租户头时使用的默认 ID
但要注意:如果开启认证,却不设置默认租户,缺失头的请求会被直接拒绝。
租户隔离验证
1. 写入日志时指定租户
使用 curl 或任何 HTTP 客户端推送日志时,加上 X-Scope-OrgID 头:
curl -X POST http://loki:3100/loki/api/v1/push \
-H "Content-Type: application/json" \
-H "X-Scope-OrgID: team-a" \
--data-raw '{"streams": [{ "stream": { "app": "myapp" }, "values": [ [ "'$(date +%s)000000000'", "log line for team-a" ] ] }]}'
同样,推送另一条属于 team-b 的日志:
curl -X POST http://loki:3100/loki/api/v1/push \
-H "Content-Type: application/json" \
-H "X-Scope-OrgID: team-b" \
--data-raw '{"streams": [{ "stream": { "app": "myapp" }, "values": [ [ "'$(date +%s)000000000'", "log line for team-b" ] ] }]}'
2. 查询时指定租户
在 LogQL 查询时,同样需要加上该头。我们先查询 team-a 的日志:
curl -G http://loki:3100/loki/api/v1/query_range \
-H "X-Scope-OrgID: team-a" \
--data-urlencode 'query={app="myapp"}' \
--data-urlencode 'start=1650000000000000000' \
--data-urlencode 'end=1650003600000000000'
只会返回 team-a 的日志行。再切换头为 team-b,返回 team-b 的数据,完全隔离。
3. 对象存储中的实际表现
如果你查看 Loki 使用的对象存储(例如 S3 或文件系统),会看到租户数据被物理分开:
/loki/
fake/ # 单租户模式下的默认租户
team-a/
index/
chunks/
team-b/
index/
chunks/
这样即使直接访问存储层,也能保持隔离。
多租户查询控制
默认情况下,Loki 不允许在一个查询中跨租户查询(即请求头中只能有一个 X-Scope-OrgID)。如果你需要在一个查询中聚合多个租户的数据(例如管理员视图),可以开启 多租户查询。
开启多租户查询
配置 frontend 组件:
frontend:
multi_tenant_queries_enabled: true
然后,在查询请求头中可以使用 | 分隔多个租户 ID:
curl -G http://loki:3100/loki/api/v1/query_range \
-H "X-Scope-OrgID: team-a|team-b" \
--data-urlencode 'query={app="myapp"}' \
--data-urlencode 'limit=100'
该查询会返回 team-a 和 team-b 的合并结果。慎用此功能,需要确保请求方有权查看这些租户的数据,通常结合认证代理严格控制。
租户资源限制 (Runtime Overrides / 速率限制)
多租户环境下,避免某个租户滥用资源影响其他租户至关重要。Loki 通过 limits_config 中的租户级覆盖(使用 overrides 文件或运行时配置)实现。
示例:为租户设置不同的摄取速率
创建 /etc/loki/overrides.yaml 文件:
overrides:
team-a:
ingestion_rate_mb: 10 # 允许每秒 10 MB
ingestion_burst_size_mb: 20
max_streams_per_user: 10000
max_global_streams_per_user: 50000
team-b:
ingestion_rate_mb: 1
ingestion_burst_size_mb: 3
max_streams_per_user: 500
然后在 loki.yaml 中引用:
limits_config:
ingestion_rate_mb: 5 # 默认限制
ingestion_burst_size_mb: 10
enforcement_interval: 5m
per_tenant_override_config: /etc/loki/overrides.yaml
重启 Loki 后,配置生效。当 team-b 的写入速率超过 1 MB/s 时,会收到 429 Too Many Requests 错误。
查询限制
同样可配置租户的查询限制:
overrides:
team-a:
max_entries_limit_per_query: 10000
max_queries_per_second: 10
max_query_series: 1000
max_query_length: 30d