作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
1.背景
在可观测领域的 metrics 解决方案中,VictoriaMetrics 整个产品体系的性能非常高。两年前,我们团队用这个组件来代替 kafka+druid 实现的旧版监控系统。其中一个群集扛住了鹅厂内部一个达到 9000 万/s datapoint 的业务所产生的 metrics 数据,vm 部分用了不到 1000 核,成本相比 kafka+druid 低了很多。
美中不足是 VictoriaMetrics 并未提供类似历史群集的解决方案,虽然提供了免费的 vmbackup 和 vmrestore 工具,但是数据的备份和恢复与历史群集相比仍然是不够的。
历史群集的难题是这样的:
-
与历史群集相对的实时群集,默认情况下实时群集仅保存最近 30 天的数据;如果需要读取 30 一天,以及更长周期的数据,就需要把备份数据恢复出来形成历史群集;
-
实时群集一般会部署多个 sharding 节点:
- 如果 vm-insert 上的
-replicationFactor=1
,则数据不重复,每个 sharding 上的数据完全重复,每个节点是全量数据的 n 分之一。 - 如果 vm-insert 上的
-replicationFactor=2
, 则各个 sharding 上的数据有一部分与其他 sharding 节点是重复的。
- 如果 vm-insert 上的
-
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>
来删除快照
- vm-backup 的原理是先使用 http 协议访问 vm-storage 的 http 服务,访问
-
备份完成后:
-
s3 上每个 sharding 对应着一个数据的文件夹
-
每个数据文件夹的数据包含从创建快照开始,倒数 30 天的数据;(如果默认的数据存储周期是 30 天的话)
-
如果需要每天都全新备份,则还需要产生以日期命名的文件夹
-
下面是一个 s3 上备份目录的例子:
s3://bucket_name: metrics_data/ daily/ 2024-01-26/ sharding-0/ sharding-1/ sharding-2/
-
-
如果需要直接根据历史的备份数据来启动历史群集,则:
- 有多少个 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 的相关链接是:
- 源码(目前还很混乱,整理中):https://github.com/ahfuzhang/VictoriaMetrics_cluster_v1.96.0/tree/cluster_dev/app/vmfile
- 版本 v0.0.3: https://github.com/ahfuzhang/VictoriaMetrics_cluster_v1.96.0/releases/tag/vmfile_v0.0.3
- docker 镜像:https://hub.docker.com/layers/ahfuzhang/vm-historical/v1.95.1-vmfile/images/sha256-ef2c2f68c2f0fcf6a297cdf0774e4c0e90fbed914505c00f6a5529aa291cdf4b?context=repo
- 使用 vmfile 来部署历史群集的代码:https://github.com/ahfuzhang/deploy_VictoriaMetrics_cluster/blob/v0.0.3/terraform/historical_cluster/vm-storage-with-merge.tf
下面介绍一下这个工具的几个基本功能:
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 上的部署代码,通过阅读源码,修改成别的部署方式也很容易。
历史群集的部署流程如下:
-
实时群集需要创建 crontab,每天半夜使用 vmbackup 把各个 sharding 部署到 s3 上;
-
构造一个新的 docker 镜像:
- 镜像内包含了 vm-restore 和 vm-storage 两个官方程序
- 镜像内包含了编译好的 vmfile 工具
- 镜像内提供一个 shell 脚本来把上面的工具串起来
- 源码请看:https://github.com/ahfuzhang/deploy_VictoriaMetrics_cluster/tree/main/terraform/historical_cluster/docker_image
- 镜像已经发布到 docker hub: https://hub.docker.com/layers/ahfuzhang/vm-historical/v1.95.1-vmfile/images/sha256-ef2c2f68c2f0fcf6a297cdf0774e4c0e90fbed914505c00f6a5529aa291cdf4b?context=repo
-
创建一个 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 脚本启动
]
......
shell 的控制参数通过容器的环境变量来传入,重要的参数有这些:
- storage_base_path: 本地的存储路径
- s3_storage_base_path: s3 上的备份文件的路径
- sharding_count: sharding 的个数
- n_days_before : 历史群集从几天以前开始,填 1 就是昨天。历史群集会在第二天半夜拉取最新备份,始终维持在当前时间的 n 天以前。
-
shell 的执行逻辑:
- 检查是否已经存在 merge 后的文件夹,如果不存在,就使用 vmrestore 来下载多个 sharding
- sharding 文件夹下载好后,使用 vmfile 的 merge 功能来重建索引和数据。
- 在 merge 好后的文件夹上启动 vm-storage 来提供历史数据的查询服务;
- 脚本进入循环睡眠,等到第二天半夜。
- 根据当前日期,下载 n_days_before 指定的历史文件夹到本地
- 使用 vmfile merge 数据
- 启动新的 vm-storage
- 停掉旧的 vm-storage
The End
- vmfile 的功能还会继续优化,如果你们的团队也在使用 VictoriaMetrics ,欢迎关注我的后续进展;
- vmfile 的实现代码很简单,但是原理却相对复杂,设计很多存储引擎的结构设计。如果对 vm-storage 的 tsdb 感兴趣,欢迎查看我分享的源码分析电子书:《VictoriaMetrics存储引擎分析.pdf》
Have fun. 希望对你有用。😃