Prometheus 数据只放本地,磁盘坏了怎么办?用 Thanos 备份到 S3,这几个坑别踩
Prometheus 跑得好好的,监控图也一直有数据,我最担心的却是它脚下那块磁盘。
本地数据保留 15 天,查最近的故障没问题。可一旦磁盘损坏、机器误删,或者过了保留期才有人来追历史问题,Prometheus 自己已经拿不出那段数据了。把保留时间改成一年?磁盘、扩容和迁移又成了新问题。
这篇聊一个比较常见的做法:让 Thanos Sidecar 把 Prometheus 生成的 TSDB block 上传到 Amazon S3。我会把配置、权限、验收和恢复验证放在一起讲。这里是一套基于官方文档的部署示例,桶名、机器标签都用占位示例;没有实际部署记录的地方,我不会写成“线上已经跑通”。
先说清楚:它上传的不是实时数据
这套链路大致是这样:
Exporter → Prometheus → 本地 TSDB block → Thanos Sidecar → S3
↓
S3 → Thanos Store Gateway → Thanos Query → Grafana 查询历史数据Prometheus 继续负责抓取和保存近期数据,Sidecar 跟它部署在一起。Prometheus 通常每 2 小时生成一个 TSDB block,Sidecar 上传的是已经生成的 block。
这里有个非常容易误解的地方:当前正在写入的 head 数据和 WAL,不会被 Sidecar 实时同步到 S3。 刚装好就去桶里找最近一分钟的数据,找不到是正常的。机器在一个 block 生成前损坏,最近约 2 小时的数据可能还没上传。网络中断时,上传也会延后。
所以我不会把“用了 S3”理解成 Prometheus 从此可以不要持久磁盘。Thanos Sidecar 文档也明确建议保留 Prometheus 的持久存储。S3解决的是历史 block 的长期存放,不是零数据丢失的实时复制。
我会先把 S3 桶和权限收拾好
下面以 AWS 常规区域 ap-southeast-1 为例,假设桶名为 example-thanos-metrics-prod。实际部署时换成自己的桶名和区域。
桶要提前创建,保持阻止公开访问,按团队要求开启服务端加密。我也会考虑开启版本控制或跨区域复制,但不会把它们当成“恢复验证”的替代品。Thanos 后续会压缩、标记和删除旧 block;S3 里有对象,不等于任意时间点都能完整恢复。
权限尽量按组件分开。Sidecar 的工作是上传 block,通常不需要删除整个桶里的数据。给运行它的 IAM 角色限定到目标桶,例如:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::example-thanos-metrics-prod"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::example-thanos-metrics-prod/*"
}
]
}这是示例权限,具体还要结合桶策略和加密方式检查。若使用 SSE-KMS,运行组件的角色还需要相应 KMS 权限。后面要运行 Compactor,它需要处理旧 block,权限范围与 Sidecar 不一样,最好单独授予。
我更倾向于用 EC2 实例角色等临时凭据方式,而不是把 access_key、secret_key 写进配置文件。Thanos 的 S3 配置文档支持使用 AWS 凭据链;如果组件跑在容器里,还得确认容器内部确实能拿到对应角色凭据,不能只在宿主机上测通 AWS CLI 就算完事。
S3 配置文件 /etc/thanos/bucket.yml 可以这样写:
type: S3
config:
bucket: example-thanos-metrics-prod
endpoint: s3.ap-southeast-1.amazonaws.com
region: ap-southeast-1
aws_sdk_auth: true这份文件里没有静态密钥,但仍要控制文件权限,并确认 Sidecar 进程能读取。选定 Thanos 版本后,按该版本文档核对配置字段;生产环境不要不经测试就跟着 latest 升级。
Prometheus 这里有两个参数不能漏
假设 Prometheus 已经在 Linux 主机上运行,数据目录是 /var/lib/prometheus。我会先给它设置稳定且唯一的外部标签:
global:
scrape_interval: 15s
external_labels:
cluster: prod-a
replica: prom-01external_labels 会跟随上传的 block,用来标识数据来自哪套 Prometheus。多实例场景尤其要认真设计:不同实例不能意外使用完全相同的标签组合,同一实例重启后也别随手换标签。标签弄乱了,后续 Compactor 分组和历史查询都会麻烦。
启用 Sidecar 上传时,Prometheus 的启动参数还要包含:
--storage.tsdb.path=/var/lib/prometheus
--storage.tsdb.min-block-duration=2h
--storage.tsdb.max-block-duration=2h
--storage.tsdb.retention.time=15d
--web.enable-admin-api把最小和最大 block 时长都设为 2h,是为了关闭 Prometheus 对这些本地 block 的进一步压缩,让 Sidecar 上传后由 Thanos Compactor 负责对象存储中的压缩。这个要求在 Thanos 官方文档里标得很重,不能只加 Sidecar、漏了 Prometheus 参数。
15d 只是示例本地保留时间,不是 S3 的保留时间。官方建议本地保留时间不要低于最小 block 时长的三倍;以 2 小时 block 计算,至少要给上传故障留出 6 小时缓冲。我不会把本地保留期压到刚好 6 小时,S3 网络或权限出点小问题,就可能来不及补传。
--web.enable-admin-api 也要注意安全边界。Prometheus API 不应因为接 Sidecar 就暴露到公网。在同一台机器部署时,可以让它监听受控地址,并通过防火墙或内部网络限制访问。已有 Grafana、采集或代理依赖 Prometheus 地址的环境,改监听配置前先确认影响。
改完配置,用 promtool check config 检查 YAML,重启 Prometheus 后再查看实际启动参数和运行日志。不要只盯着配置文件:文件写对了,服务没重启,实际运行的还是旧参数。
Sidecar 跑起来,别只看进程在不在
下载与你的系统架构匹配的 Thanos 官方发行版,固定版本,确认 thanos --version 后再启动 Sidecar。示例命令如下:
thanos sidecar \
--tsdb.path=/var/lib/prometheus \
--prometheus.url=http://127.0.0.1:9090 \
--objstore.config-file=/etc/thanos/bucket.yml \
--http-address=127.0.0.1:10902 \
--grpc-address=127.0.0.1:10901这里假设 Sidecar 与 Prometheus 在同一台机器上。--tsdb.path 必须指向同一个 Prometheus 数据目录;服务账号还要有合适的目录权限,Sidecar 需要维护自己的上传状态文件。生产环境用 systemd 或现有编排平台托管,不要靠一个 SSH 窗口长期挂着。
启动后,我会按这个顺序看:
- Sidecar 日志里能否连接 Prometheus、读到外部标签;
- 是否出现 S3 认证、区域、端点或网络错误;
- Prometheus 数据目录里是否已经产生完整的 TSDB block;
- 第一个新 block 形成后,Sidecar 是否记录上传成功;
- S3 桶里是否出现以 block ID 命名的目录及其
meta.json、index、chunks等内容。
也可以用 Thanos 自带工具列出桶中的 block:
thanos tools bucket ls \
--objstore.config-file=/etc/thanos/bucket.yml一台全新启动的 Prometheus,刚过几分钟时没有 block 可上传,先别把“桶还是空的”当故障。反过来,桶里出现一个目录也不代表持续备份正常。我会继续观察下一轮 block,确认上传不是只成功了一次,并监控 Sidecar 最近一次成功上传的时间与错误日志。
还有一种情况容易漏:Prometheus 已经运行很久,目录里有大量过去压缩过的历史 block。Sidecar 默认上传未压缩的 block,旧历史不一定会自动全进 S3。Thanos 提供一次性迁移用的 --shipper.upload-compacted,但它需要在关闭 Prometheus 本地压缩的前提下使用;现有数据量大时,应先评估时间、重复和重叠风险,再按官方流程迁移,不能在生产上随手加个参数碰碰运气。
S3 有数据了,还要安排 Compactor
只让 Sidecar 不断上传,S3 中的小 block 会越来越多。Thanos Compactor 负责压缩、降采样,也负责按策略处理对象存储中的数据保留期。示例启动方式:
thanos compact \
--data-dir=/var/lib/thanos/compact \
--objstore.config-file=/etc/thanos/bucket.yml \
--wait \
--http-address=127.0.0.1:10903Compactor 要有可用的本地工作磁盘,它会下载 block、处理后再上传。更要紧的是:同一条 block 数据流不能同时让多个 Compactor 随意处理。 官方文档要求按单实例原则部署,扩容也要先做好明确的分片规划。
默认情况下,Thanos 对对象存储中的数据不设置过期时间。这是符合“长期留存”目标的,但 S3 账单会持续增长。要设保留期,应评估原始数据、5 分钟降采样数据和 1 小时降采样数据分别保留多久,再用 Compactor 的 --retention.resolution-* 参数管理。
别直接给桶配置一条“对象超过 90 天就删除”的通用 S3 生命周期规则。一个 TSDB block 是多文件组成的,按单个对象年龄删文件,可能留下不完整的 block。涉及删除和保留期时,先按 Thanos 的 block 生命周期设计,再安排独立的备份保护策略。
真正的验收,是从 S3 把历史数据查出来
这是我认为整套方案里最不能省的一步。上传成功是写入验收,历史查询成功才开始证明数据可用。
Thanos Store Gateway 负责读取 S3 中的历史 block,Thanos Query 提供兼容 Prometheus 的查询入口。可以在受控测试环境启动:
thanos store \
--data-dir=/var/lib/thanos/store \
--objstore.config-file=/etc/thanos/bucket.yml \
--grpc-address=127.0.0.1:10911 \
--http-address=127.0.0.1:10912再让 Query 只连接这个 Store Gateway:
thanos query \
--endpoint=127.0.0.1:10911 \
--http-address=127.0.0.1:10904选择一个已上传 block 覆盖的历史时间点,通过 Query 的 Prometheus API 查询一条已知指标,对照原 Prometheus 当时的值。这里只连 Store Gateway,是为了确认结果来自 S3,而不是不小心又从正在运行的 Prometheus 读到了本地数据。
这还不是“整机灾难恢复演练”。Thanos 保存的是指标 block,不会替你备份 Prometheus 配置、抓取目标、告警规则、Grafana 仪表盘,更不会实时保存尚未形成 block 的 WAL。真正要回答“Prometheus 那台机器没了能否恢复业务监控”,还得把配置备份、S3 保护、重建步骤和查询验证一起演练。
写到这里,我对这套方案的判断很简单:Sidecar 负责把历史 block 送上 S3;持久磁盘守住最近一段尚未上传的数据;Compactor 管长期数据;Store Gateway 和 Query 负责证明历史数据读得回来。 少了最后的验证,“备份成功”四个字还是说得太早。
如果你正在给 Prometheus 做长期留存,建议先拿一套非核心实例走通上传、查询和恢复验证,再逐步接入生产。文章对你有帮助,欢迎点赞、转发给正在维护监控平台的同事,也欢迎关注 @运维躬行录。后面我会继续写监控存储、告警和故障排查里那些容易被忽略的细节。
公众号:耕云躬行录
个人博客:躬行笔记
参考文档:Thanos Sidecar · Thanos 对象存储配置 · Thanos Compactor · Thanos Store Gateway · Thanos Query