
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 - -retentionPeriod3 - -search.maxUniqueTimeseries1000000 - -search.maxQueryDuration30s - -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.time7d depends_on: - victoria-metrics grafana: image: grafana/grafana:10.1.2 container_name: grafana restart: unless-stopped ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORDadmin123 - GF_USERS_ALLOW_SIGN_UPfalse volumes: - ./data/grafana:/var/lib/grafana depends_on: - victoria-metrics几个容易被忽略的点-retentionPeriod3单位是天也可以配置3表示三个月不对官方文档明确是“保留天数”。例如-retentionPeriod3就是保留 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 默认 2500max_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{jobnode-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_URLhttp://localhost:8428 BACKUP_ROOT/opt/vm-stack/backup RETENTION_DAYS7 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} backupremote-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-metricsmkdir -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 的 dashboardID 1860导入后稍作变量调整就完全可用。这也是选 VictoriaMetrics 的一个隐性福利Prometheus 生态的资产不用浪费。4. 常见问题与排查技巧实录4.1 问题速查表下面列出我这些日子遇到的问题和解决思路。现象可能原因排查方法解决方案容器启动后反复重启数据目录权限不对或 storageDataPath 不存在docker compose logs victoria-metrics查看报错创建目录并授权确认挂载映射Prometheus 远程写入报错server returned HTTP 500VictoriaMetrics 写入超时或磁盘满查看 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 服务名不要用 localhost4.2 关于 VictoriaMetrics 内存和磁盘占用的经验VictoriaMetrics 单机版对内存的占用主要取决于活跃时间序列数量和索引大小。我监控大约 300 台机器的 node_exporter加上一些业务指标活跃时间序列大约 80 万内存稳定在 2GB 左右。内存不足时VictoriaMetrics 会频繁触发删除和索引合并CPU 使用率飙升查询延迟变高。磁盘方面一个经验公式每个活跃时间序列每小时大约产生 4MB 数据这个数字并不准确实际取决于指标数量和采样频率。我这边 15 秒间隔、300 台目标每天新增数据量约 4GB保留 90 天大约需要 360GB 空间。如果磁盘吃紧可以调整-downsampling配置比如旧数据降采样。# 1年以上数据保留日级精度 -downsampling.period720h: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% 时告警。很多人搭好监控就放心不管结果时序库自己挂了监控数据成了黑洞。总之这套方案的优势是轻量、透明、可恢复踩坑点也集中希望这些记录能让你少走弯路。