强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

VictoriaMetrics 完全指南 / 09 - 集群部署详解

09 · 集群部署详解

本章目标

  • 深入理解集群三大组件的职责与配置
  • 掌握集群容量规划与节点分配
  • 学会集群的扩缩容操作
  • 了解多租户机制
  • 掌握集群的高可用配置

9.1 集群架构回顾

                   ┌─────────────┐
     ┌────────────▶│   vmselect   │◀────────────┐
     │             │  查询层 (N)   │              │
     │             └──────┬───────┘              │
     │                    │                      │
     │                    ▼                      │
     │    ┌──────────┬──────────┬──────────┐     │
     │    │vmstorage1│vmstorage2│vmstorage3│     │
     │    │ 存储层    │ 存储层    │ 存储层    │     │
     │    └──────────┴──────────┴──────────┘     │
     │                    ▲                      │
     │                    │                      │
     │             ┌──────┴───────┐              │
     │             │   vminsert   │              │
     └─────────────│  写入层 (M)  │──────────────┘
                   └─────────────┘
组件职责状态扩容方式
vminsert接收写入、路由分片无状态增加实例
vmselect接收查询、聚合结果近无状态(有缓存)增加实例
vmstorage持久化存储、索引有状态增加节点(需规划)

9.2 vminsert 详解

9.2.1 核心参数

vminsert \
    # vmstorage 节点地址
    -storageNode=vmstorage1:8400,vmstorage2:8400,vmstorage3:8400 \
    
    # HTTP 监听地址
    -httpListenAddr=:8480 \
    
    # 副本因子(写入几份)
    -replicationFactor=2 \
    
    # 每个时间序列最大标签数
    -maxLabelsPerTimeseries=30 \
    
    # 标签名称最大长度
    -maxLabelNameLen=256 \
    
    # 标签值最大长度
    -maxLabelValueLen=4096 \
    
    # 采样间隔去重(用于 Prometheus HA 对)
    -dedup.minScrapeInterval=15s \
    
    # 最大并发写入数
    -maxConcurrentInserts=32 \
    
    # 是否禁用请求日志
    -disableRerouting=false

9.2.2 支持的写入协议

vminsert 接受多种协议的写入:

协议路径说明
Prometheus remote_write/api/v1/writeProtobuf 格式
Prometheus import/api/v1/import/prometheus文本格式
InfluxDB Line Protocol/api/v1/import/influx/writeInfluxDB 格式
OpenTSDB/api/v1/import/opentsdbOpenTSDB JSON 格式
OpenTSDB telnet/api/v1/import/opentsdb/telnetOpenTSDB telnet 格式
Graphite/api/v1/import/graphiteGraphite 格式
CSV/api/v1/import/csvCSV 格式
Native/api/v1/import/nativeVM 原生格式

9.2.3 写入路由策略

写入路由(Consistent Hashing):

series = metric_name + label_set
hash_value = hash(series)
target_node = hash_value % len(storage_nodes)

示例:
  cpu_usage{host="web01"} → hash → node 0
  cpu_usage{host="web02"} → hash → node 1
  cpu_usage{host="db01"}  → hash → node 2
  
同一 series 始终路由到同一 vmstorage(保证数据局部性)

9.2.4 路由重试

# 禁用路由重排(当某个 vmstorage 短暂不可用时)
vminsert -disableRerouting=false

# 启用路由重排(将不可用节点的流量分配到其他节点)
vminsert -disableRerouting=true  # 注意:这可能导致数据不均匀

9.3 vmselect 详解

9.3.1 核心参数

vmselect \
    # vmstorage 节点地址
    -storageNode=vmstorage1:8401,vmstorage2:8401,vmstorage3:8401 \
    
    # HTTP 监听地址
    -httpListenAddr=:8481 \
    
    # 查询缓存存储路径(磁盘缓存比内存缓存更持久)
    -cacheDataPath=/var/lib/vmselect/cache \
    
    # 去重间隔
    -dedup.minScrapeInterval=15s \
    
    # 最大查询持续时间
    -search.maxQueryDuration=30s \
    
    # 最大并发查询数
    -search.maxConcurrentRequests=16 \
    
    # 查询最大点数
    -search.maxPointsPerTimeseries=30000 \
    
    # 最大可查询的活跃序列数
    -search.maxUniqueTimeseries=1000000 \
    
    # 最大标签值数(label_values API)
    -search.maxTagKeys=100000 \
    -search.maxTagValues=100000 \
    
    # staleness 窗口
    -search.maxStalenessInterval=30s

9.3.2 查询缓存

vmselect 使用多级缓存:

查询请求
    │
    ▼
┌──────────────┐
│ L1: 内存缓存  │  ← 最快,进程重启后丢失
└──────┬───────┘
       │ miss
       ▼
┌──────────────┐
│ L2: 磁盘缓存  │  ← 较快,进程重启后仍有效
└──────┬───────┘  (需 -cacheDataPath)
       │ miss
       ▼
┌──────────────┐
│ vmstorage    │  ← 查询存储层
└──────────────┘
# 配置磁盘缓存
vmselect -cacheDataPath=/var/lib/vmselect/cache

# 监控缓存命中率
# 在 /metrics 端点中查看:
# vm_cache_entries{type="storage/tsid"} - 缓存条目数
# vm_cache_size_bytes{type="storage/tsid"} - 缓存大小

9.3.3 查询并发控制

# 限制最大并发查询数(防止 OOM)
vmselect -search.maxConcurrentRequests=16

# 设置查询超时
vmselect -search.maxQueryDuration=30s

# 限制返回的时间序列数
vmselect -search.maxUniqueTimeseries=500000

9.4 vmstorage 详解

9.4.1 核心参数

vmstorage \
    # 数据存储路径
    -storageDataPath=/var/lib/vmstorage \
    
    # 数据保留期
    -retentionPeriod=90d \
    
    # HTTP 监听地址
    -httpListenAddr=:8482 \
    
    # vminsert 端口
    -vminsertAddr=:8400 \
    
    # vmselect 端口
    -vmselectAddr=:8401 \
    
    # 内存使用限制
    -memory.allowedPercent=60 \
    
    # 最小合并间隔
    -dedup.minScrapeInterval=15s \
    
    # 最大并发插入数
    -maxConcurrentInserts=32 \
    
    # 最小磁盘可用空间(低于此值拒绝写入)
    -storage.minFreeDiskSpaceBytes=10GB \
    
    # 是否使用日期索引
    -disablePerDayIndex=false \
    
    # 强制合并的最小 Part 大小
    -finalMergeDelay=6h

9.4.2 存储目录结构

<storageDataPath>/
├── data/
│   ├── big/                   # 大 Part(合并后)
│   │   ├── 20240115/         # 按日期分区
│   │   │   ├── part-0/
│   │   │   └── part-1/
│   │   └── 20240116/
│   │       └── part-0/
│   ├── small/                  # 小 Part(待合并)
│   │   └── ...
│   └── pending/                # 待删除的过期 Part
│       └── ...
├── snapshots/                  # 快照
│   └── 20240115T120000Z/
└── indexdb/                    # 倒排索引
    ├── current/
    └── previous/

9.4.3 磁盘 I/O 模式

写入 I/O:
  ├── 追加写入(顺序 I/O)→ SSD/HDD 均可
  ├── Part 合并(顺序读 + 顺序写)→ SSD 更优
  └── 索引更新(随机写)→ SSD 强烈推荐

读取 I/O:
  ├── 查询(随机读)→ SSD 更优
  └── 合并(顺序读)→ HDD 可接受

推荐:
  生产环境 → SSD(NVMe 最佳)
  归档/冷数据 → 可考虑 HDD

9.5 多租户配置

9.5.1 租户标识

# 租户通过 HTTP Header 指定
# AccountID(租户 ID)+ ProjectID(项目 ID)

# 写入数据到租户 123, 项目 456
curl -H 'AccountID: 123' -H 'ProjectID: 456' \
    -d 'metric_name 42' \
    'http://vminsert:8480/insert/123/456/prometheus/api/v1/import/prometheus'

# 查询租户 123, 项目 456 的数据
curl 'http://vmselect:8481/select/123/456/prometheus/api/v1/query?query=metric_name'

9.5.2 路径式租户路由

# vminsert 支持路径式租户
/insert/<accountID>/<projectID>/prometheus/api/v1/write

# vmselect 支持路径式租户
/select/<accountID>/<projectID>/prometheus/api/v1/query

9.5.3 多租户资源隔离

# 限制单个租户的最大活跃序列数
vmstorage -maxTenantTokens=1000000

# 限制单个租户的查询并发
vmselect -search.maxTenantConcurrentRequests=10

9.6 高可用配置

9.6.1 推荐 HA 架构

                    ┌───────────────┐
                    │   vmauth      │  负载均衡
                    │  / Nginx      │
                    └───┬───────┬───┘
                        │       │
              ┌─────────┤       ├─────────┐
              ▼         ▼       ▼         ▼
        ┌──────────┐ ┌──────────┐
        │ vmselect1│ │ vmselect2│   查询层(至少2个)
        └─────┬────┘ └────┬─────┘
              │            │
    ┌─────────┼────────────┼─────────┐
    ▼         ▼            ▼         ▼
┌────────┐ ┌────────┐ ┌────────┐
│storage1│ │storage2│ │storage3│    存储层(3节点,副本=2)
└────────┘ └────────┘ └────────┘
    ▲         ▲            ▲
    │         │            │
    ├─────────┼────────────┤
    │         │            │
┌─────┴───┐ ┌──┴──────┐
│inserter1│ │inserter2│            写入层(至少2个)
└─────────┘ └─────────┘
    ▲           ▲
    │           │
┌───┴───┐   ┌──┴────┐
│Prom-1 │   │Prom-2 │              数据源(HA Pair)
└───────┘   └───────┘

9.6.2 vmstorage 副本因子

# 设置副本因子为 2(推荐生产环境)
vminsert -replicationFactor=2

# 当 rf=2 时,vminsert 会将每条数据写入 2 个 vmstorage
# 任意 1 个 vmstorage 宕机,数据仍然完整
副本因子容错能力存储开销适用场景
10 节点1x开发测试
21 节点2x生产推荐
32 节点3x关键业务

9.6.3 vminsert HA Pair 去重

# Prometheus HA Pair 两个实例写入同一 VM
# 使用 dedup 去重
vmselect -dedup.minScrapeInterval=15s
vmstorage -dedup.minScrapeInterval=15s
vminsert -dedup.minScrapeInterval=15s  # 写入端去重

9.7 容量规划

9.7.1 vmstorage 节点数

所需 vmstorage 数量取决于:
  1. 总活跃序列数
  2. 每节点建议的最大序列数
  3. 副本因子

公式:
  nodes = ceil(total_series / per_node_series * replication_factor)

示例:
  总序列数 = 1000 万
  每节点建议 = 500 万
  副本因子 = 2

  nodes = ceil(10,000,000 / 5,000,000 * 2) = 4 个节点

9.7.2 每组件资源配置

组件CPU内存磁盘网络
vminsert (每实例)2-4 核2-4 GB1 GB(日志)中等(写入带宽)
vmselect (每实例)4-8 核8-16 GB10-100 GB(缓存)高(查询带宽)
vmstorage (每节点)4-16 核16-64 GB按数据量中等

9.7.3 典型部署示例

中等规模(100 万序列,90 天保留)

# vmstorage × 3 (每节点 4 核 16 GB, 100 GB SSD)
vmstorage1: -storageDataPath=/data/vmstorage1 -retentionPeriod=90d -memory.allowedPercent=60
vmstorage2: -storageDataPath=/data/vmstorage2 -retentionPeriod=90d -memory.allowedPercent=60
vmstorage3: -storageDataPath=/data/vmstorage3 -retentionPeriod=90d -memory.allowedPercent=60

# vminsert × 2 (每实例 2 核 2 GB)
vminsert1: -storageNode=vmstorage1:8400,vmstorage2:8400,vmstorage3:8400
vminsert2: -storageNode=vmstorage1:8400,vmstorage2:8400,vmstorage3:8400

# vmselect × 2 (每实例 4 核 8 GB)
vmselect1: -storageNode=vmstorage1:8401,vmstorage2:8401,vmstorage3:8401
vmselect2: -storageNode=vmstorage1:8401,vmstorage2:8401,vmstorage3:8401

9.8 集群运维

9.8.1 滚动更新

# 更新 vmstorage(逐个节点)
# 1. 从 vminsert 和 vmselect 的 -storageNode 中移除目标节点
# 2. 等待当前请求完成(graceful shutdown)
# 3. 更新二进制
# 4. 重新启动
# 5. 将节点添加回 -storageNode

# 使用 Kubernetes 时,StatefulSet 的 rolling update 自动处理

9.8.2 节点替换

# 替换故障 vmstorage 节点
# 1. 确认副本因子 > 1(否则数据会丢失)
# 2. 停止故障节点
# 3. 部署新节点(空数据)
# 4. 新节点加入集群
# 5. vmstorage 会自动从副本节点恢复数据

本章小结

要点内容
vminsert无状态,接收写入并分片路由
vmselect近无状态,查询聚合+多级缓存
vmstorage有状态,持久化存储
多租户通过 AccountID/ProjectID 隔离
HA副本因子 + 多实例 + 去重

扩展阅读