【VictoriaMetrics的vmbackupmanager】这个一年卖 2 万美元的功能,我做出来了

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


1.背景

在可观测领域的 metrics 解决方案中,VictoriaMetrics 整个产品体系的性能非常高。两年前,我们团队用这个组件来代替 kafka+druid 实现的旧版监控系统。其中一个群集扛住了鹅厂内部一个达到 9000 万/s datapoint 的业务所产生的 metrics 数据,vm 部分用了不到 1000 核,成本相比 kafka+druid 低了很多。

美中不足是 VictoriaMetrics 并未提供类似历史群集的解决方案,虽然提供了免费的 vmbackup 和 vmrestore 工具,但是数据的备份和恢复与历史群集相比仍然是不够的。

历史群集的难题是这样的:

  1. 与历史群集相对的实时群集,默认情况下实时群集仅保存最近 30 天的数据;如果需要读取 30 一天,以及更长周期的数据,就需要把备份数据恢复出来形成历史群集;

  2. 实时群集一般会部署多个 sharding 节点:

    • 如果 vm-insert 上的 -replicationFactor=1,则数据不重复,每个 sharding 上的数据完全重复,每个节点是全量数据的 n 分之一。
    • 如果 vm-insert 上的 -replicationFactor=2, 则各个 sharding 上的数据有一部分与其他 sharding 节点是重复的。
  3. vm-backup 的备份功能是针对 vm-storage 的 sharding 节点的,如果 vm-storage 群集有 n 个节点,则每个节点都需要独立使用 vm-backup 来备份。

    • vm-backup 的原理是先使用 http 协议访问 vm-storage 的 http 服务,访问 /snapshot/create 来创建一个所有磁盘上数据文件的快照。
    • 数据文件的快照是文件系统的 hardlink,意味着只是在 vfs 上增加对数据文件的引用计数,不会产生拷贝。这一步非常快。
    • vm-backup 将数据文件备份到 s3 中;
    • 备份完成后,调用 vm-storage http 中的 /snapshot/delete?snapshot=<id> 来删除快照
  4. 备份完成后:

    • s3 上每个 sharding 对应着一个数据的文件夹

    • 每个数据文件夹的数据包含从创建快照开始,倒数 30 天的数据;(如果默认的数据存储周期是 30 天的话)

    • 如果需要每天都全新备份,则还需要产生以日期命名的文件夹

    • 下面是一个 s3 上备份目录的例子:

          s3://bucket_name:
              metrics_data/
                 daily/
                    2024-01-26/
                       sharding-0/
                       sharding-1/
                       sharding-2/
      
  5. 如果需要直接根据历史的备份数据来启动历史群集,则:

    • 有多少个 sharding, 就要启动多少个 vm-storage 实例;
    • 假设从昨天的备份数据开始启动 vm-storage,则这一组 vm-storage 支持 -1 天到 -31 天的历史数据查询;
    • 当过去了 24 小时,之前的 vm-storage 群集支持的数据变成了 -2 天到 -32 天。如果需要始终从-1 天开始,则需要在每天半夜重新下载最新的备份,然后重启 vm-storage;
    • 如果需要支持更长周期,例如 -31 天到 -61 天,则需要从 -31 天开始的备份数据里,再启动一组 vm-storage 群集。假设需要支持过去 300天的历史数据查询,则需要部署十组 vm-storage.

通过以上的描述,应该可以了解到通过目前的工具,支持历史群集很麻烦。

实际上,我已经通过已有的工具做出来了对应的部署脚本,请看:https://github.com/ahfuzhang/deploy_VictoriaMetrics_cluster/tree/main/terraform/historical_cluster

2.vmfile

从官方的文档(https://docs.victoriametrics.com/vmbackupmanager.html)看,vmbackupmanager 对备份数据有更强大的功能,不过也仍然没有提如何部署历史群集。

收费购买企业版套件,有可能让部署历史群集变得简单。

如果有一个工具,能够从多个 sharding 中导出 metrics 数据,然后再导入到一个包含所有数据的文件夹中,那么部署历史群集就会非常简单了。

因此,我花了两周时间写出了这个可能节约 2 万美元的小工具:vmfile,对离线的 vm-storage 的数据文件进行处理。

vmfile 的相关链接是:

下面介绍一下这个工具的几个基本功能:

2.1 count_index 统计数据文件的索引信息

  • 通过 -storageDataPath 来指向某个 vm-storage 的数据(或者通过 vm-restore 恢复的)文件夹。

    vmfile
    -action=count_index
    -storageDataPath=/Users/fuchunzhang/xxx/data/realtime-cluster/sharding-0/
    -fs.disableMmap=false

2.2 count_data 统计数据文件的数据信息

vmfile \
	  -action=count_data \
	  -storageDataPath=/Users/fuchunzhang/xxx/data/realtime-cluster/sharding-0/ \
	  -fs.disableMmap=false

2.3 export_metrics 导出 metrics 数据到文本文件

  • 通过 output 来指定导出的位置

    vmfile
    -action=export_metrics
    -storageDataPath=/Users/fuchunzhang/xxx/data/realtime-cluster/sharding-0/
    -output=./metrics.txt
    -fs.disableMmap=false

(后续会优化这个功能,例如:可以把所有 metrics 数据的 name, value, timestamp 都导出,然后可以再导入到 clickhouse 做复杂分析)

2.4 simple_merge 简单合并多个 sharding 数据

把多个 sharding 文件夹的数据,把索引和数据两部分分别拷贝到目的文件夹,并最终成为一个可以正常启动 vm-storage 的数据文件夹。

  • simple_merge_from: 多个逗号分隔的路径

  • simple_merge_to: merge 后的目的文件夹

    vmfile
    -action=simple_merge
    -simple_merge_from=/Users/fuchunzhang/xxx/data/2024-01-02/sharding-0/,/Users/fuchunzhang/xxx/data/2024-01-02/sharding-1/,/Users/fuchunzhang/xxx/data/2024-01-02/sharding-2/
    -simple_merge_to=/Users/fuchunzhang/Documents/temp/2024/simple_merge

2.5 merge 重建索引和数据方式的merge

  • mergev2_from: 多个逗号分隔的路径

  • mergev2_to: merge 后的目的文件夹

    vmfile
    -action=merge_v2
    -mergev2_from=/Users/fuchunzhang/xxx/sharding-0/,/Users/fuchunzhang/xxx/sharding-1/,/Users/fuchunzhang/xxx/sharding-2/
    -mergev2_to=/Users/fuchunzhang/Documents/temp/2024/2024-01-22/merge_v2/
    -dedup.minScrapeInterval=0s
    -cpu=1

2.5.1 降采样(downsample)功能

-dedup.minScrapeInterval=0s 的时候,数据部分不会丢失任何数据。

可以使用这个参数指定一个时间窗口,在时间窗口内,同一个 metric 仅保留一个 data point。

下面的例子,每 5 分钟仅保留一个 data point,数据部分的体积会缩小很多:

vmfile \
	  -action=merge_v2 \
	  -mergev2_from=/Users/fuchunzhang/xxx/sharding-0/,/Users/fuchunzhang/xxx/sharding-1/,/Users/fuchunzhang/xxx/sharding-2/ \
	  -mergev2_to=/Users/fuchunzhang/Documents/temp/2024/2024-01-22/merge_v2/ \
	  -dedup.minScrapeInterval=5m \
	  -cpu=1

使用降采样方式的 merge,我的测试数据如下:

  • 降采样时间窗口 0s: 数据部分膨胀 1.1 倍,merge 后记录数不变
  • 降采样时间窗口 1m: 数据部分膨胀 1.005 倍, merge 后记录数减少 0.004%
  • 降采样时间窗口 5m: 数据部分减少 2.919 倍, merge 后记录数减少 79.97%

2.5.2 索引和数据并行merge

-cpu=参数大于 1 时,索引部分和数据部分会并行 merge。目前最多只支持两个核。

vmfile \
	  -action=merge_v2 \
	  -mergev2_from=/Users/fuchunzhang/xxx/sharding-0/,/Users/fuchunzhang/xxx/sharding-1/,/Users/fuchunzhang/xxx/sharding-2/ \
	  -mergev2_to=/Users/fuchunzhang/Documents/temp/2024/2024-01-22/merge_v2/ \
	  -dedup.minScrapeInterval=5m \
	  -cpu=2

3.历史群集部署

有了 vmfile 这个工具后,部署历史群集就简单很多了。

每天只需要把最新的备份数据不断 merge 到一个最终的数据文件就行了。(当然,这里仍然是离线操作的)

每天自动拉取最新的各个 sharding,并开始 merge,然后停掉旧的 vm-storage,启动新的 vm-storage......这一系列的动作我都通过一个 shell 脚本来实现了。

我通过 terraform 来实现了 k8s 上的部署代码,通过阅读源码,修改成别的部署方式也很容易。

历史群集的部署流程如下:

  1. 实时群集需要创建 crontab,每天半夜使用 vmbackup 把各个 sharding 部署到 s3 上;

  2. 构造一个新的 docker 镜像:

  3. 创建一个 k8s 上的 deployment,启动命令是上面提到的 shell

tf 复制代码
        container {
          name              = "${local.vm-storage-name-with-merge}-${count.index}"
          image             = "ahfuzhang/vm-historical:v1.95.1-vmfile"  # 包含了多种工具和 shell 的镜像
          image_pull_policy = "Always" #"IfNotPresent"
          command           = ["/bin/sh"]
          args = [
            "/daily_with_vmfile.sh",  # 从镜像中的 shell 脚本启动
          ]
          ......

部署的代码请看:https://github.com/ahfuzhang/deploy_VictoriaMetrics_cluster/blob/main/terraform/historical_cluster/vm-storage-with-merge.tf

shell 的控制参数通过容器的环境变量来传入,重要的参数有这些:

  • storage_base_path: 本地的存储路径
  • s3_storage_base_path: s3 上的备份文件的路径
  • sharding_count: sharding 的个数
  • n_days_before : 历史群集从几天以前开始,填 1 就是昨天。历史群集会在第二天半夜拉取最新备份,始终维持在当前时间的 n 天以前。
  1. shell 的执行逻辑:

    • 检查是否已经存在 merge 后的文件夹,如果不存在,就使用 vmrestore 来下载多个 sharding
    • sharding 文件夹下载好后,使用 vmfile 的 merge 功能来重建索引和数据。
    • 在 merge 好后的文件夹上启动 vm-storage 来提供历史数据的查询服务;
    • 脚本进入循环睡眠,等到第二天半夜。
    • 根据当前日期,下载 n_days_before 指定的历史文件夹到本地
    • 使用 vmfile merge 数据
    • 启动新的 vm-storage
    • 停掉旧的 vm-storage

    具体代码请看:https://github.com/ahfuzhang/deploy_VictoriaMetrics_cluster/blob/main/terraform/historical_cluster/docker_image/daily_with_vmfile.sh

The End

  • vmfile 的功能还会继续优化,如果你们的团队也在使用 VictoriaMetrics ,欢迎关注我的后续进展;
  • vmfile 的实现代码很简单,但是原理却相对复杂,设计很多存储引擎的结构设计。如果对 vm-storage 的 tsdb 感兴趣,欢迎查看我分享的源码分析电子书:《VictoriaMetrics存储引擎分析.pdf》

Have fun. 希望对你有用。😃