Skip to content

存储

https://prometheus.io/docs/prometheus/latest/storage/

Prometheus 包含一个本地磁盘上的时间序列数据库,也可以选择集成远程存储系统。

本地存储

Prometheus 的本地时序数据库以一种自定义且高效的格式将数据存储在本地存储中。

磁盘布局

Prometheus 会将采集到的样本按两个小时为一组划分为数据块(block)。每个两个小时的数据块对应一个目录,该目录中包含:

  • 一个 chunks 子目录(存放该时间段内所有时间序列的样本)
  • 一个元数据文件(metadata file)
  • 一个索引文件(index file,用于将指标名称和标签映射到 chunks 目录中的时间序列)

chunks 目录中的样本数据会被打包为一个或多个段文件(segment file),每个文件默认最大为 512MB。 当通过 API 删除某些时间序列时,删除操作不会立即清除数据,而是将删除记录写入单独的标记文件(tombstone file)中。

当前用于接收新数据的块存储在内存中,并不会立即写入磁盘。 为防止崩溃导致数据丢失,Prometheus 使用预写日志(WAL)进行保护,服务重启时可以通过 WAL 进行恢复。WAL 文件存放在 wal 目录中,并按每个 128MB 分段。 由于这些日志保存的是尚未压缩的原始数据,因此它们的体积通常比常规数据块更大。

Prometheus 会至少保留三个 WAL 文件。对于数据量大的服务器,为确保至少保留两小时的原始数据,可能会保留更多的 WAL 文件。

一个 Prometheus 服务器的数据目录结构大致如下所示:

bash
./data
├── 01BKGV7JBM69T2G1BGBGM6KB12
   └── meta.json
├── 01BKGTZQ1SYQJTR4PB43C8PD98
   ├── chunks
   └── 000001
   ├── tombstones
   ├── index
   └── meta.json
├── 01BKGTZQ1HHWHV8FBJXW1Y3W0K
   └── meta.json
├── 01BKGV7JC0RY8A6MACW02A2PJD
   ├── chunks
   └── 000001
   ├── tombstones
   ├── index
   └── meta.json
├── chunks_head
   └── 000001
└── wal
    ├── 000000002
    └── checkpoint.00000001
        └── 00000000

请注意,本地存储的一个限制是它不具备集群能力,也不支持数据副本。因此,在硬盘或节点故障的情况下,它无法实现任意的可扩展性或持久性,应当像管理其他单节点数据库一样进行管理。

建议使用 快照 进行备份。 如果不使用快照进行备份,则有可能丢失自上次 WAL(预写日志)同步以来记录的数据,而 WAL 同步通常每两小时发生一次。通过合理的架构设计,本地存储也可以保留多年的数据。

另外,也可以通过 远程读写 API 使用外部存储。这些系统在持久性、性能和效率方面差异较大,因此需要谨慎评估。

有关文件格式的更多详细信息,请参阅 TSDB 格式

压缩

最初的两个小时的数据块最终会在后台被压缩为更大的数据块。

压缩过程会创建更大的数据块,这些块包含的数据跨度最多为保留时间的 10%,或 31 天,以较小者为准。

操作方面

Prometheus 有几个用于配置本地存储的重要参数,主要包括:

  • -storage.tsdb.path:Prometheus 写入数据库的位置,默认是 data/ 目录。
  • -storage.tsdb.retention.time:样本在存储中保留的时长。如果既没有设置此参数,也没有设置 storage.tsdb.retention.size,则默认保留时间为 15 天。支持的单位有:年(y)、周(w)、天(d)、小时(h)、分钟(m)、秒(s)、毫秒(ms)。
  • -storage.tsdb.retention.size:最大保留的存储块字节数。最旧的数据会被优先删除。默认值为 0,表示禁用。支持单位:B、KB、MB、GB、TB、PB、EB。例如:"512MB"。单位基于二进制,1KB = 1024B。只有持久化的存储块会被删除以满足此保留策略,虽然 WAL 和内存映射块(m-mapped chunks)也计入总大小。因此,磁盘的最小空间需求是 wal(包含 WAL 和 Checkpoint)目录与 chunks_head(内存映射的 Head chunks)目录合计的峰值空间,这个峰值每两小时产生一次。
  • -storage.tsdb.wal-compression:启用写前日志(WAL)压缩。根据数据不同,启用后 WAL 大小通常会减半,且 CPU 负载增加较小。该参数自版本 2.11.0 引入,并在 2.20.0 版本默认启用。注意,一旦启用此功能,降级 Prometheus 到低于 2.11.0 的版本时,需要删除 WAL 文件。

Prometheus 平均每个样本仅占用 1 到 2 字节的存储空间。因此,在规划 Prometheus 服务器容量时,可以使用以下大致公式:

bash
needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample

为了降低采集的样本数量,可以通过减少抓取的时间序列数量(即减少抓取的目标数或每个目标的序列数),或者增加抓取间隔来实现。不过,由于同一序列内样本会被压缩,减少时间序列数量通常更有效。

如果本地存储出现严重损坏,导致 Prometheus 无法启动,建议先备份存储目录,然后从备份中恢复被损坏的块目录。如果没有备份,最后的办法是删除损坏的文件,比如单独删除某些块目录或写前日志(WAL)文件。需要注意的是,这样做会丢失对应时间段内的数据。

CAUTION

Prometheus 本地存储不支持非 POSIX 标准的文件系统,因为这可能导致无法恢复的损坏。NFS 文件系统(包括 AWS 的 EFS)也不被支持。虽然 NFS 理论上可以是 POSIX 兼容的,但大多数实现并非如此。强烈建议使用本地文件系统以保证可靠性。

如果同时设置了时间和大小两种数据保留策略,系统会优先执行先触发的那个策略。

过期数据块的清理在后台进行,可能需要最长两小时才能完成。只有数据块完全过期后才会被删除。

正确设置保留大小

如果你使用 storage.tsdb.retention.size 来设置 Prometheus 的存储容量上限,那么应当根据为 Prometheus 分配的存储空间,合理确定该值的大小。建议适当降低保留数据的容量上限,以预留出缓冲空间,从而确保旧的数据能够在磁盘占满之前被删除。

目前推荐将保留容量设置为 Prometheus 分配磁盘空间的 最多 80% 到 85%。这样可以提高在存储耗尽前成功清理旧数据的可能性,避免磁盘被写满。

远程存储集成

Prometheus 的本地存储受限于单节点的可扩展性和持久性。Prometheus 并不试图在自身内部解决集群存储问题,而是通过一组接口,支持与远程存储系统集成。

概览

Prometheus 通过以下四种方式与远程存储系统集成:

  • Prometheus 可以将采集到的样本数据以 Remote Write 格式 写入到远程 URL。
  • Prometheus 可以接收来自其他客户端发送的、符合 Remote Write 格式 的样本数据。
  • Prometheus 可以从远程 URL 读取(回读)样本数据,格式为 Remote Read 格式
  • Prometheus 可以将样本数据以 Remote Read 格式 返回给请求的客户端。

remote-integrations

远程读写协议都采用了基于 HTTP 的 Snappy 压缩 Protocol Buffer 编码。需要注意的是,远程读协议尚未被视为稳定的 API。

写入协议方面,Prometheus 服务器支持两个版本:

关于如何在 Prometheus 中配置作为客户端的远程存储集成,详细说明请参考 Prometheus 配置文档中的 remote_writeremote_read 部分。

需要注意的是,在远程读取流程中,Prometheus 仅从远端获取满足标签选择器和时间范围的原始时间序列数据。 所有基于 PromQL 的查询计算仍在本地的 Prometheus 服务器中执行。 这意味着远程读取查询存在一定的可扩展性限制,因为必须先将所有所需数据加载到本地查询的 Prometheus 服务器后,再进行处理。 对于实现完全分布式的 PromQL 计算,目前认为不可行。

此外,Prometheus 也支持作为这两种协议的服务端。 通过设置命令行参数 --web.enable-remote-write-receiver 可启用内置的远程写入接收器,启用后远程写入接收端点为 /api/v1/write。 远程读取接口则可通过 /api/v1/read 访问。

现有集成

想了解更多关于已有远程存储系统集成的内容,请参阅官方的 集成文档

从 OpenMetrics 格式回填

如果用户想将符合 OpenMetrics 格式的数据导入到 Prometheus 的 TSDB 中,可以通过回填(backfilling)来实现。不过需要注意,不能回填最近 3 小时内的数据(即当前的 head block),因为这段时间的数据可能仍在被 Prometheus 修改,回填可能导致数据冲突。

回填会创建新的 TSDB 数据块(block),每个数据块包含 2 小时的指标数据,这样可以限制创建数据块时的内存消耗。之后 Prometheus 服务器会对这些 2 小时的数据块进行合并压缩,生成更大的数据块。

典型用途

回填常用于将其他监控系统或时序数据库中的指标数据迁移到 Prometheus。迁移前,必须先将源数据转换成 OpenMetrics 格式,这是回填的输入格式。

需要注意的是,该回填过程不支持原生的直方图(histograms)和过期标记(staleness markers),因为它们无法用 OpenMetrics 格式表示。

使用方法

回填功能通过 promtool 命令行工具使用。promtool 会将数据块写入一个目录,默认输出目录是 ./data/,也可以通过命令的可选参数指定其他输出目录。

bash
promtool tsdb create-blocks-from openmetrics <输入文> [<输出目录>]

数据块创建完成后,需要将它们移动到 Prometheus 的数据目录中。若生成的数据块与 Prometheus 现有数据块有重叠(覆盖)部分,在 Prometheus 2.38 及之前版本中需要启动参数 --storage.tsdb.allow-overlapping-blocks 才能正常加载。请注意,所有回填的数据都会受到 Prometheus 服务器配置的保留策略(按时间或大小)限制。

较长的数据块时长

默认情况下,promtool 会使用 2 小时作为数据块时长,这种方式最通用且安全。但如果需要回填的数据时间跨度很长,可以考虑用更长的数据块时长,这样回填速度更快,也能减少后续 TSDB 的合并压缩次数。

可以通过 --max-block-duration 参数来设置数据块的最长时长,回填工具会选用不超过该时长的合适数据块大小。

虽然更大的数据块有利于提高大规模数据回填的效率,但也存在缺点:

  • 基于时间的保留策略:只要数据块中的任一样本在保留时间范围内,整个数据块都会被保留,可能导致保留更多过期数据。
  • 基于大小的保留策略:只要数据块大小超过限制,整个数据块都会被删除,即使只超出一点点。

因此,选择较大数据块进行回填需谨慎,不建议在生产环境中使用。

录制规则的回填

当创建一个新的录制规则时,之前并不存在该规则的历史数据。录制规则的数据仅从创建时开始生效。使用 promtool 可以生成录制规则的历史数据块。

使用方法

查看所有选项,请执行:$ promtool tsdb create-blocks-from rules --help

示例命令:

bash
promtool tsdb create-blocks-from rules \
    --start 1617079873 \
    --end 1617097873 \
    --url http://mypromserver.com:9090 \
    rules.yaml rules2.yaml

传入的录制规则文件应为普通的 Prometheus 规则文件

promtool tsdb create-blocks-from rules 命令的输出是一个目录,里面包含了对应录制规则所有历史数据的数据块。默认输出目录为 data/。要使用这些新的数据块,必须将它们移动到正在运行的 Prometheus 实例的数据目录(即 storage.tsdb.path)中。对于 Prometheus 2.38 及之前版本,还需要开启参数 --storage.tsdb.allow-overlapping-blocks。移动完成后,新的数据块将在下一次压缩(compaction)时与已有数据块合并。

限制

  • 如果多次运行规则回填,且时间范围重叠,则每次都会生成包含相同数据的数据块。
  • 会对录制规则文件中的所有规则进行计算。
  • 如果规则文件中定义了 interval,则优先使用该值,覆盖规则回填命令中的 eval-interval 参数。
  • 目前规则文件中的告警(alerts)会被忽略。
  • 同一组(group)内的规则无法引用其他规则的回填结果,也就是说规则之间互相依赖的回填不被支持。解决办法是分多次回填,先生成被依赖的规则数据,并将这些数据移动到 Prometheus 服务器数据目录,使其能通过 Prometheus API 被访问。