Loki 多租户日志隔离

FreeGuideOnline 最新 2026-07-13

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 的传递:

Grafana 设置租户 ID

在数据源的 HTTP 头部设置中,新增键 X-Scope-OrgID,值填入固定的租户 ID 或使用 Grafana 的内置变量(如 ${USER:email})动态生成。这样,Grafana 中的所有 Loki 查询都会携带该头。

3. 使用默认租户(可选)

如果你希望未提供租户 ID 的请求自动归入某个默认租户(类似 fake),可以通过 frontendlimits_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-ateam-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