目录
[1. Prometheus生态时序库(监控指标专用)](#1. Prometheus生态时序库(监控指标专用))
[2. 通用时序数据库](#2. 通用时序数据库)
[3. 时序库的特点](#3. 时序库的特点)
[3.1. Prometheus / Mimir / VictoriaMetrics / Thanos 这类(Prometheus模型)](#3.1. Prometheus / Mimir / VictoriaMetrics / Thanos 这类(Prometheus模型))
[Prometheus原生TSDB / Thanos / Mimir / VictoriaMetrics 横向对比表](#Prometheus原生TSDB / Thanos / Mimir / VictoriaMetrics 横向对比表)
[3.2. 多模时序库 GreptimeDB / TDengine / IoTDB](#3.2. 多模时序库 GreptimeDB / TDengine / IoTDB)
[方式A:接入OTLP / remote_write(兼容Prometheus生态)](#方式A:接入OTLP / remote_write(兼容Prometheus生态))
[3.3. 和MySQL对比](#3.3. 和MySQL对比)
[3.4. 重要误区:无schema ≠ 完全没有数据模型](#3.4. 重要误区:无schema ≠ 完全没有数据模型)
[3.5. 工程上的约束(虽然不用预定义,但要管控)](#3.5. 工程上的约束(虽然不用预定义,但要管控))
[4. 时序数据labels(标签) vs 关系数据库索引](#4. 时序数据labels(标签) vs 关系数据库索引)
[4.1. MySQL 索引(B+树)](#4.1. MySQL 索引(B+树))
[4.2. Prometheus体系:labels(标签)](#4.2. Prometheus体系:labels(标签))
[4.3. 最关键的差异点](#4.3. 最关键的差异点)
[① 主键逻辑完全不一样](#① 主键逻辑完全不一样)
[② 新增维度(label)的代价](#② 新增维度(label)的代价)
[③ 查询模型](#③ 查询模型)
[④ 写入开销](#④ 写入开销)
[4.4. 类比理解](#4.4. 类比理解)
[4.5. 工程上的启示(非常贴合OTel采集链路)](#4.5. 工程上的启示(非常贴合OTel采集链路))
[4.6. 简单对比汇总](#4.6. 简单对比汇总)
[二、Prometheus TSDB(v2+)存储架构解析](#二、Prometheus TSDB(v2+)存储架构解析)
[2.1. 写入流程(WAL → Head → Block)](#2.1. 写入流程(WAL → Head → Block))
[2.2. 查询流程](#2.2. 查询流程)
[3. TSDB核心短板(这就是Thanos诞生的原因)](#3. TSDB核心短板(这就是Thanos诞生的原因))
[三、Thanos 完整介绍](#三、Thanos 完整介绍)
[3.1. Thanos Sidecar(和Prometheus同Pod部署,核心)](#3.1. Thanos Sidecar(和Prometheus同Pod部署,核心))
[3.2. Thanos Querier(全局查询网关,无状态,可水平扩)](#3.2. Thanos Querier(全局查询网关,无状态,可水平扩))
[3.3. Thanos Store Gateway(对象存储网关)](#3.3. Thanos Store Gateway(对象存储网关))
[3.4. Thanos Compactor(单实例,对象存储后台合并任务)](#3.4. Thanos Compactor(单实例,对象存储后台合并任务))
[3.5. Thanos Receiver(可选,Push模式接入)](#3.5. Thanos Receiver(可选,Push模式接入))
[3.6. Thanos Ruler(可选,全局告警/记录规则)](#3.6. Thanos Ruler(可选,全局告警/记录规则))
架构1:经典Sidecar模式(存量Prometheus扩容首选)
[架构2:Receiver Push模式(适合OTel链路)](#架构2:Receiver Push模式(适合OTel链路))
[Thanos 和 Mimir / VictoriaMetrics 关键区别](#Thanos 和 Mimir / VictoriaMetrics 关键区别)
[四、Cortex 介绍](#四、Cortex 介绍)
[4.1 核心架构(微服务组件)](#4.1 核心架构(微服务组件))
[4.2 完整写入流程](#4.2 完整写入流程)
[4.3 支持的写入协议](#4.3 支持的写入协议)
[4.4 Cortex 优缺点](#4.4 Cortex 优缺点)
[4.5 Cortex vs Mimir(重点,二者血缘关系)](#4.5 Cortex vs Mimir(重点,二者血缘关系))
[4.6 Cortex / Thanos / Mimir 快速对比](#4.6 Cortex / Thanos / Mimir 快速对比)
[五、Grafana Mimir 多租户实现详解](#五、Grafana Mimir 多租户实现详解)
[5.1 Mimir核心组件(微服务架构,也支持单体模式快速测试)](#5.1 Mimir核心组件(微服务架构,也支持单体模式快速测试))
[5.2 Mimir 多租户是怎么实现的(重点)](#5.2 Mimir 多租户是怎么实现的(重点))
[1. 租户ID传递全链路](#1. 租户ID传递全链路)
[2. 三层隔离机制](#2. 三层隔离机制)
[① 逻辑隔离(最核心)](#① 逻辑隔离(最核心))
[② 租户配额与限流(Mimir内置)](#② 租户配额与限流(Mimir内置))
[③ 权限隔离(上层配套)](#③ 权限隔离(上层配套))
[3. 多租户两种使用场景](#3. 多租户两种使用场景)
[4. 单租户模式](#4. 单租户模式)
[5.3 Mimir支持的写入协议](#5.3 Mimir支持的写入协议)
[5.4 Mimir优缺点](#5.4 Mimir优缺点)
[5.5 Mimir多租户关键误区澄清](#5.5 Mimir多租户关键误区澄清)
[六、VictoriaMetrics 开源版(社区版)多租户说明](#六、VictoriaMetrics 开源版(社区版)多租户说明)
[6.1 两种多租户方案(开源版可选)](#6.1 两种多租户方案(开源版可选))
[方案A:VM Cluster(集群版,推荐,硬隔离)](#方案A:VM Cluster(集群版,推荐,硬隔离))
[方案B:vmsingle 单节点(仅标签模拟,弱隔离,不适合SaaS)](#方案B:vmsingle 单节点(仅标签模拟,弱隔离,不适合SaaS))
[6.2 在开源集群版上做多租户,需要做的工作](#6.2 在开源集群版上做多租户,需要做的工作)
[1. 部署组件:vmcluster + vmauth(必须)](#1. 部署组件:vmcluster + vmauth(必须))
[2. 自建租户管理后台(开源版没有)](#2. 自建租户管理后台(开源版没有))
[3. 资源保护(社区版最大短板,需要自己开发)](#3. 资源保护(社区版最大短板,需要自己开发))
[4. 告警规则(vmalert,开源)](#4. 告警规则(vmalert,开源))
[5. 存储与备份](#5. 存储与备份)
[6. 安全加固清单](#6. 安全加固清单)
[6.3 VM开源集群多租户 vs Mimir多租户对比](#6.3 VM开源集群多租户 vs Mimir多租户对比)
[6.4 选型建议](#6.4 选型建议)
[6.5 误区澄清](#6.5 误区澄清)
一、时序数据库
Mimir、VictoriaMetrics、GreptimeDB、TDengine等 都属于时序数据库(Time Series Database,简称 TSDB)
时序数据库核心特征:数据自带时间戳,数据主要是「时间 + 标签 + 数值」,大量按时间范围查询,专门针对时序场景优化。
简单区分两类时序库
1. Prometheus生态时序库(监控指标专用)
Mimir、VictoriaMetrics、Cortex、Thanos
-
数据模型:Prometheus 时序模型(metric name + labels + timestamp + value)
-
查询语言:PromQL
-
主要用途:
监控指标(Metrics)
,也就是我们前面聊的Gauge/Counter/Histogram
这类也常被叫做「Prometheus兼容时序库」,属于时序数据库的一个子集。
2. 通用时序数据库
TDengine、GreptimeDB、InfluxDB、IoTDB
-
数据模型更宽泛,除了监控指标,还支持IoT设备采集、传感器数据
-
查询:SQL为主,同时可以兼容PromQL、OTLP协议,有的还能同时存日志、链路追踪
一个容易混淆的点
Prometheus 本身内置的
tsdb,也是时序数据库,只是是单机版本,不支持分布式。
时序数据库的数据样例
http_server_requests_seconds_bucket{method="GET",status="200",le="0.005"} 20 1759284000
-
时间戳:
1759284000 -
标签:method、status、le
-
指标值:20
但是:
-
Mimir 只存指标时序,不能存Trace链路、日志;
-
GreptimeDB这类多模时序库,一套存储引擎可以同时承载指标、日志、trace,属于多模时序数据库。
一句话总结
凡是以时间序列数据为核心存储、针对时间范围查询做优化的数据库,统称时序数据库;Mimir、VM是时序数据库里专门面向Prometheus监控指标的分支。
3. 时序库的特点
✅ 结论:不用预先定义表、预先定义列、预先写schema
这是时序数据库(尤其是Prometheus生态这套)和MySQL这类关系型数据库最核心差别之一。
3.1. Prometheus / Mimir / VictoriaMetrics / Thanos 这类(Prometheus模型)
无schema,不需要建表、不需要预先定义字段 一条时序的标识:指标名 + 标签集合
http_requests_total{method="POST",status="200",env="prod"} 123 1759284000
-
method、status、env这些标签,不需要提前声明。 -
上报的时候带什么标签,系统就自动识别;新增标签、新增标签值,随时来随时存。
-
不存在
CREATE TABLE,没有固定列。
注意: 虽然不用预先定义,但标签就是它的"维度" 。标签数量、标签值基数(不同取值数量)会直接影响存储开销,不是无代价随便加,高基数标签(user_id、request_id)会爆炸式增加时序数量。
Prometheus原生TSDB / Thanos / Mimir / VictoriaMetrics 横向对比表
| 对比项 | Prometheus原生TSDB | Thanos | Mimir | VictoriaMetrics |
|---|---|---|---|---|
| 核心定位 | 单机一体化监控(采集+本地时序存储+PromQL+告警) | 基于Prometheus TSDB外挂扩容层, 不替换Prometheus,用于跨集群全局查询+对象归档 | Cortex分支重构,独立分布式时序库,替代Prometheus存储层,LGTM组件 | 自研高性能时序库,可完全替代Prometheus,含采集组件vmagent |
| 写入模型 | Pull为主,本地磁盘写入; 支持remote_write推送(作为客户端向外推) | 两种模式: 1.Sidecar:Prometheus本地生成Block,上传对象存储 2.Receiver:remote_write push写入 | 纯Push模型,distributor接收远端推送 | Pull(vmagent) + Push双模型都支持 |
| 写入协议 | Pull:OpenMetrics/Prom文本 Push:remote_write(作为发送方) 3.x新增:OTLP/HTTP metrics接收 | 1. Sidecar:复用Prometheus本地Block上传 2. Receiver:remote_write;支持OTLP/HTTP metrics | remote_write、OTLP/HTTP(仅metrics)、Influx Line | remote_write、OTLP/HTTP(仅metrics)、Influx Line、Graphite、OpenMetrics |
| 存储底层 | Prometheus原生TSDB,Block不可变,本地磁盘,默认保留15天,无对象存储原生支持 | 复用Prometheus TSDB Block格式,热数据在Prometheus本地,冷数据存入对象存储S3 | 自研分布式Block存储,数据存对象存储S3/COS,存算分离 | 自研存储引擎;单机可用本地磁盘;集群支持对象存储;压缩率极高 |
| 查询能力 | 单机PromQL; 仅能查本机时序,不能跨实例查询 | Querier做全局PromQL查询;同时查Prometheus本地热数据+对象存储冷数据;多副本自动去重 | 原生PromQL;多租户隔离查询;内置查询缓存、查询分片;Trace Metrics Generator联动Tempo | MetricsQL(PromQL超集,完全兼容PromQL,增加扩展函数) |
| 多租户 | ❌ 无原生多租户,单实例单租户 | ❌ 无原生多租户,依靠标签模拟租户隔离,隔离弱 | ✅ 原生多租户,HTTP头X-Scope-OrgID隔离,租户限流、配额 |
社区版:弱多租户;企业版完善多租户、租户限流 |
| 告警能力 | 内置Prometheus Rule + Alertmanager, 仅能基于本机数据计算 | Thanos Ruler,全局数据集执行告警/记录规则 | Mimir Ruler,内置Prometheus告警、记录规则 | vmalert独立组件,支持Prometheus告警规则 |
| 运维组件 | 单二进制,组件极少,最简单 | 组件多:Prometheus+Sidecar+Querier+StoreGateway+Compactor;架构重 | 微服务组件:distributor/ingester/querier/compactor;单体模式可快速测试 | vmsingle单二进制极简部署;集群版vminsert/vmstorage/vmselect,vmagent采集 |
| 运维复杂度 | ⭐ 很低(单节点) | ⭐⭐⭐⭐ 高,组件多,Compactor单实例瓶颈、热数据存在Prometheus本地风险 | ⭐⭐⭐ 中等;微服务多,但LGTM生态配套成熟 | ⭐⭐ 低;单二进制上手简单;集群运维轻于Mimir/Thanos |
| 开源协议 | Apache2.0 | Apache2.0 | AGPLv3(修改源码对外提供服务,需要开源修改代码) | Apache2.0(社区版) |
| 适合场景 | 小规模、单机/小集群监控, 简单场景,存量小型业务 | 已有大量Prometheus存量集群,需要跨集群全局查询、长期归档,不想替换Prometheus | 全新搭建LGTM可观测平台、多租户SaaS、Grafana深度协同、海量时序 | 高基数场景,追求低成本、高压缩,不想依赖对象存储,资源紧张环境 |
| 主要短板 | 无法分布式扩容;时序多易OOM; 存储受本地磁盘限制,历史数据保存时间短 | 组件多;最近2小时热数据保存在Prometheus本地,存在丢数据风险;无原生多租户 | AGPL协议约束;微服务集群部署组件多,资源开销高于VM | 社区版缺少企业级多租户高级能力;生态案例少于Mimir |
选型小结
-
血缘区分:Thanos不改变Prometheus存储;Mimir来自Cortex分支,完全独立存储;VM是从零自研引擎。
-
OTel接入推荐
-
存量Prometheus集群扩容:选Thanos
-
新建LGTM、Grafana生态、多租户需求:Mimir
-
资源紧张、高基数、希望协议宽松Apache2.0:VictoriaMetrics
-
小规模测试:直接Prometheus原生TSDB
-
3.2. 多模时序库 GreptimeDB / TDengine / IoTDB
这里要稍微区分,分两种用法:
方式A:接入OTLP / remote_write(兼容Prometheus生态)
同样不需要提前建表定义列,直接接收遥测数据,自动识别标签。
方式B:原生SQL模式使用
可以预先建表定义schema(列) ,也可以自动建表(schema-less,写入时自动识别字段)。
-
TDengine:可以自动建表;也可以手动建超级表,预先定义列(工业场景常用)
-
GreptimeDB:支持无schema写入,也支持显式建表
3.3. 和MySQL对比
MySQL:
CREATE TABLE t(
id int PRIMARY KEY,
name varchar(50),
create_time datetime
);
必须先定义有哪些列、类型;不能随便插入一个不存在的新字段。
时序库(Prometheus模型): 直接推数据,不需要建表,新标签随时新增。
3.4. 重要误区:无schema ≠ 完全没有数据模型
不是随便乱写,它有固定的数据模型约束:
-
每条样本:
时间戳 + 数值 + 一组key=value标签 -
标签key不能是任意复杂嵌套结构(OTLP里attributes是扁平键值对)
-
标签名、标签值是字符串,指标值一般是浮点数
OTLP的attributes(就是咱们前面聊的otel span/metric里的attributes),对应到Prometheus体系就是labels,不需要提前定义。
3.5. 工程上的约束(虽然不用预定义,但要管控)
虽然不用预先建表,但生产上必须做标签治理:
-
禁止把唯一ID(traceId、userId)直接作为标签,造成时序爆炸(高基数)
-
过滤无用attributes,避免应用随便携带大量标签上报,打爆Mimir/VM存储
4. 时序数据labels(标签) vs 关系数据库索引
先一句话概括:
Prometheus模型里的labels(标签)本质就是内置的维度索引,但设计思路、代价、适用场景,和MySQL的B+树索引差别很大。
4.1. MySQL 索引(B+树)
假设表:request(id, ts, method, status_code, duration)
-
你需要手动创建索引 :
CREATE INDEX idx_method ON request(method); -
索引依附于固定表结构,列是预先定义好的。
-
一条行记录,多个索引可以独立存在。
-
查找逻辑:索引 → 找到主键 → 回表读取整行数据。
-
适合:海量行,按条件筛选少量行。
-
代价:索引越多,写入越慢(每写一行,要更新所有相关索引)。
例子:
SELECT AVG(duration) FROM request WHERE method='GET' AND status_code='200' AND ts BETWEEN xxx AND yyy;
MySQL用索引快速过滤出符合条件的行,再计算聚合。
4.2. Prometheus体系:labels(标签)
核心:
metric_name + 全部label键值对唯一确定一条时序(time series)
http_requests_total{method="GET",status="200",env="prod"}
-
所有label自动作为维度,不需要手动建索引。
-
不存在"单独给某个label建索引",label集合本身就是时序的主键。
-
存储底层:倒排索引
-
索引:
label_key=label_value → 时序ID列表 -
查询时:根据标签条件,求多个标签集合的交集,找到匹配的时序,再读取该时序下所有时间点样本。
-
查询等价逻辑:
http_requests_total{method="GET",status="200"}
引擎查找:
-
找到
method="GET"对应的所有时序集合 -
找到
status="200"对应的所有时序集合 -
取交集,得到匹配的时序列表
-
读取这些时序的时间序列样本,做聚合计算
4.3. 最关键的差异点
① 主键逻辑完全不一样
-
MySQL:一条行 = 一条记录;主键唯一标识一行。一行里面可以有很多字段。
-
Prometheus:
**一条时序 = 指标名+全部标签;**每一条时序,独立存储一串时间戳+数值样本。
只要任意一个label的值变了,就是全新的一条时序 。
{method="GET"}和{method="POST"}是两条独立时序。
② 新增维度(label)的代价
-
MySQL:新增一个字段,已有索引不受影响;新建索引要扫描全表,成本很高。
-
Prometheus:新增一个label,会直接分裂出大量新时序。 比如原来:
http_requests_total{method="GET"}上报时多带一个
user_id="123"就变成:
http_requests_total{method="GET",user_id="123"}如果user_id是千万级唯一值,直接产生千万条时序,也就是传说中的高基数爆炸。
✅ 重点:MySQL加索引是"增加索引开销";Prometheus加高基数标签是直接成倍增加时序数量、存储、内存。
③ 查询模型
-
MySQL:先筛选行,再对行做聚合;适合任意维度自由查询。
-
Prometheus:先筛选
时序集合,再读取这条时序上连续时间样本做聚合。
非常适合:对同一条时序,按时间范围取连续样本(监控最常见场景) 不擅长:跨大量时序做复杂多维度筛选(大量标签交叉过滤,查询开销飙升)
④ 写入开销
-
MySQL:写入一行,更新相关索引。
-
Prometheus:写入是追加样本到已存在的时序 ; 如果这个标签组合是第一次出现,需要新建一条时序(写入索引元数据),新建时序开销远高于追加样本。
4.4. 类比理解
-
MySQL:一张大表,很多行,列固定,索引用来快速挑出行。
-
Prometheus: 每一组标签组合,单独开一条独立的时间线。标签就是这条时间线的身份; 底层倒排索引用来快速找到符合标签条件的时间线,然后读取这条线上的时间点数据。
4.5. 工程上的启示(非常贴合OTel采集链路)
OTLP上报的attributes会转成labels:
-
不要把唯一ID(trace_id、request_id、user_id)直接作为label,会高基数爆炸。
-
无用attributes要在OTel Collector阶段过滤掉(processor: attributes删除),减少label数量。
-
标签尽量控制在少量低基数维度:env、service_name、instance、job、status。
4.6. 简单对比汇总
| 特性 | MySQL(B+树索引) | Prometheus labels(倒排索引) |
|---|---|---|
| Schema | 预先定义列 | 无schema,标签动态新增 |
| 索引创建 | 手动建索引 | 标签自动进入倒排索引,无需手动创建 |
| 唯一标识 | 主键标识一行记录 | metric+全部label标识一条时序 |
| 新增维度代价 | 新增字段/索引,代价可控 | 新增标签,可能爆炸式新增时序 |
| 查询特点 | 按条件筛选行 | 先筛选时序,再读取时序上连续样本 |
| 高基数影响 | 索引变大,查询变慢 | 时序数量暴涨,内存/存储直接打爆 |
二、Prometheus TSDB(v2+)存储架构解析
Prometheus 2.0之后自研TSDB,块(Block)为核心的不可变时序存储,Go语言开发。
核心思想:新写入数据放内存Head块,每2小时固化成一个不可变Block;Block一旦落地磁盘,不会修改内部数据,只新增墓碑标记做删除。
2.1. 写入流程(WAL → Head → Block)
- WAL(预写日志,wal目录) 所有采集到的sample(时间戳+float值)先写WAL,再写入内存Head。
-
作用:崩溃恢复,Prometheus重启后回放WAL,重建内存Head,防止丢最近数据。
-
WAL是分段文件,每段默认128MB,循环复用。
-
WAL里是原始未压缩样本,体积远大于压缩后的Block。
- Head(内存活跃块,当前正在写入的2小时窗口) 内存中维护所有活跃时序,接收新样本;
-
时序(series):
metric_name + labelset唯一标识一条时序 -
Chunk:一条时序的连续样本集合,采用Gorilla XOR压缩(时间戳双delta,数值XOR),压缩率极高,平均每个样本1~2字节。
Head是可变的,可以追加样本;当时间窗口满2小时,Head被关闭,固化成磁盘上的Block,之后这个Block永远只读、不可修改。
- Block(磁盘固化块,ULID命名) 每一个Block是独立目录,目录名是ULID(可字典排序,内置时间信息),代表固定2小时时间范围。
ULID目录/
├── meta.json # 元信息:起止时间、样本数、外部标签、版本
├── index # 倒排索引:标签名/标签值 → 时序ID → chunk偏移
├── chunks/ # 多个段文件,存放压缩后的时序样本,单段最大512MB
└── tombstones # 墓碑:标记哪些时序在本块内被删除,不是直接删数据
-
不可变:Block生成后,不能修改里面任何样本。删除操作只写tombstone墓碑。
-
Compaction(合并压缩):后台自动把多个小Block合并成更大Block(2h→10h→1d),减少Block数量,降低查询时要打开的文件数量,同时做降采样downsample。
-
保留策略retention:超过保留期(默认15天),Prometheus直接删除旧Block目录。
2.2. 查询流程
查询PromQL时间范围:
-
查最近2小时:读内存Head + WAL,直接取内存数据,速度最快
-
查2小时~保留期内历史数据:遍历磁盘上对应的Block,读取index找到对应时序,再读chunks里的压缩样本,解压计算。
3. TSDB核心短板(这就是Thanos诞生的原因)
-
单机绑定本地磁盘,无法水平扩展;时序量大后内存暴涨,高基数容易OOM
-
本地磁盘保存,默认15天,长期存储成本高
-
多Prometheus集群(多机房/多K8s集群),没有全局统一查询入口,每个Prometheus独立查询
一句话:TSDB是单机本地块存储,本身不支持分布式、对象存储、全局跨集群查询。
三、Thanos 完整介绍
Github:GitHub - thanos-io/thanos: Highly available Prometheus setup with long term storage capabilities. A CNCF Incubating project. · GitHub 许可证:Apache2.0,CNCF孵化项目,Go语言开发
本质:不是替换Prometheus,而是在原生Prometheus+TSDB之上,增加全局查询、对象存储长期归档能力。
Thanos完全复用Prometheus原生TSDB Block格式,不需要修改Prometheus内部存储,这是和Mimir/VM最大区别。
核心设计思想
保持原有Prometheus不变(继续scrape采集、本地告警),Sidecar监听Prometheus,当2小时Block固化到本地磁盘后,自动把Block上传到S3兼容对象存储。
再通过Thanos组件实现:跨多个Prometheus实例的全局查询 + 对象存储历史数据查询。
Thanos 全部组件(模块化,按需部署)
3.1. Thanos Sidecar(和Prometheus同Pod部署,核心)
-
监听Prometheus,读取Prometheus生成的TSDB Block,Block生成完毕后,上传Block到对象存储桶。
-
暴露StoreAPI(gRPC),Thanos Querier可以实时查询这个Prometheus本地的最近2小时未上传的热数据。
注意:Prometheus本地Block上传前,最近2小时数据只存在Prometheus本地磁盘,如果Prometheus宕机,这2小时未上传数据有丢失风险。
配置Sidecar时,通常关闭Prometheus本地compaction,交给Thanos Compactor在对象存储端做合并,会增加Prometheus内存开销。
3.2. Thanos Querier(全局查询网关,无状态,可水平扩)
对外暴露Prometheus HTTP API,Grafana直接对接Querier。
-
接收PromQL,并发向所有StoreAPI(Sidecar + StoreGateway)并行查询
-
合并来自多个Prometheus、多个集群的数据;多副本自动去重(HA多Prometheus副本场景非常好用)
-
做查询分片、结果聚合。
3.3. Thanos Store Gateway(对象存储网关)
对象存储S3里的Block不能直接读,StoreGateway负责:
-
读取对象存储桶内的TSDB Block元信息+索引,做本地缓存;
-
收到Querier查询请求,按需从S3拉取对应chunk数据返回;
不会把全量Block下载到本地,只按需范围读取。
3.4. Thanos Compactor(单实例,对象存储后台合并任务)
只操作对象存储桶里的Block,不碰Prometheus本地磁盘
-
合并多个小2h Block → 更大Block(10h、1d),减少Block数量,降低查询开销
-
降采样 downsampling:生成低分辨率的历史指标(5min、1h粒度),久远历史查询更快,节省存储
-
执行保留策略,删除超过保留期的Block
3.5. Thanos Receiver(可选,Push模式接入)
替代Prometheus,接收remote_write推送指标,内部构建TSDB Block,上传到对象存储。
适合push场景,比如OTel Collector remote_write推给Thanos Receiver。
3.6. Thanos Ruler(可选,全局告警/记录规则)
在全局数据集上执行PromQL记录规则、告警规则,弥补Prometheus只能看本地数据做告警的局限。
Thanos 典型2种部署架构
架构1:经典Sidecar模式(存量Prometheus扩容首选)
Exporter → Prometheus(scrape采集,本地TSDB) → Thanos Sidecar(同pod) → 上传Block到S3对象存储
↓
Thanos Querier ← StoreGateway(读S3历史) + Sidecar(查Prometheus实时热数据)
Grafana → Querier
✅ 优点:存量Prometheus不动,渐进式改造;多机房多集群全局查询;数据归档到对象存储,长期保存。 ❌ 缺点:组件多运维复杂;最近2小时数据在Prometheus本地,有丢失风险;Sidecar会增加Prometheus内存压力。
架构2:Receiver Push模式(适合OTel链路)
OTel Collector → remote_write → Thanos Receiver → 生成Block上传S3
Querier + StoreGateway + Compactor 提供查询
不再部署Prometheus做scrape,全部指标push进Thanos Receiver。
Thanos 和 Mimir / VictoriaMetrics 关键区别
| 项目 | 底层 | 模型 | 多租户 |
|---|---|---|---|
| Thanos | 复用Prometheus原生TSDB Block | 保留Prometheus作为采集节点,sidecar上传块 | 无原生多租户,靠外部标签区分 |
| Mimir | 自研分布式时序库,Cortex演进 | push模型,distributor统一接收remote_write | 原生多租户 |
| VictoriaMetrics | 自研时序引擎 | push/pull都支持,单二进制简单 | 企业版才有完善多租户 |
Thanos优缺点
✅ 优点
-
完全复用原生Prometheus,原有采集、告警几乎不用改动,平滑扩容
-
块存储存到对象存储,低成本长期保存海量指标
-
跨多个Prometheus集群,全局统一PromQL查询,HA副本自动去重
-
Apache2.0协议,商用友好
❌ 缺点
-
组件多(Sidecar/Querier/StoreGateway/Compactor),运维复杂度高
-
最近2小时热数据在Prometheus本地,故障会丢这部分数据
-
Compactor是单实例,对象存储桶大了之后Compactor容易成为瓶颈
-
高基数场景没有Mimir完善的租户限流保护
选型小结
-
如果已经大量Prometheus集群,不想推翻现有采集体系,需要跨集群全局查询+长期归档 → Thanos非常合适。
-
如果是全新搭建OTel可观测体系,一般优先Mimir或VM,而不是Thanos,组件更少。
补充:Thanos的存储格式和Prometheus TSDB Block完全一致,所以Thanos和Prometheus是共生关系,不是替换关系;Mimir/VM是直接替代Prometheus的存储层。
四、Cortex 介绍
Cortex 是 CNCF 项目、Apache2.0协议、Go开发 ,Grafana Labs 早期开发,原生多租户、水平扩展、面向Prometheus的分布式时序存储 ,Mimir就是从Cortex fork出来的分支 ,Cortex现在进入维护模式,不再大规模新增功能,新项目官方推荐直接上Mimir。
Github:GitHub - cortexproject/cortex: A horizontally scalable, highly available, multi-tenant, long term Prometheus. · GitHub 核心定位:接收remote_write推送指标,支持OTLP Metrics,复用Prometheus TSDB Block存储模型,基于对象存储做长期保存。
和Thanos最大区别:Thanos是外挂在Prometheus之上;Cortex是独立分布式系统,完全采用Push模型,不需要Prometheus做本地采集。
4.1 核心架构(微服务组件)
写入链路组件
-
Distributor(分发器,无状态) 接收remote_write / OTLP metrics请求;校验指标、租户ID(
X-Scope-OrgID);对时序做hash,通过hash环(ring)把样本转发给对应的Ingester;做数据副本(默认3副本)保证高可用。 -
Ingester(写入存储节点,有状态)
-
接收样本,先写WAL预写日志,再写入内存;
-
内部每个租户独立维护一套Prometheus原生TSDB;
-
内存积累满2小时,固化成不可变Block,上传到S3/对象存储;
-
保存最近未固化的热数据,查询时Querier会来Ingester读取实时数据。
-
重点:Block格式和Prometheus TSDB完全一致,和Thanos Block格式同源。
查询链路组件
-
Querier(查询器,无状态)
对外暴露Prometheus HTTP API,接收PromQL;并行查询两处数据:
-
实时热数据:Ingester内存里还没固化的样本
-
历史冷数据:请求StoreGateway读取对象存储中的Block 合并、去重、聚合结果返回给Grafana。
-
-
Store Gateway(对象存储网关,半有状态) 读取对象存储里的Block元信息、索引,缓存索引;按需从对象存储拉取chunks样本,给Querier使用。
-
Compactor(块合并器) 后台任务,只操作对象存储桶:把多个2小时小Block合并成更大Block,减少文件数量,提升查询速度;做去重、保留策略清理过期数据。
可选组件
-
Ruler:执行Prometheus记录规则、告警规则;
-
Query Frontend:查询网关,做查询限流、分片、缓存、大查询拆分;
-
Alertmanager:告警管理。
4.2 完整写入流程
-
OTel Collector / Prometheus → remote_write 推给 Cortex Distributor
-
Distributor hash分片,复制多份转发给Ingester
-
Ingester:WAL写入 + 内存TSDB写入
-
满2小时,固化Block,上传对象存储
-
Compactor后台合并对象存储内的Block
特点:全链路Push模型,不需要Prometheus本地TSDB。你可以直接OTel Collector remote_write推Cortex,不需要部署Prometheus。
4.3 支持的写入协议
-
Prometheus Remote Write(主流)
-
OTLP over HTTP(仅Metrics,不支持Trace、Logs,gRPC OTLP不支持)
和Mimir的写入协议基本一致,同样依靠
X-Scope-OrgID做租户隔离。
4.4 Cortex 优缺点
✅ 优点
-
原生多租户,租户之间数据完全隔离,适合SaaS平台;
-
完全复用Prometheus TSDB Block模型,PromQL原生支持;
-
组件可以独立横向扩容;
-
支持对象存储长期保存,不受单机磁盘限制;
-
Apache2.0协议,商用友好,修改代码不需要开源。
❌ 缺点
-
项目进入维护模式,新功能开发全部转移到Mimir;新项目不推荐新建Cortex集群;
-
组件非常多,运维复杂度高;
-
早期Chunk存储模式已废弃,现在只用Block存储;
-
Ingester故障时,内存中未固化的2小时热数据有丢失风险(依赖副本策略);
-
高基数场景优化弱于Mimir,Mimir修复了大量Cortex遗留技术债。
4.5 Cortex vs Mimir(重点,二者血缘关系)
Mimir是Grafana从Cortex fork出来的,架构同源,但是做了大量重构:
-
Cortex:Apache2.0,维护模式;组件多,高基数处理弱;
-
Mimir:AGPLv3,活跃开发;优化了Compactor、查询引擎,专门针对高基数时序优化,组件配置简化;
协议层面:OTel Collector推Cortex和推Mimir写法几乎一模一样,仅修改endpoint和鉴头。
4.6 Cortex / Thanos / Mimir 快速对比
| 项目 | 模型 | 存储来源 | 多租户 | 开发状态 |
|---|---|---|---|---|
| Cortex | Push模型,独立分布式系统 | 复用Prometheus TSDB Block | ✅原生 | 维护模式,不推荐新项目 |
| Thanos | Sidecar外挂,依附Prometheus | 复用Prometheus TSDB Block | ❌,靠标签模拟 | 活跃 |
| Mimir | Push模型,独立分布式系统 | 基于Cortex改造自研Block存储 | ✅原生 | 活跃,Grafana主推 |
五、Grafana Mimir 多租户实现详解
Github:GitHub - grafana/mimir: Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus. · GitHub 开源协议:AGPLv3,Go开发,由Grafana Labs维护,是Cortex的重构分支,CNCF项目。
一句话定位:分布式、多租户、存算分离的Prometheus风格时序数据库,专门接收指标metrics,支持PromQL,作为LGTM里的M组件 。 核心设计:全Push模型,不自带scrape采集(可以搭配OTel Collector的prometheus receiver拉取/metrics),底层存储块存在对象存储(S3/COS/OSS)。
5.1 Mimir核心组件(微服务架构,也支持单体模式快速测试)
-
Distributor(分发器,无状态,写入入口)
接收 remote_write / OTLP/HTTP metrics 请求,是所有写入流量的入口。
-
读取HTTP请求头
X-Scope-OrgID获取租户ID; -
对时序做hash分片,写入到对应的Ingester;
-
做多副本写入(默认3副本)保证高可用;
-
做租户层面的限流、写入校验、拒绝非法标签。
-
-
Ingester(写入节点,有状态)
接收Distributor转发的样本,内部为
每个租户独立维护内存时序数据
。
-
写入流程:WAL预写日志 → 内存时序;
-
同样2小时窗口固化成不可变Block,上传到对象存储;
-
保存还没固化的热数据,查询时提供实时数据;
-
租户之间内存数据隔离。
-
-
Querier(查询节点,无状态)
对外暴露Prometheus HTTP API,Grafana对接这个组件。
-
读取
X-Scope-OrgID,限定只查询该租户的数据; -
并行查询两处数据:Ingester内存热数据 + StoreGateway读取对象存储冷块;
-
合并、去重、计算PromQL结果返回。
-
-
Store-gateway(对象存储网关,半有状态) 读取对象存储里的Block元数据和索引,缓存索引;按需拉取chunk样本。
每个租户的Block在对象存储里是隔离的。
-
Compactor(块合并器,后台任务) 在对象存储侧,对每个租户的Block单独做合并、压缩、降采样、过期清理。租户之间Block互不干扰。
-
Ruler(可选,告警引擎) 按租户独立执行PromQL记录规则、告警规则,每个租户拥有独立规则配置,独立告警。
-
Query-frontend(可选) 查询网关,做查询拆分、缓存、限流、队列控制,保护后端。
5.2 Mimir 多租户是怎么实现的(重点)
Mimir是原生硬隔离多租户 ,不是简单用标签模拟,租户A完全看不到租户B的数据。 租户识别依靠HTTP请求头:
X-Scope-OrgID,所有写入、查询请求都必须携带这个header。
1. 租户ID传递全链路
-
客户端(OTel Collector / Prometheus remote_write)发请求,带上
X-Scope-OrgID: tenant-01 -
Distributor读取这个Header,识别租户ID
-
所有后续逻辑:分片、Ingester内存分区、对象存储Block、Compactor任务、Ruler告警规则全部绑定这个租户ID
2. 三层隔离机制
① 逻辑隔离(最核心)
-
Ingester内部:不同租户的时序数据完全分开管理,内存里按租户划分数据空间;
-
对象存储: 桶内路径按租户ID区分,格式类似:
<bucket>/<tenant-id>/blocks/...不同租户的Block文件放在不同路径前缀。 Compactor处理Block的时候,按租户独立扫描、独立合并。
即使两个租户有完全一样的指标名+标签,也不会互相干扰。
② 租户配额与限流(Mimir内置)
可以为每个租户单独配置:
-
最大活跃时序数量(series上限,防止某个租户高基数打爆整个集群)
-
写入样本速率上限
-
查询并发、查询最大时间范围
-
存储块大小限制 Distributor会拦截超过配额的写入/查询请求,实现租户资源隔离。
③ 权限隔离(上层配套)
Mimir本身不内置账号密码、RBAC权限系统。
-
Mimir只负责识别
X-Scope-OrgID,保证租户数据隔离; -
账号、登录、谁能访问哪个tenantId,一般交给前置网关(nginx/ingress/认证网关)处理: 用户登录 → 网关校验权限 → 网关在转发请求时,自动注入对应的
X-Scope-OrgID,客户端不能随便伪造这个header(网关层禁止客户端直接传入)。
安全最佳实践:前端网关拦截客户端传来的X-Scope-OrgID,由认证网关统一注入,防止客户端伪造租户ID访问别人数据。
3. 多租户两种使用场景
-
SaaS场景:多业务/多客户,数据完全隔离 每个客户分配独立tenant-id,资源配额独立,互相看不到数据。
-
企业内部多业务线 业务A:
tenant=biz-a;业务B:tenant=biz-b; 共享同一套Mimir集群硬件,但是指标数据隔离,配额分开。
4. 单租户模式
如果不需要多租户,部署Mimir可以固定使用一个tenant-id,所有请求统一写同一个租户。 单体模式默认就是单租户使用。
5.3 Mimir支持的写入协议
-
Prometheus remote_write(主流,OTel Collector推荐走这个)
-
OTLP over HTTP(仅Metrics,不支持gRPC OTLP,不支持Trace/Logs)
-
支持Influx Line协议
所有写入请求,都需要携带
X-Scope-OrgID。
OTel Collector配置片段:
exporters:
prometheusremotewrite:
endpoint: "http://mimir-distributor/api/v1/push"
headers:
X-Scope-OrgID: "biz-a" # 租户ID
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheusremotewrite]
5.4 Mimir优缺点
✅ 优点
-
原生多租户硬隔离,SaaS、多业务线场景非常合适;
-
存算分离,基于对象存储,指标可以长期保存;
-
针对高基数时序做大量优化,内置租户级时序配额保护;
-
原生支持PromQL,Ruler内置告警;LGTM生态和Tempo、Loki深度打通;
-
支持单体模式快速测试,也可以横向扩展成大规模微服务集群。
❌ 缺点
-
AGPLv3协议:如果修改Mimir源码,对外提供托管服务,需要开源你的修改代码;
-
微服务集群部署组件较多,运维复杂度高于VictoriaMetrics;
-
不自带采集scrape能力,采集需要依赖OTel Collector或者vmagent/Prometheus;
-
不存储Trace、Logs,只处理指标。
-
Mimir 的冷存储就是依赖对象存储,两条路:
-
公有云对象存储:AWS S3、阿里云 OSS、腾讯云 COS、华为 OBS,直接买现成服务,不用自己运维存储节点。
-
自建对象存储:MinIO(最常用)、Ceph RGW,自己部署、维护集群,对外提供 S3 兼容 API,Mimir 直接对接。
-
5.5 Mimir多租户关键误区澄清
-
❌ 不是靠label标签区分租户(Thanos那种弱隔离) ✅ Mimir是顶层租户ID隔离,时序、存储块、告警规则全部按租户隔离。
-
❌ Mimir自带账号登录 ✅ Mimir没有账号/RBAC,租户认证交给前置网关。
-
❌ 多租户必须部署复杂微服务集群 ✅ Mimir单体模式同样支持多租户 ,单体下也可以接收不同
X-Scope-OrgID的数据。
六、VictoriaMetrics 开源版(社区版)多租户说明
关键点:
单节点 vmsingle:没有原生硬隔离多租户,只能用标签模拟弱隔离;
集群版 vmcluster(开源)原生支持硬隔离多租户 ,租户靠
accountID[:projectID],放在URL路径:/insert/ACCOUNT_ID/prometheus/api/v1/write、/select/ACCOUNT_ID/prometheus;社区版集群缺少企业版的:租户级限流、租户统计、租户配额、按租户独立告警规则,这些是Enterprise独有。
vmauth 属于开源组件,用来做前置鉴权、路由,是社区版做多租户的核心网关。
开源协议:VictoriaMetrics 社区版是 Apache2.0。
6.1 两种多租户方案(开源版可选)
方案A:VM Cluster(集群版,推荐,硬隔离)
底层 vminsert / vmstorage / vmselect 原生支持多租户,不同租户数据物理上分开namespace,同一个指标名+标签,不同accountID互相隔离,属于硬隔离。 租户标识放在URL路径:
# 写入:accountID=100
http://vminsert:8480/insert/100/prometheus/api/v1/write
# 查询:accountID=100
http://vmselect:8481/select/100/prometheus/api/v1/query
也支持
accountID:projectID两级租户,例如/insert/100:5/prometheus/...。 租户在第一次写入数据时自动创建,不需要预先在VM里注册租户。
方案B:vmsingle 单节点(仅标签模拟,弱隔离,不适合SaaS)
单节点没有内置租户namespace。 实现方式:所有指标强制注入 tenant="biz-a" 标签;查询时网关强制追加标签过滤 {tenant="biz-a"}。 缺点:
-
底层数据混在同一个存储里;
-
如果查询语句去掉标签,就能跨租户读取;
-
无法做租户级资源隔离,一个租户高基数会打爆整个实例。
适合企业内部业务线隔离,严禁对外给客户做SaaS。
6.2 在开源集群版上做多租户,需要做的工作
1. 部署组件:vmcluster + vmauth(必须)
OTel Collector / VMAgent → vmauth(鉴权网关) → vminsert(写入)
Grafana → vmauth → vmselect(查询)
vmauth是开源自带的反向代理网关,承担:
-
鉴权(BasicAuth / Bearer Token)
-
校验token → 映射到对应的
accountID -
丢弃客户端传入的租户标识,由vmauth重写URL,注入正确的tenant路径
-
可以给不同用户增加强制标签过滤(extra filters)
和Mimir的思路一样:VM集群本身不做账号、RBAC、token校验,只识别URL里的accountID;鉴权交给前置vmauth。
vmauth 核心逻辑示例(auth.config)
users:
- username: "biz-a-agent"
password: "secret123"
url_map:
- src_paths: ["/api/v1/write"]
url_prefix: "http://vminsert:8480/insert/100/prometheus"
- src_paths: ["/api/v1/query*"]
url_prefix: "http://vmselect:8481/select/100/prometheus"
客户端只访问vmauth,不需要带tenantID;vmauth鉴权成功自动把请求转发到 /insert/100/...。 攻击者无法伪造租户,因为客户端不能直接访问后端vminsert/vmselect。
2. 自建租户管理后台(开源版没有)
你需要自己维护一张映射表:apiKey/username → accountID,功能:
-
租户创建、删除,分配accountID;
-
管理API Key/账号,支持吊销密钥;
-
租户元数据(名称、负责人、备注)。
企业版自带租户统计,社区版只能自己采集指标,统计每个accountID的写入量、活跃时序。
3. 资源保护(社区版最大短板,需要自己开发)
Enterprise版自带租户级限流、样本速率上限、最大活跃时序保护;社区版集群没有内置租户配额。 所以开源集群要自己做防护,两种方式:
-
在vmauth层做全局限流(粗粒度):按token/账号做请求QPS限流;缺点:无法感知时序数量、样本速率。
-
自建监控告警
:采集VM内置的租户指标(
vm_tenant_*),监控每个accountID活跃时序、写入样本速率,超过阈值触发告警,人工/自动拉黑该租户的API Key。
风险:如果某个租户疯狂写入高基数指标,会占用整个集群资源,影响其他租户。社区版没有原生硬配额阻断。
4. 告警规则(vmalert,开源)
vmalert支持多租户,但社区版vmalert没有按租户独立规则管理。 两种方案:
-
为每个租户独立部署一套vmalert(简单,资源开销高);
-
单套vmalert,在规则里硬编码tenant标签过滤,区分不同租户告警。
Enterprise才支持原生租户隔离的告警规则管理。
5. 存储与备份
vmstorage底层自动按accountID隔离数据;备份可以按全量快照,没有原生单租户导出/备份API,如需单租户数据导出,需要自己开发。
6. 安全加固清单
-
后端
vminsert/vmselect/vmstorage禁止直接暴露给客户端,只允许vmauth访问; -
所有流量走HTTPS;
-
vmauth禁止客户端传入任何能控制租户的URL参数;
-
OTel Collector上报时,不要让业务代码携带tenant标签,由Collector/vmauth统一注入;
-
在Collector阶段做attribute过滤,防止高基数标签(traceId、user_id)进入系统。
6.3 VM开源集群多租户 vs Mimir多租户对比
| 项目 | 租户标识 | 内置租户配额 | 鉴权 | 隔离级别 | 协议 | 开源协议 |
|---|---|---|---|---|---|---|
| VM集群开源 | URL路径 accountID | ❌无,需要自建限流告警 | 交给vmauth | 硬隔离,租户数据namespace隔离 | remote_write、OTLP/HTTP | Apache2.0 |
| Mimir | HTTP头 X-Scope-OrgID | ✅内置租户时序/速率配额 | 前置网关 | 硬隔离,租户独立存储块 | remote_write、OTLP/HTTP | AGPLv3 |
核心差异:Mimir内置租户资源保护,防止单租户打爆集群;VM社区版没有,必须自己做监控+限流告警。
6.4 选型建议
-
SaaS平台,多客户隔离,需要租户资源保护:优先Mimir;
-
企业内部多业务线,资源可以信任,不想AGPL协议约束:选VM集群开源+vmauth;
-
小规模,不想部署复杂集群:vmsingle+标签弱隔离,只内部使用,不对外。
6.5 误区澄清
-
❌ vmcluster开源版多租户不是靠label标签隔离 ✅ 是URL里的accountID,底层存储按租户namespace隔离,属于硬隔离。
-
❌ vmauth只能做BasicAuth ✅ 支持Bearer token、JWT(v1.138+),适合OIDC/Grafana登录场景。
-
❌ 开源版可以按租户单独设置存储保留时间 ✅ 不支持,保留时间是全局vmstorage配置;按租户不同retention是企业版能力。