☰
Grafana Mimir TSDB 块上传(Block Upload)配置与实现详解:启用开关、校验规则与已知限制
2026/10/13 9:27:33 网站建设 项目流程
  • 时序数据库
  • 后端
  • 数据存储
  • 可观测性
  • 云原生

【免费下载链接】mimir

Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.

项目地址:https://gitcode.com/gh_mirrors/mimir/mimir
点击查看免费下载

Grafana Mimir 支持从 Prometheus、Cortex 乃至其他 Mimir 集群直接导入历史 TSDB 块(block),这是从存量监控系统向 Mimir 迁移数据时的核心能力之一。本文基于仓库中的配置文档与源码实现,完整讲解如何启用并配置该功能(全局与按租户两种粒度)、Mimir 在上传前/上传后分别执行哪些块校验、mimirtool backfill客户端与 compactor 上传 API 之间的完整交互流程,以及 Thanos 块不可上传、结果缓存失效、"过新"的块暂不可查这三类已知限制的原理与规避手段。

一、功能定位与总体说明

Grafana Mimir 支持上传来自 Prometheus、Cortex 或其他 Mimir 部署的历史 TSDB 块。需要特别注意的是:来自 Thanos 的块目前不支持直接上传(原因见本文"已知限制"一节,涉及 Thanos 外部标签)。

为了让块上传操作尽可能简单,Mimir 将客户端支持直接内置到了官方命令行工具mimirtool的backfill子命令中。该子命令的定位(源码描述)是"Upload Prometheus TSDB blocks to Grafana Mimir compactor",其定义见 backfill 命令注册。更完整的工具说明可参考仓库文档 mimirtool 手册。

块上传目前仍属于实验性功能(experimental),因此默认关闭。可以通过以下任一方式启用:

  • CLI 标志:-compactor.block-upload-enabled
  • 配置参数:limits.compactor_block_upload_enabled
limits: # Enable TSDB block upload compactor_block_upload_enabled: true

从源码看,这几个标志在 limits 定义 中注册,且每个都支持按租户覆盖(per-tenant override):

配置参数(limits.*)CLI 标志默认值作用
compactor_block_upload_enabled-compactor.block-upload-enabledfalse是否对租户开放块上传 API
compactor_block_upload_validation_enabled-compactor.block-upload-validation-enabledtrue上传完成后是否执行"整块"深度校验
compactor_block_upload_verify_chunks-compactor.block-upload-verify-chunkstrue深度校验时是否逐个校验 chunks(保留 index 校验)
compactor_block_upload_max_block_size_bytes-compactor.block-upload-max-block-size-bytes0(不限制)允许上传/校验的块大小上限(字节)

对应的按租户查询入口为 Overrides 方法,如CompactorBlockUploadEnabled(tenantID)、CompactorBlockUploadMaxBlockSizeBytes(userID),这正是"全局默认 + 按租户覆盖"这一 Mimir 常规配置模式的体现。

二、按租户启用 TSDB 块上传

如果集群启用了多租户,除了用上一节的方式对所有租户统一开启之外,也可以只为指定租户开启块上传,推荐做法是通过 runtime configuration 设置按租户覆盖:

  1. 先启用 runtime configuration(--runtime-config.mode=enabled等,详见上述文档);
  2. 在config-overrides.yaml中添加目标租户的覆盖项:
overrides: tenant1: compactor_block_upload_enabled: true

从服务端实现看,每一次上传 API 请求都会经过租户校验:parseBlockUploadParameters 会解析 URL 中的块 ID 和请求上下文中的租户 ID,然后调用c.cfgProvider.CompactorBlockUploadEnabled(tenantID),若该租户未启用上传则直接拒绝:

if !c.cfgProvider.CompactorBlockUploadEnabled(tenantID) { return ulid.ULID{}, "", errors.New("block upload is disabled") }

三、上传过程中的块校验规则

Mimir 对上传块采取"两步校验":先对meta.json做元数据检查,放行后才允许上传 index 与 chunks;上传完毕后再做更重的"整块校验"。

3.1 上传开始前的 meta.json 校验

在真正上传块数据之前,Mimir 会对meta.json文件执行以下检查(与文档 configure-tsdb-block-upload 所列规则一致):

  • 仅支持 TSDB "v1" 块——这是 Prometheus v2、Grafana Mimir 与 Thanos 使用的格式;
  • MinTime/MaxTime非法的块被拒绝(负值,或MaxTime < MinTime);
  • MinTime或MaxTime位于"未来"的块被拒绝;
  • 超出保留期(retention)的块被拒绝;
  • 时间跨度超过最大 compaction range(-compactor.block-ranges选项,最大档默认 24h)的块被拒绝;
  • 跨越最大 compaction range 边界的块被拒绝。例如最大 compaction range 为 24 小时时,前一天 23:00 开始、后一天 01:00 结束的块会被拒绝;
  • 带 Thanos 降采样(downsampling)配置的块被拒绝;
  • 超过compactor_block_upload_max_block_size_bytes(按租户覆盖)的块被拒绝;
  • 带"外部标签"(external labels,Thanos 特性)的块被拒绝,部分 Mimir 专属标签例外允许。

这些规则的源码实现在 blockvalidation 包。其中CheckMeta函数(meta.go)覆盖如下检查点:

  1. 降采样检查:meta.Thanos.Downsample.Resolution > 0时直接报block contains downsampled data;
  2. 外部标签白名单:仅允许 Mimir 的 compactor shard ID 外部标签(且值必须可被sharding.ParseShardIDLabelValue解析)以及若干已废弃的历史标签(会被清除),其他任何标签一律报unsupported external label: %s;
  3. 文件路径与大小白名单:meta.Thanos.Files中除meta.json外的文件,相对路径必须匹配正则^(index|chunks/\d{6})$(AllowedRelPath),且SizeBytes > 0;
  4. 块大小上限:CheckMaxBlockSize累加所有文件声明大小,超过MaxBlockSizeBytes时返回带限制值与大小的MaxBlockInvalidSizeError(<= 0表示不限制);
  5. 版本检查:meta.Version必须为block.TSDBVersion1,否则报version must be 1;
  6. 时间检查:MinTime < 0 || MaxTime < 0 || MaxTime < MinTime报invalid minTime/maxTime;任一时间戳大于当前时间(毫秒)报block time(s) greater than the present。

另外,保留期、时间跨度、跨边界这三项检查实现在 createBlockUpload:

// validate data is within the retention period retention := c.cfgProvider.CompactorBlocksRetentionPeriod(tenantID) if retention > 0 { threshold := time.Now().Add(-retention) if blockMaxTime.Before(threshold) { ... 422 ... } } blockDuration := blockMaxTime.Sub(blockMinTime) maxRange := c.compactorCfg.BlockRanges[len(c.compactorCfg.BlockRanges)-1] if blockDuration > maxRange { ... 422 ... } if blockMaxTime.After(blockMinTime.Truncate(maxRange).Add(maxRange)) { ... // 跨边界,422 }

即在元数据阶段即被拒绝的块,会以400 Bad Request(参数错误)或422 Unprocessable Entity(块内容不符合要求)返回。

3.2 上传完成后的整块校验(full block validation)

index 和 chunks 上传完成后,Mimir 会执行额外校验,确认块结构完整(well-formed)且不会给 Mimir 的后续运行带来问题。相关开关(均为按租户覆盖参数):

  • 通过compactor_block_upload_validation_enabled可以整体关闭整块校验(关闭后 finish 请求直接标记块完成);
  • 若只想跳过昂贵的 chunks 逐块校验而保留 index 校验,则使用compactor_block_upload_verify_chunks。

服务端对应的流程在 FinishBlockUpload:当CompactorBlockUploadValidationEnabled(tenantID)为真时,先检查并发数是否超过-compactor.max-block-upload-validation-concurrency(超出返回429 Too Many Requests),然后异步执行validateAndCompleteBlockUpload:

  • 校验期间会在块目录下写入validation.json心跳文件(每 1 分钟更新一次,validationHeartbeatInterval),超过 5 分钟(validationHeartbeatTimeout)未更新则视为 stale,允许重新开始校验;
  • 校验本身由 validateBlock 完成:先把块从对象存储下载到 compactor 本地临时目录(-compactor.data-dir下upload*前缀的目录,进程重启时会由 cleanupLeftoverValidationDirectories 清理残留),再把临时元数据文件改名为正式的meta.json,最后调用blockvalidation.CheckBlockOnDisk,其CheckChunks选项由CompactorBlockUploadVerifyChunks(userID)决定;
  • 校验失败会把错误信息写入validation.json并停留在failed状态;校验成功则进入markBlockComplete。

3.3 上传过程中的状态机与状态文件

整个上传过程通过块目录下的两个辅助文件驱动状态机(状态枚举见 block_upload.go):

状态文件说明
uploading-meta.json上传"开始"时由 compactor 写入的元数据快照;正式的meta.json在整块校验通过后才写入
validation.json记录校验心跳时间戳(LastUpdate,毫秒)与错误信息(Error)

由此可推导出块的上传状态:

  • meta.json存在 →complete;
  • 两个文件都不存在 → 块不存在(404);
  • 仅uploading-meta.json→uploading(上传中,允许继续传文件);
  • uploading-meta.json+ 新鲜心跳的validation.json→validating(此时再传文件会收到400 block validation in progress);
  • validation.json心跳超时(stale)→ 回退为uploading,校验可以重新发起;
  • validation.json带Error→failed,且该块不可再重试(再次操作返回400 block validation failed)。

状态查询入口为 GetBlockUploadStateHandler,返回形如{"result": "complete"}或{"result": "failed", "error": "..."}的 JSON。

四、上传操作实战:mimirtool backfill 与 HTTP API

4.1 mimirtool backfill 命令

实际生产中不需要手写 HTTP 请求,直接用mimirtool backfill <block-dir>即可。从 命令定义 可见其主要参数(均可通过对应环境变量提供):

参数默认值说明
block-dir(位置参数,可多个)必填要上传的块所在目录,目录名即块 ULID
--address空(必填,--dry-run除外)Mimir 集群地址
--id空(必填,--dry-run除外)租户 ID,用于X-Scope-OrgID请求头
--user/--key空Basic Auth 用户名/密码(未提供--user时用--id作用户名)
--extra-headers空附加请求头,header=value形式
--tls-ca-path/--tls-cert-path/--tls-key-path/--tls-insecure-skip-verify—mTLS 配置
--sleep-time20s上传完文件后轮询块状态的间隔
--verifyfalse上传前先本地校验块(实验特性,当前默认关闭)
--dry-runfalse只校验不上传,全部通过时退出码 0
--fail-fasttrue校验发现首个失败即中止
--deep-verificationtrue高深度校验,包括逐 chunk 的 CRC32 遍历(较慢)
--single-block-per-dayfalse校验时强制每个 UTC 日最多一个块;否则允许同日多块但不得重叠;无论哪种,块都不允许跨两个 UTC 日
--verify-concurrency0(取min(GOMAXPROCS, 4))并行校验的块数,1 为串行

典型的本地校验用法(不上传,先体检):

mimirtool backfill /path/to/blocks/ --dry-run --no-fail-fast

4.2 客户端与 compactor 的交互流程

BackfillWithOptions 与 backfillBlock 实现了完整的上传编排:

  1. (可选)先运行本地校验器,任何块校验失败则不发出任何上传请求(--dry-run时校验通过即直接返回);
  2. 读取本地块目录中的meta.json,并按目录实际内容重建thanos.files文件列表(GetBlockMeta 会用文件系统的真实大小覆盖/补齐文件清单,且强制要求version == 1);
  3. POST /api/v1/upload/block/{block}/start,请求体为 meta 的 JSON(服务端 StartBlockUpload 限制 meta 请求体最大 1 MiB,然后执行 3.1 节的元数据校验,通过后写入uploading-meta.json);
  4. 对 meta 中除meta.json外的每个文件(index、chunks/000001…),POST /api/v1/upload/block/{block}/files?path={relPath}。服务端 UploadBlockFile 会核对:块处于上传中状态、路径匹配白名单、文件确实在thanos.files中声明、声明大小与实际ContentLength一致,最后直接写入租户桶的{blockID}/{relPath};
  5. POST /api/v1/upload/block/{block}/finish。若命中并发校验上限收到429,客户端按--sleep-time退避重试;
  6. 轮询GET /api/v1/upload/block/{block}/check,直到状态为complete(成功)、failed(携带错误原因,如block validation failed: ...)或上下文取消;
  7. 汇总输出succeeded / already_exists / failed计数;对已存在的块(服务端返回409 Conflict)计入already_exists而不视为失败。

4.3 上传 API 路由一览

上述端点在 API 路由注册处 统一定义:

方法路径用途
POST/api/v1/upload/block/{block}/start提交 meta.json,开始上传
POST/api/v1/upload/block/{block}/files?path=...上传块内单个文件
POST/api/v1/upload/block/{block}/finish结束上传并触发校验
GET/api/v1/upload/block/{block}/check查询上传/校验状态

所有请求都需要租户头(X-Scope-OrgID或等效认证),且该租户必须已启用块上传。

五、TSDB 块上传的已知限制

以下三类限制必须在使用前了解清楚,它们均来自文档与源码可确认的事实。

5.1 Thanos 块无法直接上传

Thanos 块的元数据中带有 Mimir 不支持的外部标签(external labels),因此无法上传。从源码看,CheckMeta 对外部标签执行严格白名单:凡不属于 Mimir 允许集合的标签一律以unsupported external label: %s拒绝,而 Thanos 块普遍携带thanos.ruler等自有标签,因此整块被拒。关于从 Thanos/Prometheus 导入的更多限制与已有绕行方案,可参考仓库文档 Migrating from Thanos or Prometheus to Grafana Mimir。

5.2 上传后需要冲刷结果缓存(results cache)

Mimir 会把早于 10 分钟(可通过-query-frontend.max-cache-freshness配置)的采样区间查询结果缓存在 range query 结果缓存中。上传新块后,同一查询区间的数据已经变化,缓存中的旧结果就会"错误"。应对方式:

  • 由 Mimir 运维人员手动冲刷结果缓存;
  • 或把结果缓存 TTL 从默认 7 天调短,例如用-query-frontend.results-cache-ttl命令行列选项(或按租户覆盖)设为 6 小时,这样最坏情况下 6 小时内查询结果就会基于回填后的数据。

对应的租户级接口(如MaxCacheFreshness、ResultsCacheTTL)定义在 querymiddleware/limits.go,说明这两项参数同样支持 per-tenant override。

5.3 "太新"的块在一段时间内查不到

Querier 收到[start, end]查询区间后,会依据该区间与-querier.query-store-after的关系决定从对象存储、ingester 还是两者同时读取数据。假设-querier.query-store-after为12h:查询[now-13h, now]会读存储,而[now-5h, now]只读 ingester。若用户上传了时间范围落在"新鲜窗口"内的块,querier 按配置只会向 ingester 查询该区间的"实时"数据,刚上传的块在这段时间内不会被查询命中,需要等到查询区间整体滑出query-store-after窗口后才可见。这一分区逻辑可参考 frontend v2 的 QueryStoreAfter 处理。

六、小结

  • 启用方式:全局用-compactor.block-upload-enabled/limits.compactor_block_upload_enabled,多租户场景推荐通过 runtime configuration 按租户覆盖compactor_block_upload_enabled;
  • 上传入口:mimirtool backfill命令,底层走 compactor 的/api/v1/upload/block/{block}/start|files|finish|check四个 API;
  • 校验分层:meta.json 元数据检查(版本、时间、保留期、24h 边界、外部标签、大小上限)→ 整块磁盘校验(可用compactor_block_upload_validation_enabled/compactor_block_upload_verify_chunks按租户调节);
  • 使用前注意三个限制:Thanos 块不可上传(外部标签白名单所致)、上传后要冲刷或缩短 TTL 的结果缓存、以及受-querier.query-store-after影响"太新"的块延迟可见。
  • 时序数据库
  • 后端
  • 数据存储
  • 可观测性
  • 云原生

【免费下载链接】mimir

Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.

项目地址:https://gitcode.com/gh_mirrors/mimir/mimir
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询