mimir 多租户必须全程透传 x-scope-orgid 请求头,从上游(prometheus/grafana agent)到微服务客户端、ingester、存储(s3/gcs)、grafana 数据源及限流配置,缺一不可;filesystem 模式和未启用 -auth.enabled=true 均不支持租户隔离。

直接用 Mimir 替换 Prometheus 本地存储即可,但租户隔离必须从请求头 X-Scope-OrgID 开始贯穿全链路,否则所有指标会混在一起,根本谈不上“多租户”。
remote_write 必须显式注入租户 ID
Go 微服务本身不生成 X-Scope-OrgID,这个头必须由上游(如 Prometheus Server 或 Grafana Agent)携带。如果你用的是 Prometheus 的 remote_write,配置里不能只写 URL:
-
remote_write的每个 endpoint 下必须加headers块,且值不能硬编码成固定字符串(比如"default"),而应根据实际租户动态注入 - 若微服务自身也直连 Mimir(比如做自监控或埋点上报),需在 HTTP client 中手动设置 header:
req.Header.Set("X-Scope-OrgID", tenantID),这个tenantID应来自认证上下文(如 JWT payload),而非 query 或 form - 漏掉该 header 的请求会被 Mimir 拒绝(默认行为),或落入默认租户(仅当启用了
-multitenancy.default-tenant-id才可能 fallback)
Mimir 配置要启用多租户且禁用 filesystem
单节点跑 mimir -config.file=... 不等于多租户可用。生产部署必须满足两个硬性条件:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
-auth.enabled=true是开关,没它 Mimir 根本不校验X-Scope-OrgID -
-blocks-storage.backend=s3(或gcs),-blocks-storage.filesystem.dir必须完全不配置——filesystem 模式下所有租户数据物理混存,无法隔离 - MinIO 桶名建议与租户 ID 对齐(如租户
finance-prod对应桶tenant-finance-prod),并在 Mimir 的s3.bucket-name配置中按租户分别指定,而不是共用一个桶
Grafana 数据源和限流策略要对齐租户边界
Grafana 界面看到的指标,不是“查 Mimir”,而是“以某个租户身份查 Mimir”。这意味着:
- 每个 Grafana 组织(Organization)必须对应一个唯一
X-Scope-OrgID,填在数据源配置的 “Custom HTTP headers” 里,Key=X-Scope-OrgID,Value=finance-prod - 不同组织不能共享同一 Mimir 数据源实例,否则查询会越权;也不能靠前端 JS 动态改 header,那等于把租户控制权交给浏览器
- 租户级限流(如
ingestion_rate、max_global_series_per_user)必须在 Mimir 的 limits config 中按租户名精确配置,否则高配租户可能压垮低配租户的 ingester 实例
Go 客户端上报指标时别踩 context 和缓存坑
你在 Go 代码里调用 prometheus.CounterVec.Inc() 不会自动带租户信息,这需要你主动绑定:
- 不要在 handler 里从
r.Header.Get("X-Tenant-ID")取值——这个 header 可被伪造,必须从已认证的 ctx 中取,比如ctx.Value("tenant_id").(string) - 如果用了 Redis 缓存指标聚合结果,缓存 key 必须包含租户 ID,例如
metrics:finance-prod:http_requests_total:20260701,否则 A 租户的缓存会覆盖 B 租户的 - Ingester 实例重启后,ring 状态可能短暂不一致,此时部分指标写入会失败;建议客户端加上重试逻辑,并检查返回状态码是否为
400 Bad Request(常见于租户 ID 格式错误)或429 Too Many Requests(限流触发)
最常被忽略的一点:Mimir 的租户隔离是“请求级”的,不是“连接级”或“进程级”的。同一个 Go 进程、同一个 HTTP client、甚至同一个 Prometheus remote_write target,只要 header 不同,就属于不同租户——所以租户 ID 的透传必须像日志 traceID 那样,在整个请求生命周期里无损流转,中间任何一环丢了,隔离就失效了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










