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/write | Protobuf 格式 |
| Prometheus import | /api/v1/import/prometheus | 文本格式 |
| InfluxDB Line Protocol | /api/v1/import/influx/write | InfluxDB 格式 |
| OpenTSDB | /api/v1/import/opentsdb | OpenTSDB JSON 格式 |
| OpenTSDB telnet | /api/v1/import/opentsdb/telnet | OpenTSDB telnet 格式 |
| Graphite | /api/v1/import/graphite | Graphite 格式 |
| CSV | /api/v1/import/csv | CSV 格式 |
| Native | /api/v1/import/native | VM 原生格式 |
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 宕机,数据仍然完整
| 副本因子 | 容错能力 | 存储开销 | 适用场景 |
|---|
| 1 | 0 节点 | 1x | 开发测试 |
| 2 | 1 节点 | 2x | 生产推荐 |
| 3 | 2 节点 | 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 GB | 1 GB(日志) | 中等(写入带宽) |
| vmselect (每实例) | 4-8 核 | 8-16 GB | 10-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 | 副本因子 + 多实例 + 去重 |
扩展阅读