VictoriaMetrics高可用实践

VictoriaMetrics 单机房三机器三副本部署与单机故障扩容恢复 Runbook

1. 结论先说

是的:如果你的故障域是“机器”,并且每台机器有 3 块盘,通常应该是 每台机器启动 1 个 vmstorage,而不是每块盘启动 1 个 vmstorage

推荐形态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
                 Grafana / vmalert / API
                         |
                    vmselect x2
                         |
        +----------------+----------------+
        |                |                |
   vmstorage-a      vmstorage-b      vmstorage-c
     host-a           host-b           host-c
  /data/vmstorage  /data/vmstorage  /data/vmstorage
   3 disks volume   3 disks volume   3 disks volume

 scrape targets / push clients
                         |
                      vmagent
                         |
                    vminsert x2
                         |
             vmstorage-a/b/c, RF=3

每台机器的 3 块盘先在 OS 层做成一个数据卷,例如:

  • RAID0/LVM stripe:容量和性能最好,但任意一块盘坏了就按这台机器故障处理。
  • RAID5/RAIDZ1:能扛本机单盘故障,但写放大和恢复成本更高。
  • 云盘/分布式盘:如果底层盘本身已有高可靠能力,可以直接挂到 -storageDataPath

vmstorage 自己只关心一个 -storageDataPath。多块盘怎么组合,是文件系统/块设备/云盘层的事情。

2. 为什么不是每盘一个 vmstorage

如果一台机器 3 块盘、3 台机器一共 9 块盘,你把它拆成 9 个 vmstorage,再配置:

1
vminsert -replicationFactor=3 -> 9 个 vmstorage

问题是 VictoriaMetrics 的应用层副本是按 vmstorage 节点做的,不知道哪些 vmstorage 在同一台物理机上。官方文档说明:-replicationFactor=N 会把每个样本写到 N 个不同的 vmstorage 节点,并且为了在 N-1 个 storage 不可用时仍维持新写入的 N 副本,集群至少需要 2*N-1vmstorage

所以 RF=3 时,严格意义上至少需要 5 个 vmstorage 才能在故障期继续维持新写入三副本。9 个扁平 vmstorage 看起来满足数量,但不满足“机器故障域对齐”:一台机器挂掉会同时丢 3 个 vmstorage,副本是否跨机器并没有被保证。

因此在你的模型下,更自然的做法是:

  • 3 台机器 = 3 个 vmstorage 节点。
  • 每台机器 3 块盘 = 这个 vmstorage 的本地数据卷。
  • vminsert -replicationFactor=3 = 每条样本写到三台机器各一份。

3. 需要接受的限制

3 台机器、3 个 vmstorage、RF=3 是一个很清晰的三副本模型,但它不是“故障期间仍能维持三副本写入”的模型。

官方的 2*N-1 约束意味着:RF=3 要在 N-1 个 storage 不可用时仍维持新写入三副本,需要至少 5 个 vmstorage。你只有 3 个 vmstorage 时:

  • 正常状态下,每条样本三副本完整。
  • 坏 1 台机器后,历史数据仍可从剩余副本查询。
  • 坏 1 台机器期间,新写入无法同时落到 3 个 vmstorage,因为只剩 2 个可用节点。
  • 默认情况下,vminsert 会把新样本重路由到剩余可用的 vmstorage,所以故障期通常是降级写入;但架构上不能宣称这段时间仍满足三副本。

如果你要求“坏 1 台机器后,新写入也严格三副本”,需要至少 5 台机器或 5 个跨机器故障域,而不是 3 台机器。

4. 正常部署示例

4.1 机器与磁盘规划

机器vmstorage数据路径物理盘
host-avmstorage-a/data/vmstoragedisk1 + disk2 + disk3
host-bvmstorage-b/data/vmstoragedisk1 + disk2 + disk3
host-cvmstorage-c/data/vmstoragedisk1 + disk2 + disk3

/data/vmstorage 是三块盘组合出来的一个挂载点。不要把 3 块盘分别挂成 3 个 VM 数据目录。

4.2 vmstorage

host-a:

1
2
3
4
5
6
vmstorage \
  -storageDataPath=/data/vmstorage \
  -retentionPeriod=180d \
  -httpListenAddr=:8482 \
  -vminsertAddr=:8400 \
  -vmselectAddr=:8401

host-b、host-c 只需要替换主机名和 systemd 单元,不需要改端口。

4.3 vminsert

建议至少部署 2 个 vminsert,无状态,前面挂 LB 或 vmauth

1
2
3
4
5
6
vminsert \
  -httpListenAddr=:8480 \
  -replicationFactor=3 \
  -storageNode=host-a:8400 \
  -storageNode=host-b:8400 \
  -storageNode=host-c:8400

外部 remote write 地址:

1
http://<vminsert-lb>:8480/insert/0/prometheus/api/v1/write

4.4 vmselect

建议至少部署 2 个 vmselect,无状态,前面挂 LB 或 vmauth

1
2
3
4
5
6
7
vmselect \
  -httpListenAddr=:8481 \
  -replicationFactor=3 \
  -dedup.minScrapeInterval=1ms \
  -storageNode=host-a:8401 \
  -storageNode=host-b:8401 \
  -storageNode=host-c:8401

如果你有两个 vmagent 或两套 Prometheus 同时采同一批 target,会产生非完全同 timestamp 的重复样本,那么把 -dedup.minScrapeInterval 设置为实际 scrape_interval,例如 15s

4.5 vmagent

这种情况下,vmagent 只需要写一个 VictoriaMetrics Cluster 入口,不需要配置 3 个 -remoteWrite.url。官方也说明,VictoriaMetrics Cluster 自身支持复制,写入同一个集群时不需要给同一集群配置多个 remote write URL。

1
2
3
4
5
vmagent \
  -promscrape.config=/etc/vmagent/prometheus.yml \
  -remoteWrite.url=http://<vminsert-lb>:8480/insert/0/prometheus/api/v1/write \
  -remoteWrite.tmpDataPath=/data/vmagent-queue \
  -remoteWrite.maxDiskUsagePerURL=500Gi

vmagent 队列仍然很重要:如果 vminsert 入口临时不可用,数据可以先落到 -remoteWrite.tmpDataPath,恢复后补发。

5. 备份策略

5.1 备份对象

集群模式下,vmbackup 需要在每个 vmstorage 节点上分别运行。这个架构只有 3 个 vmstorage,所以是 3 份备份任务,不是 9 份。

备份目录必须按节点分开:

1
2
3
s3://vm-backup/prod/vmstorage-a/latest
s3://vm-backup/prod/vmstorage-b/latest
s3://vm-backup/prod/vmstorage-c/latest

5.2 每小时增量备份

host-a:

1
2
3
4
vmbackup \
  -storageDataPath=/data/vmstorage \
  -snapshot.createURL=http://host-a:8482/snapshot/create \
  -dst=s3://vm-backup/prod/vmstorage-a/latest

host-b:

1
2
3
4
vmbackup \
  -storageDataPath=/data/vmstorage \
  -snapshot.createURL=http://host-b:8482/snapshot/create \
  -dst=s3://vm-backup/prod/vmstorage-b/latest

host-c:

1
2
3
4
vmbackup \
  -storageDataPath=/data/vmstorage \
  -snapshot.createURL=http://host-c:8482/snapshot/create \
  -dst=s3://vm-backup/prod/vmstorage-c/latest

5.3 每日保留点

可以每天从 latest 做服务端 copy,形成日期目录:

1
2
3
vmbackup \
  -origin=s3://vm-backup/prod/vmstorage-b/latest \
  -dst=s3://vm-backup/prod/vmstorage-b/20260706

三个节点都做。注意每日 copy 与小时增量不要同时跑。

5.4 备份巡检

每天检查:

  • 3 个 vmbackup 任务都成功。
  • 每个备份目录都有最近更新时间。
  • 备份耗时没有异常变长。
  • 备份失败后用相同参数重跑,vmbackup 支持从中断点继续。
  • 定期抽样 vmrestore 演练。

6. 单机永久故障后的扩容恢复流程

以下假设 host-b 永久故障,需要新增 host-d 接替它。

6.1 事故确认

确认:

  • host-b:8400host-b:8401host-b:8482 不可达。
  • vminsert 日志出现写入 host-b 失败或不可达。
  • vmselect 查询仍可用,但处于少一个副本的降级状态。
  • host-ahost-c 的 CPU、内存、磁盘 IO、磁盘空间正常。

此时不要把 host-bvminsert/vmselect 配置里随手删除后就不管。你需要尽快恢复第三个 vmstorage,否则故障窗口内的新数据副本数不足。

6.2 新机器准备

新增 host-d

  • 规格不低于 host-b
  • 3 块盘按原方案组合成 /data/vmstorage
  • 文件系统、mount 参数、目录权限保持一致。
  • 安装同版本 VictoriaMetrics。
  • 配置 systemd、ulimit、监控采集、日志、对象存储凭证。

6.3 停止接收故障节点写入

恢复前先保证 host-dvmstorage 不在运行。

如果你有 LB/DNS:

  • 暂时不要把 host-b 的名字指向 host-d 并立即接流量。
  • 先完成 vmrestore,再切流量。

如果配置写死主机名:

  • 准备好 vminsertvmselect 的新配置,把 host-b 替换为 host-d,但先不要重启。

6.4 从备份恢复 vmstorage-b

在 host-d 上恢复原 vmstorage-b 的最近备份:

1
2
3
vmrestore \
  -src=s3://vm-backup/prod/vmstorage-b/latest \
  -storageDataPath=/data/vmstorage

注意:

  • vmrestore 需要在对应 VictoriaMetrics 进程停止时运行。
  • 目标 -storageDataPath 要确认无误。
  • latest 只能恢复到最后一次成功备份的时间点。
  • 备份点之后、host-b 故障之前的数据,理论上还在 host-a 和 host-c 上。
  • host-b 故障之后、host-d 加入之前的新数据,第三副本需要后续补齐;VictoriaMetrics 不会自动把已有历史数据从其他 vmstorage 后台重平衡到新节点。

6.5 启动新 vmstorage

启动 host-d:

1
systemctl start vmstorage

检查:

1
curl http://host-d:8482/metrics

6.6 切换 vminsert/vmselect

把所有 vminsert 配置中的 host-b:8400 替换为 host-d:8400,并滚动重启:

1
2
3
4
5
6
vminsert \
  -httpListenAddr=:8480 \
  -replicationFactor=3 \
  -storageNode=host-a:8400 \
  -storageNode=host-d:8400 \
  -storageNode=host-c:8400

把所有 vmselect 配置中的 host-b:8401 替换为 host-d:8401,并滚动重启:

1
2
3
4
5
6
7
vmselect \
  -httpListenAddr=:8481 \
  -replicationFactor=3 \
  -dedup.minScrapeInterval=1ms \
  -storageNode=host-a:8401 \
  -storageNode=host-d:8401 \
  -storageNode=host-c:8401

如果使用稳定 DNS,例如 vmstorage-b.example,则可以把 DNS 指向 host-d,配置无需改。

6.7 恢复故障窗口的第三副本

这里要分清两类数据:

  1. vmrestore 之前已经在备份里的数据:host-d 已恢复出一份。
  2. 备份之后缺在 host-d 上的数据:需要额外补齐,特别是 host-b 故障期间的新写入。

VictoriaMetrics Cluster 不会因为新节点加入而自动把历史数据重平衡到这个节点。因此如果你要求恢复严格三副本,需要从健康节点导出缺口时间段,再导入到新节点所在的集群入口。

可选流程:

  1. 确认缺口时间范围:
1
2
缺口开始 = vmstorage-b 最近一次成功备份时间
缺口结束 = host-d 正式加入并开始接收写入时间
  1. 从正常查询入口导出 native 数据:
1
2
curl "http://<vmselect-lb>:8481/select/0/prometheus/api/v1/export/native?match[]={__name__!=\"\"}&start=<start>&end=<end>" \
  -o vm-gap.native
  1. 导入回集群入口:
1
2
curl -X POST "http://<vminsert-lb>:8480/insert/0/prometheus/api/v1/import/native" \
  --data-binary @vm-gap.native

注意:这一步会让 vminsert 按当前 RF=3 写到 host-a、host-d、host-c。因为 host-a 和 host-c 已经有这段数据,查询侧必须依赖 dedup 去重。该流程 IO 和网络成本很高,建议按 tenant、核心指标或较短时间窗口分批执行。

如果你能接受“故障窗口内只有 2 副本”,可以不做回灌;但要在故障复盘里明确记录这个风险窗口。

6.8 恢复后的立即备份

host-d 恢复并补齐后,立即刷新它的备份:

1
2
3
4
vmbackup \
  -storageDataPath=/data/vmstorage \
  -snapshot.createURL=http://host-d:8482/snapshot/create \
  -dst=s3://vm-backup/prod/vmstorage-b/latest

然后恢复正常的周期性备份。

7. 验收检查

恢复完成后检查:

  • 所有 vminsert 都能连通 host-a:8400host-d:8400host-c:8400
  • 所有 vmselect 都能连通 host-a:8401host-d:8401host-c:8401
  • 查询最近 1 小时、24 小时、7 天核心指标正常。
  • isPartial 不再异常出现。
  • vmagent_remotewrite_pending_data_bytes 下降到正常水位。
  • vmagent_remotewrite_packets_dropped_total 没有增长。
  • 新 host 的 vmbackup 成功。

8. 监控与告警

必须告警:

  • 任意 vmstorage 不可达:vm_rpc_vmstorage_is_reachable == 0
  • 任意 vmstorage 只读:vm_storage_is_read_only == 1
  • vmagent remote write 积压持续增长:vmagent_remotewrite_pending_data_bytes
  • vmagent 丢弃数据:rate(vmagent_remotewrite_packets_dropped_total[5m]) > 0
  • vmbackup 失败或超时。
  • vmstorage 磁盘剩余空间低于 20%。
  • 查询响应出现非预期 partial。

建议面板:

  • 每个 vmstorage 的写入速率、查询速率、CPU、内存、IO wait、磁盘延迟。
  • 每台机器 /data/vmstorage 使用率。
  • vminsert 到各 storage node 的错误率。
  • vmagent pending bytes 和 dropped packets。
  • 每个 vmbackup 最近成功时间和耗时。

9. 什么时候才需要每盘一个 vmstorage

只有在你明确想让“单块盘”成为独立调度/扩容/故障单元时,才考虑每盘一个 vmstorage。例如:

  • 机器很多,单盘很多,想把每块盘都作为独立 storage shard。
  • 能接受单台机器故障会同时损失多个 vmstorage,并且副本分布另有机制保证跨机器。
  • 有足够节点数满足 RF 的 2*N-1 约束,并能把同一副本的节点分散到不同机器故障域。

在你当前“三台机器、每台三盘、按机器故障扩容恢复”的需求里,每盘一个 vmstorage 反而会增加复杂度并制造误解。

10. 最终建议

你的场景推荐:

  • host-a/b/c 各启动 1 个 vmstorage
  • 每台机器 3 块盘合成一个 /data/vmstorage
  • vminsert 配 3 个 storage node,并启用 -replicationFactor=3
  • vmselect 配 3 个 storage node,并启用 -replicationFactor=3 和 dedup。
  • vmbackup 只需要 3 份,分别对应 3 个 vmstorage
  • 单机永久故障时,新增一台机器,恢复故障节点的那一份 vmbackup,替换 vminsert/vmselect 中的 storage node;如需严格补齐故障窗口第三副本,再做 export/import 回灌。
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计