应用性能监测(APM)之 (四)Prometheus与Metrics集群存储

目录

一、时序数据库

[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生态))

方式B:原生SQL模式使用

[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 关键区别)

Thanos优缺点

选型小结

[四、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

选型小结

  1. 血缘区分:Thanos不改变Prometheus存储;Mimir来自Cortex分支,完全独立存储;VM是从零自研引擎。

  2. 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"}

引擎查找:

  1. 找到 method="GET" 对应的所有时序集合

  2. 找到 status="200" 对应的所有时序集合

  3. 取交集,得到匹配的时序列表

  4. 读取这些时序的时间序列样本,做聚合计算

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:

  1. 不要把唯一ID(trace_id、request_id、user_id)直接作为label,会高基数爆炸。

  2. 无用attributes要在OTel Collector阶段过滤掉(processor: attributes删除),减少label数量。

  3. 标签尽量控制在少量低基数维度: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)

  1. WAL(预写日志,wal目录) 所有采集到的sample(时间戳+float值)先写WAL,再写入内存Head。
  • 作用:崩溃恢复,Prometheus重启后回放WAL,重建内存Head,防止丢最近数据。

  • WAL是分段文件,每段默认128MB,循环复用。

  • WAL里是原始未压缩样本,体积远大于压缩后的Block。

  1. Head(内存活跃块,当前正在写入的2小时窗口) 内存中维护所有活跃时序,接收新样本;
  • 时序(series):metric_name + labelset唯一标识一条时序

  • Chunk:一条时序的连续样本集合,采用Gorilla XOR压缩(时间戳双delta,数值XOR),压缩率极高,平均每个样本1~2字节。

Head是可变的,可以追加样本;当时间窗口满2小时,Head被关闭,固化成磁盘上的Block,之后这个Block永远只读、不可修改。

  1. 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时间范围:

  1. 查最近2小时:读内存Head + WAL,直接取内存数据,速度最快

  2. 查2小时~保留期内历史数据:遍历磁盘上对应的Block,读取index找到对应时序,再读chunks里的压缩样本,解压计算。

3. TSDB核心短板(这就是Thanos诞生的原因)

  1. 单机绑定本地磁盘,无法水平扩展;时序量大后内存暴涨,高基数容易OOM

  2. 本地磁盘保存,默认15天,长期存储成本高

  3. 多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优缺点

✅ 优点

  1. 完全复用原生Prometheus,原有采集、告警几乎不用改动,平滑扩容

  2. 块存储存到对象存储,低成本长期保存海量指标

  3. 跨多个Prometheus集群,全局统一PromQL查询,HA副本自动去重

  4. Apache2.0协议,商用友好

❌ 缺点

  1. 组件多(Sidecar/Querier/StoreGateway/Compactor),运维复杂度高

  2. 最近2小时热数据在Prometheus本地,故障会丢这部分数据

  3. Compactor是单实例,对象存储桶大了之后Compactor容易成为瓶颈

  4. 高基数场景没有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 核心架构(微服务组件)

写入链路组件

  1. Distributor(分发器,无状态) 接收remote_write / OTLP metrics请求;校验指标、租户ID(X-Scope-OrgID);对时序做hash,通过hash环(ring)把样本转发给对应的Ingester;做数据副本(默认3副本)保证高可用。

  2. Ingester(写入存储节点,有状态)

    • 接收样本,先写WAL预写日志,再写入内存;

    • 内部每个租户独立维护一套Prometheus原生TSDB;

    • 内存积累满2小时,固化成不可变Block,上传到S3/对象存储;

    • 保存最近未固化的热数据,查询时Querier会来Ingester读取实时数据。

重点:Block格式和Prometheus TSDB完全一致,和Thanos Block格式同源。

查询链路组件

  1. Querier(查询器,无状态)

    对外暴露Prometheus HTTP API,接收PromQL;并行查询两处数据:

    • 实时热数据:Ingester内存里还没固化的样本

    • 历史冷数据:请求StoreGateway读取对象存储中的Block 合并、去重、聚合结果返回给Grafana。

  2. Store Gateway(对象存储网关,半有状态) 读取对象存储里的Block元信息、索引,缓存索引;按需从对象存储拉取chunks样本,给Querier使用。

  3. Compactor(块合并器) 后台任务,只操作对象存储桶:把多个2小时小Block合并成更大Block,减少文件数量,提升查询速度;做去重、保留策略清理过期数据。

可选组件

  • Ruler:执行Prometheus记录规则、告警规则;

  • Query Frontend:查询网关,做查询限流、分片、缓存、大查询拆分;

  • Alertmanager:告警管理。

4.2 完整写入流程

  1. OTel Collector / Prometheus → remote_write 推给 Cortex Distributor

  2. Distributor hash分片,复制多份转发给Ingester

  3. Ingester:WAL写入 + 内存TSDB写入

  4. 满2小时,固化Block,上传对象存储

  5. Compactor后台合并对象存储内的Block

特点:全链路Push模型,不需要Prometheus本地TSDB。你可以直接OTel Collector remote_write推Cortex,不需要部署Prometheus。

4.3 支持的写入协议

  1. Prometheus Remote Write(主流)

  2. OTLP over HTTP(仅Metrics,不支持Trace、Logs,gRPC OTLP不支持)

    和Mimir的写入协议基本一致,同样依靠 X-Scope-OrgID 做租户隔离。

4.4 Cortex 优缺点

✅ 优点

  1. 原生多租户,租户之间数据完全隔离,适合SaaS平台;

  2. 完全复用Prometheus TSDB Block模型,PromQL原生支持;

  3. 组件可以独立横向扩容;

  4. 支持对象存储长期保存,不受单机磁盘限制;

  5. Apache2.0协议,商用友好,修改代码不需要开源。

❌ 缺点

  1. 项目进入维护模式,新功能开发全部转移到Mimir;新项目不推荐新建Cortex集群;

  2. 组件非常多,运维复杂度高;

  3. 早期Chunk存储模式已废弃,现在只用Block存储;

  4. Ingester故障时,内存中未固化的2小时热数据有丢失风险(依赖副本策略);

  5. 高基数场景优化弱于Mimir,Mimir修复了大量Cortex遗留技术债。

4.5 Cortex vs Mimir(重点,二者血缘关系)

Mimir是Grafana从Cortex fork出来的,架构同源,但是做了大量重构:

  1. Cortex:Apache2.0,维护模式;组件多,高基数处理弱;

  2. 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核心组件(微服务架构,也支持单体模式快速测试)

  1. Distributor(分发器,无状态,写入入口)

    接收 remote_write / OTLP/HTTP metrics 请求,是所有写入流量的入口。

    • 读取HTTP请求头 X-Scope-OrgID 获取租户ID;

    • 对时序做hash分片,写入到对应的Ingester;

    • 做多副本写入(默认3副本)保证高可用;

    • 做租户层面的限流、写入校验、拒绝非法标签。

  2. Ingester(写入节点,有状态)

    接收Distributor转发的样本,内部为

    每个租户独立维护内存时序数据

    。

    • 写入流程:WAL预写日志 → 内存时序;

    • 同样2小时窗口固化成不可变Block,上传到对象存储;

    • 保存还没固化的热数据,查询时提供实时数据;

    • 租户之间内存数据隔离。

  3. Querier(查询节点,无状态)

    对外暴露Prometheus HTTP API,Grafana对接这个组件。

    • 读取 X-Scope-OrgID,限定只查询该租户的数据;

    • 并行查询两处数据:Ingester内存热数据 + StoreGateway读取对象存储冷块;

    • 合并、去重、计算PromQL结果返回。

  4. Store-gateway(对象存储网关,半有状态) 读取对象存储里的Block元数据和索引,缓存索引;按需拉取chunk样本。

    每个租户的Block在对象存储里是隔离的。

  5. Compactor(块合并器,后台任务) 在对象存储侧,对每个租户的Block单独做合并、压缩、降采样、过期清理。租户之间Block互不干扰。

  6. Ruler(可选,告警引擎) 按租户独立执行PromQL记录规则、告警规则,每个租户拥有独立规则配置,独立告警。

  7. Query-frontend(可选) 查询网关,做查询拆分、缓存、限流、队列控制,保护后端。

5.2 Mimir 多租户是怎么实现的(重点)

Mimir是原生硬隔离多租户 ,不是简单用标签模拟,租户A完全看不到租户B的数据。 租户识别依靠HTTP请求头:X-Scope-OrgID,所有写入、查询请求都必须携带这个header。

1. 租户ID传递全链路

  1. 客户端(OTel Collector / Prometheus remote_write)发请求,带上 X-Scope-OrgID: tenant-01

  2. Distributor读取这个Header,识别租户ID

  3. 所有后续逻辑:分片、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. 多租户两种使用场景

  1. SaaS场景:多业务/多客户,数据完全隔离 每个客户分配独立tenant-id,资源配额独立,互相看不到数据。

  2. 企业内部多业务线 业务A:tenant=biz-a;业务B:tenant=biz-b; 共享同一套Mimir集群硬件,但是指标数据隔离,配额分开。

4. 单租户模式

如果不需要多租户,部署Mimir可以固定使用一个tenant-id,所有请求统一写同一个租户。 单体模式默认就是单租户使用。

5.3 Mimir支持的写入协议

  1. Prometheus remote_write(主流,OTel Collector推荐走这个)

  2. OTLP over HTTP(仅Metrics,不支持gRPC OTLP,不支持Trace/Logs)

  3. 支持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优缺点

✅ 优点

  1. 原生多租户硬隔离,SaaS、多业务线场景非常合适;

  2. 存算分离,基于对象存储,指标可以长期保存;

  3. 针对高基数时序做大量优化,内置租户级时序配额保护;

  4. 原生支持PromQL,Ruler内置告警;LGTM生态和Tempo、Loki深度打通;

  5. 支持单体模式快速测试,也可以横向扩展成大规模微服务集群。

❌ 缺点

  1. AGPLv3协议:如果修改Mimir源码,对外提供托管服务,需要开源你的修改代码;

  2. 微服务集群部署组件较多,运维复杂度高于VictoriaMetrics;

  3. 不自带采集scrape能力,采集需要依赖OTel Collector或者vmagent/Prometheus;

  4. 不存储Trace、Logs,只处理指标。

  5. Mimir 的冷存储就是依赖对象存储,两条路:

    • 公有云对象存储:AWS S3、阿里云 OSS、腾讯云 COS、华为 OBS,直接买现成服务,不用自己运维存储节点。

    • 自建对象存储:MinIO(最常用)、Ceph RGW,自己部署、维护集群,对外提供 S3 兼容 API,Mimir 直接对接。

5.5 Mimir多租户关键误区澄清

  1. ❌ 不是靠label标签区分租户(Thanos那种弱隔离) ✅ Mimir是顶层租户ID隔离,时序、存储块、告警规则全部按租户隔离。

  2. ❌ Mimir自带账号登录 ✅ Mimir没有账号/RBAC,租户认证交给前置网关。

  3. ❌ 多租户必须部署复杂微服务集群 ✅ Mimir单体模式同样支持多租户 ,单体下也可以接收不同X-Scope-OrgID的数据。

六、VictoriaMetrics 开源版(社区版)多租户说明

关键点:

  1. 单节点 vmsingle:没有原生硬隔离多租户,只能用标签模拟弱隔离;

  2. 集群版 vmcluster(开源)原生支持硬隔离多租户 ,租户靠 accountID[:projectID],放在URL路径:/insert/ACCOUNT_ID/prometheus/api/v1/write、/select/ACCOUNT_ID/prometheus;

  3. 社区版集群缺少企业版的:租户级限流、租户统计、租户配额、按租户独立告警规则,这些是Enterprise独有。

  4. 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是开源自带的反向代理网关,承担:

  1. 鉴权(BasicAuth / Bearer Token)

  2. 校验token → 映射到对应的 accountID

  3. 丢弃客户端传入的租户标识,由vmauth重写URL,注入正确的tenant路径

  4. 可以给不同用户增加强制标签过滤(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版自带租户级限流、样本速率上限、最大活跃时序保护;社区版集群没有内置租户配额。 所以开源集群要自己做防护,两种方式:

  1. 在vmauth层做全局限流(粗粒度):按token/账号做请求QPS限流;缺点:无法感知时序数量、样本速率。

  2. 自建监控告警

    :采集VM内置的租户指标(

    复制代码
     vm_tenant_*

    ),监控每个accountID活跃时序、写入样本速率,超过阈值触发告警,人工/自动拉黑该租户的API Key。

    风险:如果某个租户疯狂写入高基数指标,会占用整个集群资源,影响其他租户。社区版没有原生硬配额阻断。

4. 告警规则(vmalert,开源)

vmalert支持多租户,但社区版vmalert没有按租户独立规则管理。 两种方案:

  • 为每个租户独立部署一套vmalert(简单,资源开销高);

  • 单套vmalert,在规则里硬编码tenant标签过滤,区分不同租户告警。

    Enterprise才支持原生租户隔离的告警规则管理。

5. 存储与备份

vmstorage底层自动按accountID隔离数据;备份可以按全量快照,没有原生单租户导出/备份API,如需单租户数据导出,需要自己开发。

6. 安全加固清单

  1. 后端 vminsert/vmselect/vmstorage 禁止直接暴露给客户端,只允许vmauth访问;

  2. 所有流量走HTTPS;

  3. vmauth禁止客户端传入任何能控制租户的URL参数;

  4. OTel Collector上报时,不要让业务代码携带tenant标签,由Collector/vmauth统一注入;

  5. 在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 选型建议

  1. SaaS平台,多客户隔离,需要租户资源保护:优先Mimir;

  2. 企业内部多业务线,资源可以信任,不想AGPL协议约束:选VM集群开源+vmauth;

  3. 小规模,不想部署复杂集群:vmsingle+标签弱隔离,只内部使用,不对外。

6.5 误区澄清

  1. ❌ vmcluster开源版多租户不是靠label标签隔离 ✅ 是URL里的accountID,底层存储按租户namespace隔离,属于硬隔离。

  2. ❌ vmauth只能做BasicAuth ✅ 支持Bearer token、JWT(v1.138+),适合OIDC/Grafana登录场景。

  3. ❌ 开源版可以按租户单独设置存储保留时间 ✅ 不支持,保留时间是全局vmstorage配置;按租户不同retention是企业版能力。

相关推荐
Cheney Pan3 小时前
第09篇 告警体系设计:Alertmanager 路由与告警治理
prometheus
蓝胖的四次元口袋1 天前
Prometheus+Grafana知识梳理(1)
grafana·prometheus
晨陌y1 天前
CentOS 7 部署 mysqld_exporter:MySQL 指标采集、Prometheus 告警与远程监控
mysql·centos·prometheus
倔强的小石头_2 天前
Ubuntu部署Prometheus与Alertmanager:systemd配置、告警对接及cpolar远程访问
数据库·ubuntu·prometheus
打工仔折腾 AI2 天前
Prometheus接入Pushgateway实战:二进制与Docker部署、指标推送与远程写入
后端·python·docker·容器·性能优化·prometheus·ai agent 实战
Elastic 中国社区官方博客3 天前
两个依赖和一个配置块:通过 Prometheus 远程写入将 Spring Boot 指标发送到 Elasticsearch
大数据·数据库·spring boot·elasticsearch·搜索引擎·全文检索·prometheus
User_芊芊君子4 天前
Prometheus接入Pushgateway实战:二进制与Docker部署、指标推送及远程上报
docker·容器·prometheus
lbb 小魔仙10 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
zhoupenghui16811 天前
Golang pprof 工具详解:监控、压测与调优实战指南
prometheus·pprof