1. 内容整体设计与思路拆解
先说结论:这套组合是“单机可观测性基础设施”的黄金搭档。VictoriaMetrics 是时序数据库,负责存储监控指标;Prometheus 负责采集指标,两者通过 remote write 协议对接;备份恢复解决的是数据安全问题。三件事串起来,就是一个完整的监控数据闭环。
我最初接触这套方案时,也是从直接裸装 Prometheus + 本地 TSDB 开始的。跑了一段时间,发现两个痛点非常明显:一是 Prometheus 自身存储是单机文件,数据量上来后查询变慢,而且没法横向扩展;二是备份极其麻烦,Prometheus 原生的 snapshot API 只能备份当前内存中的时序块,配合对象存储上传又是一堆脚本要写。后来调研了 VictoriaMetrics,它的 remote write 接收端、按天分片存储、内置备份工具这些设计,几乎是冲着解决这两个痛点来的。
1.1 为什么用 docker-compose 而不是直接二进制部署
很多人问我,既然 serviced 这么轻量,为什么不直接下载二进制跑 systemd 服务?我的回答是:看场景。
如果你只有一台机器,要跑 vmselect、vmstorage、vminsert 三个组件,再加上 Prometheus、Grafana,至少五个进程。用二进制部署,你得手动维护 systemd unit 文件、环境变量、升级路径,一旦哪次升级改动了命令行参数,排查起来很痛苦。用 docker-compose,所有服务的启动参数、镜像版本、网络配置都写在一个 YAML 文件里,git 管理起来很舒服,换机器的时候复制过去docker-compose up -d就能拉起一套一模一样的环境。
另外 docker-compose 对“模拟生产”也有帮助。VictoriaMetrics 官方虽然提供单机版(victoria-metrics)和集群版(vmcluster),但如果只是公司内部监控几百台服务器,单机版其实够用。单机版本身就是三个组件的合集,用 docker-compose 跑也是同样的效果,只是进程数少一些。后续如果真想拆集群,再调整 compose 文件映射出 vmselect、vmstorage、vminsert 三个独立服务就行,迁移成本极低。
1.2 这套方案的适用场景和边界
我实测下来,这套方案最适合以下场景:
- 中小团队内部基础设施监控,机器规模在 50~500 台之间。
- 已有 Prometheus 生态(exporter、alertmanager、grafana)但存储不想自己维护。
- 对数据备份有要求,希望至少能“按天还原”到任意时间点。
- 不想引入太重型的分布式时序方案(如 Thanos、M3DB、InfluxDB 集群),也不想付费买云厂商托管。
边界也讲清楚:如果你的指标量达到每秒百万级 samples,或者单查要跨数月聚合且要求毫秒级响应,单机 VictoriaMetrics 就有些吃力。这时应该考虑它的集群版,或者干脆上 managed 服务。但单机版在几十万 samples/s 的规模下,体验非常流畅,查询速度比原生 Prometheus 快不少,尤其在大范围时间范围查询时,VictoriaMetrics 的索引设计优势很明显。
2. 核心细节解析与实操要点
2.1 docker compose 文件设计:每个服务为什么这么配
直接贴一份我实际使用的 docker-compose.yml,基于 VictoriaMetrics 官方镜像,版本锁定,避免“过两天镜像 tag 漂移导致行为不一致”的坑。
version: '3.8' services: victoria-metrics: image: victoriametrics/victoria-metrics:v1.93.5 container_name: vm-single restart: unless-stopped ports: - "8428:8428" - "8429:8429" command: - "-storageDataPath=/vmdata" - "-retentionPeriod=3" - "-search.maxUniqueTimeseries=1000000" - "-search.maxQueryDuration=30s" - "-httpListenAddr=:8428" - "-influxListenAddr=:8429" volumes: - ./data/victoria-metrics:/vmdata ulimits: nofile: soft: 65536 hard: 65536 prometheus: image: prom/prometheus:v2.47.2 container_name: prometheus restart: unless-stopped ports: - "9090:9090" volumes: - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./data/prometheus:/prometheus command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--storage.tsdb.retention.time=7d" depends_on: - victoria-metrics grafana: image: grafana/grafana:10.1.2 container_name: grafana restart: unless-stopped ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin123 - GF_USERS_ALLOW_SIGN_UP=false volumes: - ./data/grafana:/var/lib/grafana depends_on: - victoria-metrics几个容易被忽略的点:
-retentionPeriod=3单位是天,也可以配置3表示三个月?不对,官方文档明确是“保留天数”。例如-retentionPeriod=3就是保留 3 天。我这边配置的是 3 天?不,我实际配置是 90。谨慎起见,写配置时把这个参数理解为“保留最近 N 天”,N 的取值根据你的磁盘和需求来。如果监控数据要留半年,就写 180。设置多少其实取决于业务需要,而不是拍脑袋。-influxListenAddr=:8429,这个是为了兼容 InfluxDB line protocol。如果你的 Telegraf 或其它采集器直接写 InfluxDB 格式,可以免掉 Prometheus 直接写入 VictoriaMetrics。但我们的主链路还是 Prometheus remote write,所以这个端口更像一个扩展接口,开着不亏。ulimits的 nofile 调到 65536,是因为时序数据库高并发写入时会打开大量文件描述符。容器默认 1024 很容易满,满的时候 VictoriaMetrics 会报 “too many open files”,而且这种报错通常不是第一时间出现在日志里,而是表现为写入超时、查询变慢,排查起来相当迷惑。
2.2 Prometheus 配置:remote write 参数调优
接下来是 Prometheus 的 prometheus.yml。核心部分就是 remote_write 配置:
global: scrape_interval: 15s evaluation_interval: 15s remote_write: - url: "http://victoria-metrics:8428/api/v1/write" queue_config: max_shards: 8 capacity: 2000 max_samples_per_send: 2000 batch_send_deadline: 5s min_backoff: 1s max_backoff: 5s write_relabel_configs: - source_labels: [__name__] regex: "go_.*" action: drop scrape_configs: - job_name: "node" static_configs: - targets: ["node-exporter:9100"]关于 queue_config,网上很多人直接抄默认值,但这几个参数决定了写入吞吐和稳定性。
max_shards:并发分片数,默认 4。如果你的机器 CPU 核数多,且 Prometheus 采集目标数超过 500,可以调到 8 或 16。但不要盲目调大,每个分片都会占用内存和连接。我这边 8 个分片,VictoriaMetrics 接收端 CPU 占用大约 25% 左右,稳定。capacity和max_samples_per_send:控制每个分片内存队列大小和单批发送样本数。capacity 默认 2500,max_samples_per_send 默认 500。在高指标量下,这两个值偏保守,容易造成队列积压和背压。我调成 2000 / 2000 之后,写延迟明显降低。min_backoff和max_backoff:写入失败后的退避时间。默认是 30ms / 5s,我调到 1s / 5s,避免瞬时抖动时频繁重试打爆接收端。
write_relabel_configs这里我做了一个丢弃动作:所有go_开头的指标直接不写入 VictoriaMetrics。因为 Prometheus 自身暴露的 Go 运行时指标(go_goroutines、go_memstats_alloc_bytes 等)对业务监控没什么用,但量很大。每个 Prometheus 实例每秒会产生几千条这样的样本,存到时序库纯属浪费磁盘。同样思路可以扩展到丢弃prometheus_*内置指标,不过 prometheus_ 系列有些对排查 Prometheus 自身运行状态有用,我保留着。
2.3 VictoriaMetrics 备份原理:为什么比原生 Prometheus 简单
备份的核心对象是-storageDataPath目录下的数据。VictoriaMetrics 单机版数据目录结构如下:
/vmdata/ ├── cache/ ├── indexdb/ ├── metadata.json └── snapshots/ # 由 API 创建,存放备份快照VictoriaMetrics 提供了/-/snapshot/create和/-/snapshot/delete两个 API,用于创建一致性快照。原理是利用 Linux 的rename机制:把当前正在写入的数据文件做硬链接到 snapshots 目录,然后你在备份时读取的是那个时刻的文件状态,而不是正在写入的活跃文件。这样不需要停服务,就能拿到一套一致的备份源。
相比之下,Prometheus 的 snapshot API 虽然也类似,但它只能对当前 WAL 之前已经刷盘的 block 做快照,WAL 里最近的数据点不一定包含在内,备份出来的数据可能丢最近几分钟。VictoriaMetrics 的快照创建得更加干净,创建之后你可以直接打包快照目录。
备份流程整体是:
- 调用
curl -XPOST http://localhost:8428/-/snapshot/create,返回snapshotName。 - 进入
/vmdata/snapshots/<snapshotName>,把里面所有文件打成一个压缩包。 - 把压缩包传到远端对象存储或另一台机器。
- 调用
curl -XDELETE http://localhost:8428/-/snapshot/delete,删除快照。 - 定期轮询执行上述流程,可以用 cron 或自己写个脚本。
2.4 恢复流程设计:从备份目录到在线查询
恢复更简单,但有一个大坑:不要直接把备份文件原样覆盖到一个正在运行的 VictoriaMetrics 实例数据目录。因为 VictoriaMetrics 在启动时会锁住 storageDataPath,而且打开的文件句柄和一些内部缓存状态是基于当前目录的,直接覆盖会导致数据不一致,最典型的表现是启动后查询完全没数据,或者崩溃。
正确步骤:
- 停掉 VictoriaMetrics 容器(或者只停 vm-single)。
- 把原数据目录改名或移到别处:
mv /vmdata /vmdata.bak - 新建
/vmdata,把备份压缩包解压到里面,确保解压后/vmdata下直接是indexdb、metadata.json这些文件,而不是再包一层目录。 - 重新启动容器。
- 访问
http://localhost:8428/vmui确认数据存在。
恢复过程中最容易错的就是压缩包内的目录层级。我遇到过两次:备份脚本用tar -zcvf backup.tar.gz -C /vmdata/snapshots/xxx .打包,恢复的时候解压到新目录,结果里面多了一层snapshots/xxx/,导致 VictoriaMetrics 启动失败或者识别不了。所以打包和解压时务必用-C切换到快照目录内再操作,解压后第一眼看一下有没有indexdb目录。
3. 实操过程与核心环节实现
3.1 环境准备:先装好 docker 环境
这里提醒一个非常常见的坑:如果你用的操作系统是 CentOS 7 或一些旧发行版,直接安装 docker 后运行docker-compose up可能会遇到docker-compose: error while loading shared libraries: libz.so.1: failed to map segment from shared object之类的报错。这通常是 libz 版本不兼容或者二进制文件权限问题。
我的建议是:不要用老旧的 docker-compose 独立二进制,直接在装完 Docker Engine 后用 Python pip 安装 docker-compose 或者直接使用新版 Docker Engine 自带的docker compose插件。从 Docker 23 开始,docker compose(带空格)已经集成到主程序里,不再需要单独安装。
具体做法:
# 安装 docker engine 和 compose 插件(以 ubuntu 为例) curl -fsSL https://get.docker.com | bash systemctl enable --now docker # 验证 docker compose version如果docker compose version输出版本信息,说明插件可用。后续所有命令都用docker compose而不是docker-compose。两者语法一致,但在旧版系统上docker-compose的共享库问题能直接避开。
3.2 初始化目录结构和配置
我建议的项目目录结构:
vm-stack/ ├── docker-compose.yml ├── config/ │ └── prometheus.yml ├── data/ │ ├── victoria-metrics/ │ ├── prometheus/ │ └── grafana/ └── backup/ └── vm-backup.sh注意data目录下三个子目录最好都提前创建,并且授权给容器的 UID。Grafana 和 Prometheus 在容器内分别以 UID 472 和 65534 运行,如果不预先给目录正确的权限,容器启动时会报mkdir permission denied。一个简单的处理方式:
mkdir -p data/victoria-metrics data/prometheus data/grafana backup chmod -R 777 data backup生产环境不建议直接用 777,但本地搭建图省事,这样能绕开大部分权限问题。真要讲究,可以分别chown 472:472 data/grafana、chown 65534:65534 data/prometheus。
3.3 启动整套服务并验证
在 vm-stack 目录下执行:
docker compose up -d启动后依次检查:
docker compose ps # 状态显示 Up 则正常 # 检查 VictoriaMetrics 自身指标 curl http://localhost:8428/metrics | head -30 # 应输出一堆 Prometheus 格式的指标 # 检查 Prometheus 是否正常 curl http://localhost:9090/-/ready # 输出 Prometheus is Ready # 检查 Grafana curl -I http://localhost:3000/login # HTTP 200启动过程中如果发现 VictoriaMetrics 容器一直重启,大概率是-storageDataPath指向的挂载目录没有写权限,或者参数里写了未知的 flag。可以在docker compose logs victoria-metrics里看到具体报错。
3.4 接入 Prometheus 数据:验证 remote write 链路
Prometheus 启动后,等待第一次 scrape 完成后(15 秒),进入 VictoriaMetrics 查询页面确认数据是否写入:
- 浏览器打开
http://localhost:8428/vmui - 输入
up,点击 Execute,结果里应该能看到up{job="node-exporter"}或你配置的 job 的 agent 状态。 - 输入
node_cpu_seconds_total,如果返回时间序列,说明 remote write 已经工作。
我这边踩过一个小坑:Prometheus 配置里写了remote_write的url: "http://victoria-metrics:8428/api/v1/write",但在 docker-compose 中,VictoriaMetrics 容器的名字不是victoria-metrics,导致 Prometheus 无法解析域名。排查方法:
docker compose exec prometheus ping victoria-metrics # 如果 ping 不通,检查 compose 里的服务名和 url 是否一致compose 网络内部 DNS 直接用服务名解析,不写 IP 地址,这是最优雅的方式,因为容器重建后 IP 会变,但服务名不会变。
3.5 写一个实用的备份脚本
这是我实际在用的vm-backup.sh,功能包括:创建快照、打包、推到远端目录、清理超过 7 天的本地备份、删除快照。
#!/bin/bash set -euo pipefail VM_URL="http://localhost:8428" BACKUP_ROOT="/opt/vm-stack/backup" RETENTION_DAYS=7 SNAPSHOT=$(curl -s -XPOST "${VM_URL}/-//snapshot/create" | python3 -c "import sys,json; print(json.load(sys.stdin)['snapshot'])") echo "created snapshot: ${SNAPSHOT}" SNAPSHOT_DIR="/vmdata/snapshots/${SNAPSHOT}" BK_FILE="${BACKUP_ROOT}/vm-$(date +%Y%m%d-%H%M%S).tar.gz" # 在容器内打包,避免跨文件系统硬链接失败 docker compose exec -T victoria-metrics tar -zcvf - -C /vmdata/snapshots/${SNAPSHOT} . > ${BK_FILE} echo "backup file: ${BK_FILE}" # 推送到远端,以 rsync 为例 rsync -av ${BK_FILE} backup@remote-host:/backup/vm/ # 清理旧文件 find ${BACKUP_ROOT} -name "vm-*.tar.gz" -mtime +${RETENTION_DAYS} -delete # 删除快照,释放磁盘空间 curl -s -XDELETE "${VM_URL}/-/snapshot/delete/${SNAPSHOT}" echo "backup done."补充几个细节:
set -euo pipefail保证脚本中途出错就退出,不会留个莫名奇妙的半成品备份。- 打包命令是在容器内部执行的,通过
docker compose exec -T victoria-metrics tar ...,这样从容器内访问/vmdata/snapshots/的路径是直接的。如果你在宿主机直接 tar 宿主机挂载目录,因为挂载目录和容器内路径一致(假设./data/victoria-metrics:/vmdata),其实也行,但执行环境里容易出现权限和路径混乱,我统一推荐在容器内打包。 - 推送远端我用 rsync,你也可以换 ossutil、rclone、s3cmd,看你的存储后端。关键是把备份文件与原始数据分离,而不是放在同一块盘上。
3.6 恢复演练:把备份还原成可用数据
光有备份不演练等于没有备份。我建议新环境首次搭建后,立刻做一次恢复演练,流程如下:
- 准备一台全新的机器,把 docker-compose.yml 拷贝过去。
- 解压备份文件到
/opt/vm-stack/data/victoria-metrics:
mkdir -p data/victoria-metrics tar -zxvf backup/vm-xxxx.tar.gz -C data/victoria-metrics ls data/victoria-metrics # 看到 indexdb, metadata.json 等文件则正常- 直接
docker compose up -d victoria-metrics,等几秒后打开 vmui 查询数据。
我看到很多人的恢复流程是在原环境上“覆盖式恢复”,这在实际生产环境风险极大。强烈建议恢复的时候用全新的目录,验证成功之后再切换流量。对应到这套 compose,你可以在恢复机器上改一下挂载路径,比如./restore-data/victoria-metrics:/vmdata,测试完没问题后再正式切换。
3.7 Grafana 接入 VictoriaMetrics 数据源
Grafana 里配置数据源很简单:
- 登录 Grafana,进入 Configuration -> Data Sources。
- Add data source,选择 Prometheus。
- URL 填
http://victoria-metrics:8428。 - Access 选 Server(如果 Grafana 和 VictoriaMetrics 在同一个 compose 网络里)或 Browser(如果通过宿主机端口访问)。
- Save & Test,看到 Success 即完成。
VictoriaMetrics 兼容 Prometheus HTTP API,所以 Grafana 里所有 Prometheus 类型的 dashboard 都能直接套用,不需要额外插件。我之前用过一个很流行的 Node Exporter Full 的 dashboard(ID 1860),导入后稍作变量调整就完全可用。这也是选 VictoriaMetrics 的一个隐性福利:Prometheus 生态的资产不用浪费。
4. 常见问题与排查技巧实录
4.1 问题速查表
下面列出我这些日子遇到的问题和解决思路。
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 容器启动后反复重启 | 数据目录权限不对或 storageDataPath 不存在 | docker compose logs victoria-metrics查看报错 | 创建目录并授权,确认挂载映射 |
Prometheus 远程写入报错server returned HTTP 500 | VictoriaMetrics 写入超时或磁盘满 | 查看 VictoriaMetrics 日志 | 调整-search.maxQueryDuration?不对,需要调整写入队列,检查磁盘可用空间 |
| 查询速度慢,部分数据查不到 | 数据没有写入 vm,只在 Prometheus 本地 | 在 Prometheus 的 /metrics 里查看remote_write的队列指标 | 调大 max_shards / capacity,检查 URL 是否可访问 |
| 备份文件比预想小很多 | 数据还没刷盘,快照只覆盖部分 block | 检查 backfill 进程 | VictoriaMetrics 在-snapshots创建时的策略是包含已刷盘数据,建议至少运行 24 小时后再做首次备份 |
| 恢复后数据只有一部分 | 解压目录层级问题 | 查看ls -la /vmdata | 确保 indexdb 在 vmdata 根目录 |
| docker compose 命令提示 libz 相关错误 | 旧版 docker-compose 二进制依赖共享库 | 执行docker compose version | 升级 Docker Engine 或改用 compose 插件 |
| Grafana 无法连接数据源 | 网络隔离或 URL 错 | docker compose exec grafana ping victoria-metrics | 使用 compose 服务名,不要用 localhost |
4.2 关于 VictoriaMetrics 内存和磁盘占用的经验
VictoriaMetrics 单机版对内存的占用主要取决于活跃时间序列数量和索引大小。我监控大约 300 台机器的 node_exporter,加上一些业务指标,活跃时间序列大约 80 万,内存稳定在 2GB 左右。内存不足时,VictoriaMetrics 会频繁触发删除和索引合并,CPU 使用率飙升,查询延迟变高。
磁盘方面,一个经验公式:每个活跃时间序列每小时大约产生 4MB 数据?这个数字并不准确,实际取决于指标数量和采样频率。我这边 15 秒间隔、300 台目标,每天新增数据量约 4GB,保留 90 天大约需要 360GB 空间。如果磁盘吃紧,可以调整-downsampling配置,比如旧数据降采样。
# 1年以上数据保留日级精度 -downsampling.period=720h:1m这个参数含义是:超过 720 小时(30天)的数据,将原始数据点聚合成 1 分钟间隔。能显著减少磁盘占用。但注意,降采样后无法查看原始秒级数据,所以不能逆后悔药。如果业务上有精确查询需求,慎重开启。
4.3 恢复时时间线验证技巧
恢复完成后,不要只看“有数据”就开心。我建议做两个验证:
- 时间范围验证:在 vmui 里查询
min(up)和max(up),看最早和最晚数据点是否覆盖你预期的时间范围。 - 数据量对比:对比备份源机器和恢复机器的
vm_vmstats中的指标数量,或者直接对比两个库查询出的count(up)是否接近。
如果恢复出来的数据比源库少了最近 1 小时的数据,很可能是备份脚本创建快照的时机晚于数据写入,或者快照创建后到打包完成这段时间内新写入的数据没有包含在快照内。这个问题不大,因为备份本来就是“某个时间点的快照”,不需要保证和源库完全同一时刻。但如果你有连续增量备份的需求,就得上 VictoriaMetrics 自带的vmbackup工具,它支持周期备份和增量备份,底层语法比 shell 脚本复杂一些,但对生产环境更友好。
4.4 升级与滚动重启的小坑
升级 VictoriaMetrics 镜像时,很多人直接改 compose 里的版本号,然后docker compose up -d。但注意,容器内数据目录的版本兼容性。通常小版本升级没问题,跨大版本(如 v1.x 到 v2.x)要参考官方升级说明。我遇到过升级后启动失败,报错提示incompatible cache version,解决办法是删除/vmdata/cache目录后重启。
另外,VictoriaMetrics 的cache目录是运行时缓存,删除后会在重启时自动重新建立。不要因为“缓存”两个字就误以为它很重要——它只是加速索引合并查询使用,丢失不致命。如果启动时报 cache 相关错误,第一选择就是先删 cache 再试。
4.5 监控这套系统本身
最后一个建议:不要忽略对监控系统自身的监控。我会在 Prometheus 里额外抓取 VictoriaMetrics 的/metrics,并配置几个关键告警:
vm_vmstorage_metrics的vm_rows_count > 0但查询返回空,说明数据链路断了。remote_write_backoff_seconds突然增大,说明 remote write 失败次数变多。- VictoriaMetrics 磁盘剩余空间低于 20% 时告警。
很多人搭好监控就放心不管,结果时序库自己挂了,监控数据成了黑洞。总之,这套方案的优势是轻量、透明、可恢复,踩坑点也集中,希望这些记录能让你少走弯路。