NAS 风扇狂转,查出 Thanos Compactor 把 bucket 撑到了 241G
TL;DR:一次例行升级重启了 Thanos Compactor,它开始疯狂追赶积压了一年多的 block,把家里 NAS 的磁盘 I/O 打满、风扇狂转。排查发现 compactor 早已静默停摆、bucket 膨胀到 241G,最后用
thanos tools bucket retention把过期数据一刀清到 18G。复盘才发现:它压根不是”某天停了”,而是从上线起就因为 PVC 太小反复 halt,retention 一次都没跑完过。补上告警防止再次发生。
风扇为什么在狂转
最近给家里的 Kubernetes 集群做一次例行的 pod 滚动升级,几个 pod 陆续更新。没过多久,就摆在我旁边、客厅里的绿联 NAS 风扇开始呼呼狂转。
进入 NAS 后台网页查看,网络和磁盘 I/O 已经全顶满了。NAS 上跑着一个 MinIO,是集群里 Thanos 的对象存储——磁盘被打满,说明集群里有什么东西正在疯狂读写它。
打开 Lens 按 CPU 排序,占用最高的是 Thanos Compactor。翻它的日志,确认就是它在驱动 NAS 的磁盘读写:
downloading blocks (5.2s)compacting blocks (3.1s)uploaded block (4.8s)downloading blocks (6.9s)compacting blocks (3.6s)uploaded block (4.9s)... 循环 47 次,12 分钟内完成compactor 的工作是定期把 Prometheus 写入的 2 小时小数据块合并、降采样、清理。正常情况下应该是一个持续的、分散在日常的后台进程,不应该在某个瞬间集中爆发。肯定有东西坏了。
插播:Thanos 是什么
在继续之前,先花一分钟搞清楚 Thanos 的几个组件。如果你已经很熟了,可以跳过。
Thanos 是 Prometheus 的长期存储和高可用扩展层——把指标长期堆到廉价的对象存储上,正是我引入它的原因。几个组件的关系是这样的:

主要组件:
- Sidecar:挂在 Prometheus 旁边,把 TSDB block 上传到对象存储(S3/MinIO)
- Store Gateway:从对象存储读历史数据,暴露给 Querier 查询
- Compactor:把对象存储里的 2h block 压缩合并,同时做降采样(5m / 1h 精度),并按保留策略删除过期数据
- Querier / Query Frontend:聚合 Sidecar 的实时数据 + Store Gateway 的历史数据,给 Grafana 提供统一查询入口
简单说:Compactor 是管数据整理和清理的。如果它停了,数据只增不减,但查询不受影响(因为 Store Gateway 可以直接读未压缩的 block),这也是为什么它停了好久我都没发现。
第一层:Thanos Compactor 积压了多久?
先看日志时间线。最早被压缩的 block 能追溯到 2025 年 7 月——算下来积压了一年多。
不用翻日志也能验证:直接在 NAS 上打开 MinIO 的 thanos bucket,最早的那批 block 同样停在 2025 年 7 月。两边对得上,说明不是日志滚动造成的错觉——这些数据是真的从那时候起就没被清理过。
compactor 暴露了 Prometheus 指标在 :10902/metrics:
thanos_compact_todo_downsample_blocks 179thanos_compact_halted 0179 个 block 等待降采样处理。halted 是 0,说明它此刻在正常跑;但结合这么大的积压,只能推断它此前长期没有真正推进过——具体原因留到复盘(这也是后面要补告警的动机)。
compactor 什么时候停的?当时我答不上来。因为没有告警。(后面复盘会发现,答案比“某天停了”更尴尬。)
这个指标一直是暴露的,Prometheus 一直在抓,但没有人配一条告警规则说 “compactor 挂了请通知我”。compactor 停掉不会立刻影响任何业务——Grafana 照常查询,所有应用指标正常采集——它是一个静默失败的服务。
第二层:追赶积压时 compactor 的 PVC 被挤爆
重启后的 compactor 在追赶积压了一年多的 block。它并行下载 7 个旧 block 到本地 PVC,合并之后再把结果传回去——开头那阵风扇狂转,就是这个”下载、压缩、上传”的循环在把 NAS 的网络和磁盘打满。
而在本地这边,峰值磁盘占用超过了 PVC 容量:
preallocate: no space left on devicecritical error detected; halting挂了。10Gi 的 PVC 不够用。
两个措施:
- 扩容 PVC 到 20Gi
- 把
compact.concurrency从 4 降到 2,减少同时并行的压缩组,从而压低峰值磁盘占用(它和上面”并行拉取几个 block”不是一个概念)
重启 pod,compactor 重新开始追赶。
第三层:bucket 里积压了多少数据?
compactor 处理了一会儿之后我心里冒出一个问题:这一年多的积压,bucket 里到底有多少数据?
去 NAS 上 du -sh 看了一下:
thanos-dev/ 241G241G。对于一个保留策略是 14d / 30d / 60d 的 Prometheus 集群,正常应该在 20G 以内。
这时候我问了下 AI,得到的建议是”让 compactor 自己处理就行,它会慢慢把积压追上来”。听着挺有道理,但有个显而易见的逻辑问题:我最长的保留期就是 60 天,一年前的数据不管怎么压缩、降采样,追上来之后照样是要删的。
再看一眼 compactor 此刻在干的事:
- 从 MinIO 下载 2025 年的旧 block
- 在本地做 compaction 合并
- 上传合并结果回 MinIO
- 然后标记旧 block 删除(因为超过 60d 保留期)
下载、压缩、上传,最后再删掉。 每一步都是 NAS 上的读写 I/O,而最终结果跟直接删除一模一样。compactor 在浪费生命。
一刀切:用 thanos tools bucket retention 清掉过期数据
Thanos 有个内置命令 tools bucket retention,可以直接按保留策略扫描 bucket,标记所有过期 block 为待删除:
# 先停掉 compactor,避免冲突kubectl scale deploy thanos-compactor -n observability --replicas=0
# 按保留策略标记过期 block,--delete-delay=0s 立即生效thanos tools bucket retention \ --objstore.config-file=/tmp/objstore.yml \ --retention.resolution-raw=14d \ --retention.resolution-5m=30d \ --retention.resolution-1h=60d \ --delete-delay=0s
# 物理删除thanos tools bucket cleanup \ --objstore.config-file=/tmp/objstore.yml \ --delete-delay=0s
# 恢复 compactorkubectl scale deploy thanos-compactor -n observability --replicas=1
objstore.yml就是 compactor 平时用的那份对象存储配置(指向同一个 bucket),直接复用即可。运行前务必确认 compactor 已经停了,两边同时写会冲突。
几分钟后,再次 du -sh:
thanos-dev/ 18G241G → 18G。 风扇终于安静了。
复盘:根因是 PVC 太小,撑爆后不会重试
10Gi 的 PVC 太小。compactor 一重启就去追赶积压,并发下载直接撑爆本地磁盘,no space left on device 之后它就 halt 在那儿——halt 了不会自己重试,一直等到下次 pod 重启,再重演同一场失败:
pod 重启 → 追赶积压 → 撑爆本地磁盘 → no space left on device → halted → 一直 halted,直到下次 pod 重启 → 回到第一步而这一年里 thanos_compact_halted 一直亮着 1,没人看过它一眼。
让沉默的不再沉默:给 compactor 补上告警
清理数据是治标,加告警才是治本。配了一条 PrometheusRule:
- alert: ThanosCompactorHalted expr: thanos_compact_halted == 1 for: 10m labels: severity: critical annotations: summary: "Thanos compactor is halted"
- alert: ThanosCompactorDown expr: absent(thanos_compact_halted) for: 15m labels: severity: critical annotations: summary: "Thanos compactor is down"两个告警都是 critical 级别,路由到 Pushover,手机收通知。早点配的话,这也就一行配置——指标一直在那,没人看而已。
如果你也在跑 Thanos:三步自查
留一份可以照抄的自查清单,怕你的 compactor 也在悄悄积压:
- 看指标:抓
thanos_compact_halted(是否 == 1)和thanos_compact_todo_compaction_blocks/thanos_compact_todo_downsample_blocks(还有多少 block 待处理),确认 compactor 最近确实在推进、没有卡住。 - 看 bucket 大小:在对象存储上
du -sh(或 MinIO 控制台)看实际占用,对照你的保留策略估个正常值——差一个数量级就要警惕。类似的”磁盘莫名被占满”排查思路,我在树莓派磁盘占满那篇也写过。 - 加告警:把上面那两条 PrometheusRule 抄进去,让它在 halted 或 down 时主动喊你,而不是等风扇替你报警。
总结:241G → 18G
| 度量 | 修复前 | 修复后 |
|---|---|---|
| bucket 大小 | 241G | 18G |
| compactor PVC | 10Gi | 20Gi |
| 并发数 | 4 | 2 |
| 告警规则 | 0 | 2 |
| NAS 风扇 | 狂转 | 安静 |
几点教训:
- compactor 静默失败的杀伤力——不影响查询,所以很难被发现。但数据不断堆积,某天重启后集中爆发。对这类服务要主动监控,不能”等用户报告”。
- “没炸”不等于”在工作”——复盘下来,它不是某天停的,而是从上线起就没成功清理过一次。pod 一直是 Running,日志里也一直有动静,唯一说真话的是那个没人看的
thanos_compact_halted。给这类后台服务定验收标准时,要盯它推进了多少,而不是它还活着没有。 - Prometheus 指标已经在等你了——
thanos_compact_halted暴露了很久,少一条告警规则的区别就是 241G 的磁盘占用。 - 过期数据别让 compactor 硬算——如果知道大量数据已经超出保留期,直接用
bucket retention一刀切,省掉把这些注定要删的数据来回下载、压缩、上传的整套 I/O。