灯下哥谭 灯下哥谭
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

灯下哥谭

灯还亮着
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 工作笔记

  • 容器与编排

  • Nginx

  • 监控

    • prometheus监控介绍
    • PromQL介绍
    • VictoriaMetrics 集群版:三组件架构与水平扩展机制解析
      • 1. 从 Prometheus 到 VictoriaMetrics:为什么需要它
      • 2. 集群版三组件:职责与数据流
        • 2.1 vmstorage:存储节点
        • 2.2 vminsert:写入网关
        • 2.3 vmselect:查询聚合器
        • 2.4 数据流全景
      • 3. vmagent 的角色与采集链路
      • 3. 集群拓扑选择:单机版 vs 集群版
        • 3.1 单机版足够的情况
        • 3.2 需要集群版的信号
        • 3.3 集群版的代价
      • 4. 关键配置参数与容量规划
        • 4.1 副本与一致性
        • 4.2 副本与去重的配合
        • 4.3 扩容操作与节点数规划
      • 5. 部署后验证清单
        • 5.1 写入验证
        • 5.2 查询验证
        • 5.3 高可用验证
      • 6. 常见坑与边界
        • 6.1 副本数配置不一致
        • 6.2 vmstorage 节点时钟不同步
        • 6.3 扩容后期望立即平衡
        • 6.4 防火墙只开放 HTTP 端口
      • 7. 排查指南:当查询不到数据时
        • 7.1 确认写入侧是否正常
        • 7.2 确认查询侧是否正常
        • 7.3 常见症状与定位
    • rclone常用命令参数详解(含 WebDAV 挂载实战)
  • 网络安全

  • 其他

  • Linux笔记
  • 监控
灯下哥谭
2024-08-24
目录

VictoriaMetrics 集群版:三组件架构与水平扩展机制解析

假设你的 Prometheus 已经运行了一段时间,单实例的存储成为瓶颈:数据量增长到几百 GB,查询范围稍大就超时,硬盘快满却不敢删旧数据。这时候你会考虑 VictoriaMetrics——一个兼容 Prometheus 远程写入协议的时序数据库,用更低的资源占用存储更多数据。但很快你会发现它有两个形态:单机版(Single-node)和集群版(Cluster)。单机版部署简单,集群版则有 vminsert、vmstorage、vmselect 三个组件。本文拆解这三个组件的职责分工、数据流转路径,以及什么时候该从单机版迈向集群版。

版本说明

原写于 2024-08,2026-09 重写。
基于 VictoriaMetrics 1.102+ 版本。集群版架构设计在 v1.80 后趋于稳定,但参数和端口可能随版本微调,部署前请查阅对应版本的官方文档确认。


# 1. 从 Prometheus 到 VictoriaMetrics:为什么需要它

Prometheus 的设计哲学是「每个服务一个实例,横向通过联邦聚合」。这种模型适合中小规模,但当监控目标膨胀到成千上万、指标基数(cardinality)达到百万级时,单实例 Prometheus 会遇到几个硬瓶颈:

  • 存储限制:Prometheus 默认只保留 15 天数据,TSDB 没有内置压缩机制,长期存储成本高昂
  • 查询限制:大时间范围的聚合查询会触发大量磁盘 I/O,单线程查询模型容易超时
  • 高可用缺口:Prometheus 没有原生存储级的高可用,只能靠外部 Thanos/Cortex 或双写

VictoriaMetrics 作为 Prometheus 的远程存储后端,用几个设计解决了这些问题:

  • 更高的数据压缩率(官方称可达 10x,相比 Prometheus 原始存储)
  • 支持长期保留(通过 -retentionPeriod 参数)
  • 单机版即可水平扩展到多核,集群版进一步拆解读写路径

但 VictoriaMetrics 不是 Prometheus 的替代品——它承接 Prometheus 的远程写入(remote_write),但告警规则、服务发现、抓取(scrape)仍然依赖 Prometheus 或其生态(如 vmagent)。准确说,VictoriaMetrics 是「存储与查询层」,Prometheus(或 vmagent)是「采集层」。


# 2. 集群版三组件:职责与数据流

VictoriaMetrics 集群版由三个独立可扩展的组件构成,每个组件都是无状态的(除 vmstorage 的磁盘数据外),可以独立扩缩容。

# 2.1 vmstorage:存储节点

职责:存储原始数据,为查询提供指定时间范围和标签过滤的数据。

vmstorage 是唯一有状态的组件,它在本地磁盘维护:

  • 原始时间序列数据(按时间分片的 LSM 结构)
  • IndexDB(metric name 和 label 的倒排索引)
  • 元数据(TSID 映射等)

关键参数:

vmstorage-prod \
  -storageDataPath=/data/vmstorage \
  -retentionPeriod=3 \
  -vminsertAddr=:8400 \
  -vmselectAddr=:8401 \
  -httpListenAddr=:8482
1
2
3
4
5
6
  • -retentionPeriod:保留月数,超过的数据自动清理
  • -vminsertAddr:接收来自 vminsert 的数据写入(TCP)
  • -vmselectAddr:接收来自 vmselect 的查询请求(TCP)

# 2.2 vminsert:写入网关

职责:接收来自采集端(Prometheus/vmagent)的远程写入请求,按一致性哈希将数据分发给 vmstorage 节点。

vminsert 本身不存储数据,它是无状态的写入代理。收到一批指标后,它会:

  1. 解析 Prometheus remote_write 协议的数据块
  2. 对每条时间序列计算哈希(基于 metric name + 所有 label)
  3. 根据哈希值将数据路由到对应的 vmstorage 节点

关键参数:

vminsert-prod \
  -storageNode=vmstorage-1:8400,vmstorage-2:8400,vmstorage-3:8400 \
  -replicationFactor=2 \
  -httpListenAddr=:8480
1
2
3
4
  • -storageNode:所有 vmstorage 节点的地址列表(逗号分隔)
  • -replicationFactor:副本数,设为 N 表示每条数据存 N 份到 N 个不同节点

当某个 vmstorage 节点不可用时,vminsert 会自动将数据路由到剩余健康节点,保证写入不中断(但剩余节点会承担更多负载)。

# 2.3 vmselect:查询聚合器

职责:接收查询请求(PromQL),并行从所有 vmstorage 节点拉取数据,在内存中合并、去重、计算后返回结果。

vmselect 是无状态的查询代理,它本身不缓存数据(除了查询过程中的临时结果)。查询流程:

  1. 接收 PromQL 查询请求
  2. 并行向所有配置的 vmstorage 节点发送子查询
  3. 合并各节点返回的数据(按时间序列去重)
  4. 执行聚合函数(sum、rate 等)
  5. 返回最终结果

关键参数:

vmselect-prod \
  -storageNode=vmstorage-1:8401,vmstorage-2:8401,vmstorage-3:8401 \
  -replicationFactor=2 \
  -search.denyPartialResponse=false \
  -httpListenAddr=:8481
1
2
3
4
5
  • -replicationFactor:必须与 vminsert 配置一致,告诉 vmselect「数据存了几份」
  • -search.denyPartialResponse:设为 true 时,如果任意 vmstorage 节点不可用,查询返回错误而非部分数据

# 2.4 数据流全景

整体数据路径如下:

[Prometheus/vmagent] --remote_write--> [vminsert:8480] 
                                           |
                                           | (分片路由)
                                           v
                          +----------------+---------------+
                          |                |               |
                    [vmstorage-1]    [vmstorage-2]   [vmstorage-3]
                    [:8400/:8401]    [:8400/:8401]   [:8400/:8401]
                          ^                ^               ^
                          |                |               |
                          +----------------+---------------+
                                           |
                                           | (并行查询)
                                           v
[Grafana/API] <--PromQL-- [vmselect:8481]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这三个组件之间通过 TCP 通信(vminsert→vmstorage 的 8400 端口,vmselect→vmstorage 的 8401 端口),HTTP 端口(8480/8481/8482)用于接收客户端请求和暴露监控指标。


# 3. vmagent 的角色与采集链路

在完整的数据链路中,vmagent 通常位于 Prometheus 与 vminsert 之间,承担「边缘采集 + 远程写入代理」的角色。它的核心职责:

  1. 替代 Prometheus 做抓取:vmagent 实现了 Prometheus 的抓取逻辑,可以配置 scrape_configs 直接拉取目标指标,但资源占用远低于 Prometheus
  2. 聚合与改写:支持 relabel_configs 对指标进行标签改写、丢弃、聚合
  3. 多远端写入:支持同时向多个 vminsert 地址写入(-remoteWrite.url),实现写入层高可用
  4. 本地 WAL 缓冲:当远端 vminsert 不可用时,数据写入本地 WAL,恢复后自动重传

Prometheus 与 vmagent 的对接方式:

# prometheus.yml 中的 remote_write 配置
remote_write:
  - url: "http://vmagent:8429/api/v1/write"
    queue_config:
      max_samples_per_send: 10000
      max_shards: 10

# 或直接指向 vminsert(无 vmagent 时)
remote_write:
  - url: "http://vminsert:8480/insert/0/prometheus/api/v1/write"
1
2
3
4
5
6
7
8
9
10

vmagent 的 HTTP 端口(默认 8429)接收 Prometheus remote_write 协议,内部做缓冲后再转发给配置的 vminsert。这种分层的好处是:

  • 边缘节点(机房、K8s 集群)部署 vmagent 做本地缓冲,跨网络写入中心 vmcluster
  • vminsert 故障时,vmagent 本地队列兜底,避免数据丢失
  • 中心机房专注存储,不直接暴露在边缘网络的公网访问中

# 3. 集群拓扑选择:单机版 vs 集群版

# 3.1 单机版足够的情况

VictoriaMetrics 单机版已经非常高效,可以处理:

  • 每秒百万级数据点写入
  • 数 TB 的存储(取决于磁盘)
  • 多核并行查询

推荐先用单机版的情况:

  • 监控目标 < 1000 个节点
  • 指标基数(unique time series)< 1000 万
  • 不需要跨可用区容灾
  • 团队运维资源有限

# 3.2 需要集群版的信号

  • 单机 CPU 或磁盘 I/O 成为瓶颈,垂直扩容(换更大机器)成本高
  • 需要持续保留 6 个月以上数据,单机磁盘容量不够
  • 对可用性要求高(单节点故障不能中断写入或查询)
  • 写入和查询负载需要独立扩容(写入量大但查询少,或反之)

# 3.3 集群版的代价

集群版不是「更强版本的单机版」,它带来额外的运维复杂度:

  • 需要管理三个独立的进程/服务
  • vmstorage 扩容后,历史数据不会自动重新平衡(只有新数据会写到新节点)
  • 网络延迟增加(组件间通过 TCP 通信)
  • 需要额外的负载均衡器(如 nginx、vmauth)在 vminsert 和 vmselect 前做代理

预期输出

# 检查 vmstorage 本地存储占用
du -sh /data/vmstorage/data/*
# 预期看到按月份分片的目录,如 big/2024_08, small/2024_08

# 查看 vmstorage 指标(本地 http 接口)
curl -s http://localhost:8482/metrics | grep vm_rows_added_to_storage_total
# 预期返回累计写入的数据行数
1
2
3
4
5
6
7

# 4. 关键配置参数与容量规划

# 4.1 副本与一致性

-replicationFactor 是集群版最重要的参数之一:

  • 设为 1:无冗余,单 vmstorage 节点故障会导致该分片数据不可查
  • 设为 2:每条数据存两份,可容忍任意一个 vmstorage 节点故障
  • 设为 3:更高冗余,但存储成本翻倍

注意:副本数在写入侧(vminsert)和查询侧(vmselect)必须配置一致,否则 vmselect 会按错误的副本预期做查询,导致数据缺失或重复。

# 4.2 副本与去重的配合

当 -replicationFactor > 1 时,同一指标数据会存在多个 vmstorage 节点上。vmselect 查询时会拿到多份相同数据,需要在查询端去重。VictoriaMetrics 通过以下机制处理:

  1. 查询时去重:vmselect 默认会对返回的时间序列按 (metric name + labels + timestamp) 去重,保留最新值
  2. -dedup.minScrapeInterval:可配置最小采集间隔,用于识别「同一指标相近时间的多个采样点」是否为重复。如果两个采样点时间差小于此值,后一个覆盖前一个

示例场景:

  • replicationFactor=2,两个 vmstorage 节点都存有 cpu_usage{host="A"} 50
  • vmselect 查询时从两个节点各拿到一条,去重后只返回一条给客户端
  • 如果因为时钟漂移或网络延迟,两条数据 timestamp 相差 1ms,-dedup.minScrapeInterval=1ms 会保留后写入的那条

配置建议:

  • 生产环境建议设 -dedup.minScrapeInterval 略大于实际采集间隔(如采集间隔 15s,设 20s),避免抖动导致的重复计数
  • 该参数只在 vmselect 配置,vminsert 无需关心

# 4.3 扩容操作与节点数规划

-replicationFactor 与 vmstorage 节点数的关系:

vmstorage 节点数 replicationFactor 容错能力 存储利用率
3 1 0(单点故障丢数据) 100%
3 2 1(容忍任意 1 节点故障) 200%
3 3 2(容忍任意 2 节点故障) 300%
5 2 1 200%
5 3 2 300%

扩容(增加 vmstorage 节点)的详细步骤:

# 1. 启动新 vmstorage 节点(假设新节点为 vmstorage-4)
vmstorage-prod \
  -storageDataPath=/data/vmstorage \
  -retentionPeriod=3 \
  -vminsertAddr=:8400 \
  -vmselectAddr=:8401 \
  -httpListenAddr=:8482

# 2. 滚动更新所有 vminsert,加入新节点地址
# 修改 vminsert 启动参数:
vminsert-prod \
  -storageNode=vmstorage-1:8400,vmstorage-2:8400,vmstorage-3:8400,vmstorage-4:8400 \
  -replicationFactor=2 \
  -httpListenAddr=:8480

# 3. 滚动更新所有 vmselect,加入新节点地址
vmselect-prod \
  -storageNode=vmstorage-1:8401,vmstorage-2:8401,vmstorage-3:8401,vmstorage-4:8401 \
  -replicationFactor=2 \
  -httpListenAddr=:8481
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

关键限制: VictoriaMetrics 的分片基于一致性哈希,但扩容时不会触发历史数据重分布。新加入的节点只承接「扩容后新写入」的数据,扩容前已有的数据仍留在原来的节点上。这意味着:

  • 查询性能不会立即均匀化(旧数据还在旧节点)
  • 磁盘使用率也不会立即平衡(旧节点仍有历史数据)
  • 随着时间推移和旧数据 TTL 过期,各节点数据量会自然趋近平衡

如果需要强制平衡,只能通过「下线旧节点 → 删除数据 → 重新加入」的方式,但这会导致该节点上所有历史数据丢失(需确保 replicationFactor 足够从其他副本恢复)。

缩容(移除 vmstorage 节点)需要更谨慎:

  1. 确保 -replicationFactor 足够(缩容后仍有冗余)
  2. 修改 vminsert 和 vmselect 的 -storageNode 移除目标节点,重启
  3. 等待数据在其他节点自然过期(由 -retentionPeriod 控制),或手动清理

预期输出

# 查看 vmstorage 集群状态(vmselect 端点)
curl -s "http://vmselect:8481/api/v1/status/tsdb" | jq '.data {seriesCountByLabelName, seriesCountByMetricName}'
# 预期返回当前 TSDB 中的时间序列统计

# 检查各 vmstorage 节点的健康状态
curl -s http://vmstorage-1:8482/health
# 预期返回 "OK"
1
2
3
4
5
6
7

# 5. 部署后验证清单

集群搭起来后,需要验证数据真的在流动:

# 5.1 写入验证

# 1. 确认 vminsert 接收数据(查看自身指标)
curl -s http://vminsert:8480/metrics | grep vm_protoparser_rows_read_total

# 2. 确认 vmstorage 存储数据
curl -s http://vmstorage:8482/metrics | grep vm_rows_added_to_storage_total
1
2
3
4
5

# 5.2 查询验证

# 查询最近 5 分钟的数据(替换为实际存在的指标名)
curl -s "http://vmselect:8481/api/v1/query?query=up&time=$(date +%s)" | jq '.data.result | length'
# 预期返回非空结果(返回时间序列数量)
1
2
3

# 5.3 高可用验证

# 停止一个 vmstorage 节点,检查写入是否继续
systemctl stop vmstorage-2

# 检查 vminsert 指标,确认重路由发生
curl -s http://vminsert:8480/metrics | grep vm_storage_node_conns

# 检查 vmselect 是否返回 partial response
curl -s "http://vmselect:8481/api/v1/query?query=up" | jq '.isPartial'
# 预期返回 true(表示数据不完整但可用)或 null(取决于 denyPartialResponse 配置)
1
2
3
4
5
6
7
8
9

# 6. 常见坑与边界

# 6.1 副本数配置不一致

vminsert 配了 -replicationFactor=2,vmselect 配了默认的 1,会导致 vmselect 只期望每份数据在一个节点上,查询时可能漏掉副本节点上的数据。必须保证两边配置一致。

# 6.2 vmstorage 节点时钟不同步

vmstorage 节点依赖本地时间进行数据分片和过期清理。如果节点间时钟偏差过大,可能导致数据分布不均或提前/延迟过期。部署前必须配置 NTP。

# 6.3 扩容后期望立即平衡

很多运维习惯 Elasticsearch 那种「加节点后自动重新分片」的行为,但 VictoriaMetrics 没有自动重平衡。新节点只承接新数据,历史数据仍在旧节点。如需强制平衡,只能让旧节点下线重建(会丢失该节点历史数据)。

# 6.4 防火墙只开放 HTTP 端口

vminsert 连接 vmstorage 的 8400 端口(TCP 自定义协议),vmselect 连接 vmstorage 的 8401 端口(TCP 自定义协议)。如果只开放 8482 HTTP 端口,集群无法通信。必须确保 8400/8401 端口可达。


# 7. 排查指南:当查询不到数据时

vmselect 查不到数据时,需要分阶段定位是「写入链路断了」还是「查询链路问题」。

# 7.1 确认写入侧是否正常

# 1. 检查 vminsert 是否收到数据
curl -s http://vminsert:8480/metrics | grep vm_protoparser_rows_read_total

# 2. 检查 vminsert 是否能连接到所有 vmstorage
curl -s http://vminsert:8480/metrics | grep vm_storage_node_conns
# 预期看到每个 storageNode 对应一条 conn 指标,值为 1 表示连接正常

# 3. 检查 vmstorage 是否实际存储了数据
curl -s http://vmstorage:8482/metrics | grep vm_rows_added_to_storage_total
# 这个值应该持续增长

# 4. 检查 vmstorage 磁盘是否已满
curl -s http://vmstorage:8482/api/v1/status/tsdb | jq '.data.freeDiskSpaceBytes'
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.2 确认查询侧是否正常

# 1. 直接查询 vmselect 可用的指标列表
curl -s "http://vmselect:8481/api/v1/label/__name__/values" | jq '.data[]'

# 2. 检查 vmselect 到各 vmstorage 的连接
curl -s http://vmselect:8481/metrics | grep vmselect_storage_nodes

# 3. 查看是否有 partial response
curl -s "http://vmselect:8481/api/v1/query?query=up" | jq '.isPartial'
# null 或 false 表示数据完整;true 表示部分节点未返回数据
1
2
3
4
5
6
7
8
9

# 7.3 常见症状与定位

症状 可能原因 排查命令
vmselect 返回空结果,但指标确定存在 时间范围错误、标签不匹配 检查 query 的 start/end 时间戳
vmselect 返回 isPartial=true 部分 vmstorage 节点故障或网络不通 检查 vmselect 到各节点的连通性
vminsert rows_read 增长但 storage rows_added 不增长 vminsert 到 vmstorage 连不通 检查 8400 端口连通性
所有指标都查不到,但服务都running vmselect 配置的 storageNode 端口错误 确认 vmselect 用 8401 而非 8400

最关键的检查点:vminsert 用 8400 端口连 vmstorage,vmselect 用 8401 端口连 vmstorage。如果 vmselect 误配成 8400,查询会无结果但也不会报错(因为 8400 是 vmstorage 的插入端口,查询请求会被静默丢弃)。


Agent 可直接解析的元数据块
{
  "article_id": "victoriametrics-cluster",
  "permalink": "/pages/victoriametrics-cluster-install/",
  "category": "Linux笔记/监控",
  "tags": ["可观测性", "VictoriaMetrics", "时序数据库", "集群"],
  "commands": {
    "check_storage_usage": "du -sh /data/vmstorage/data/*",
    "check_ingest_metrics": "curl -s http://localhost:8480/metrics | grep vm_protoparser_rows_read_total",
    "check_storage_metrics": "curl -s http://localhost:8482/metrics | grep vm_rows_added_to_storage_total",
    "query_via_select": "curl -s 'http://vmselect:8481/api/v1/query?query=up&time=$(date +%s)'",
    "check_health": "curl -s http://vmstorage:8482/health",
    "check_tsdb_status": "curl -s http://vmselect:8481/api/v1/status/tsdb"
  },
  "config_keys": [
    "-storageNode",
    "-replicationFactor",
    "-retentionPeriod",
    "-storageDataPath",
    "-vminsertAddr",
    "-vmselectAddr",
    "-search.denyPartialResponse"
  ],
  "key_insights": [
    "vmstorage是唯一有状态组件,存储所有原始数据和索引",
    "vminsert是无状态写入代理,按一致性哈希分片",
    "vmselect是无状态查询代理,并行查询所有vmstorage并合并结果",
    "副本数在写入侧和查询侧必须配置一致",
    "扩容只影响新数据,历史数据不会自动重平衡"
  ],
  "version_assertions": {
    "based_on": "v1.102+",
    "architecture_stable_since": "v1.80",
    "source": "https://docs.victoriametrics.com/cluster-victoriametrics/"
  },
  "misconceptions": [
    "VictoriaMetrics可以替代Prometheus(错,它只替代存储和查询,采集仍需要Prometheus/vmagent)",
    "集群版是单机版的升级替换(错,集群版有额外的运维复杂度,单机版能先撑很久)",
    "扩容后数据会自动平衡(错,只有新数据写到新节点)",
    "vmselect缓存查询结果(错,每次查询都穿透到vmstorage)"
  ],
  "risk_level": "medium",
  "related_articles": [
    {
      "title": "prometheus监控介绍",
      "permalink": "/pages/87d3b8/",
      "relationship": "前置阅读:Prometheus架构与数据模型"
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#可观测性#VictoriaMetrics#时序数据库#集群
上次更新: 9/11/2026

← PromQL介绍 rclone常用命令参数详解(含 WebDAV 挂载实战)→

最近更新
01
DeepSeek Harness 实战 06|学习笔记:插件、工具、技能不在同一个维度上 原创
09-11
02
DeepSeek Harness 实战 05|让两个编码 Agent 共用一份长期记忆 原创
09-09
03
DeepSeek Harness 实战 04|学习笔记:从「已知限制」里读出三处设计张力 原创
09-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式