我把 ELK 日志栈切到 VictoriaLogs 后,存储成本降了 70%:轻量可观测性改造实录
我把 ELK 日志栈切到 VictoriaLogs 后,存储成本降了 70%:轻量可观测性改造实录
说实话,我早就看我们的 ELK 堆栈不顺眼了。
倒不是 Elasticsearch 不好用,而是它越来越像一个“吞金兽”。三节点数据、一个主节点、Logstash、Kibana,光是热数据盘就挂了 45TB SSD。每天 1.5TB 日志进来,30 天保留期一卡,成本每个月都在账单上跳舞。更要命的是,某些全字段检索的查询 P99 能跑到 8 秒以上,on-call 同学一边查日志一边骂。
上个月我们把 80% 的日志流切到了 VictoriaLogs。迁移后单块存储降了 70%,集群节点从 4 台变成 1 台二进制进程,查询延迟稳了很多。这篇文章把整个过程、踩的坑和留下的 checklist 记下来,给同样被 ELK 账单压得喘不过气的同学参考。
背景:ELK 为什么成了成本黑洞
我们的日志链路比较标准:
- 业务服务 → Fluent Bit → Logstash → Elasticsearch
- Kibana 做查询和看板
- 3 个数据节点,每个 32C128G,挂载 15TB 高性能云盘
- 1 个专用主节点 + 1 个 Logstash 节点
每天大约 1.5TB 原始日志,保留 30 天。看起来中规中矩,但几个隐藏成本很扎心:
- 倒排索引占空间:为了支持全文检索,ES 对几乎所有字段建索引,磁盘占用经常是原始日志的 1.5 倍以上。
- JVM 内存贵:ES 是 Java 堆大户,数据节点堆内存配到 64GB 还经常被 GC 警告。
- 分片和节点规划麻烦:日志量一涨,就要考虑 rollover、索引模板、冷热分层,运维成本持续增加。
- 查询慢:业务同学爱用
message:*timeout*这种 wildcard 搜索,数据节点 CPU 直接拉满。
我们当时的念头很简单:能不能用更轻量的方案,只保留我们最需要的日志检索能力?
选型:为什么不是 Loki,而是 VictoriaLogs
其实 Loki 也考虑过。但 Loki 的 label 设计对我们的日志格式要求比较严格,而且当时我们没有 Grafana 全栈,迁移成本不低。后来同事推荐了 VictoriaLogs,我试了一周,发现它有几个特别对我们胃口的地方:
- 单二进制:一个可执行文件,没有 JVM、没有 ZooKeeper、没有复杂集群角色,部署简单到离谱。
- 压缩比高:它采用列式存储 + 轻量索引,存储占用大概只有 Elasticsearch 的 20%-30%。
- 生态兼容:Fluent Bit、Promtail、Vector 都能直接发数据;Grafana 有官方数据源插件。
- 查询语言接近 LogQL:
LogsQL上手很快,团队基本不用培训就能写。
当然也有取舍:复杂全文检索、聚合、嵌套文档支持不如 ES。但我们的日志场景主要是按服务、环境、日志级别过滤,然后看时间线和关键字,VictoriaLogs 完全够用。
迁移:三步走,没有停服
第一步:拉起 VictoriaLogs 实例
我们用 Docker Compose 部署,配置极简:
services:victorialogs:image:victoriametrics/victoria-logs:v1.5.0-victorialogscontainer_name:victorialogscommand:-"--storageDataPath=/vlogs"-"--retentionPeriod=30d"-"--httpListenAddr=:9428"-"--memory.allowedPercent=60"ports:-"9428:9428"volumes:-/data/vlogs:/vlogsrestart:unless-stopped相比 ES 的一堆 JVM 参数和角色配置,这个启动几乎可以说是“解压即用”。我们把数据目录挂到了一块成本更低的 SATA 盘上,准备用它扛主要日志流。
第二步:让 Fluent Bit 双写
不想停服,所以先让 Fluent Bit双写:一份继续给 ES,一份给 VictoriaLogs。等 VictoriaLogs 的查询和看板稳定后,再逐步关闭 ES 链路。
[OUTPUT] Name http Match app.* Host victorialogs Port 9428 URI /insert/jsonline Format json_lines Header Content-Type application/json Header AccountID 0 Header ProjectID 0 json_date_key timestamp json_date_format iso8601这里有几个细节:
json_date_format一定要统一成iso8601,我们刚开始用毫秒时间戳,VictoriaLogs 解析后时间戳乱了,查了半天。- 默认按
AccountID和ProjectID做多租户隔离,header 必须带,否则数据会进默认租户。 Match规则建议先按app.*分批切,不要一次性全切,方便回滚。
第三步:Grafana 数据源和查询改造
Grafana 装VictoriaLogs数据源后,之前的 Kibana 看板可以逐步迁。最常用的是下面两类查询。
按服务查最近 ERROR 日志:
service:order-service AND level:ERROR AND _time:1h按状态码统计失败请求:
service:order-service AND status_code:>=500 AND _time:30m | stats by (status_code) count()做趋势聚合:
service:payment-service AND level:ERROR | stats by (_time:1m) count()LogsQL 的语法和 LogQL 很像,业务同学基本 copy-paste 改改字段名就能用。唯一要适应的是没有 Kibana 那种点选式界面,一开始有些人不习惯,但写了三天 queries 后都真香了。
效果:账单和延迟都变了
迁移完成一个月后,我们对比了主要指标:
| 指标 | 迁移前 ELK | 迁移后 VictoriaLogs |
|---|---|---|
| 30 天日志存储占用 | 45TB | 13TB |
| 数据节点数 | 3 台 32C128G | 1 台 8C32G |
| 全文 wildcard 查询 P99 | 8-12s | 1-3s |
| 按 stream 字段过滤查询 | 2-5s | 200-500ms |
| 月度存储+计算成本 | 基准 100% | 约 30% |
存储成本降了大约 70%,主要来自三点:
- VictoriaLogs 的列式压缩和轻量索引让磁盘占用大幅下降。
- 单节点即可顶住我们的日志量,省了两台数据节点的计算成本。
- 不需要为全文检索预留大量 JVM 堆内存,内存成本也少了。
当然,这个 70% 是基于我们保留 30 天、以结构化日志为主的场景。如果你的日志全是非结构化文本、需要复杂聚合,数字会不一样。
踩过的 4 个坑
坑 1:高基数字段当 stream 字段,内存原地爆炸
VictoriaLogs 的stream字段(类似 Loki 的 label)会参与索引。如果直接把trace_id、request_id这种每条日志都不同的字段当 stream 字段,内存和查询性能会炸。
我们的做法:只把service、env、level、host作为 stream 字段,高基数字段保留在_msg里用 LogsQL 过滤。
service:order-service AND _msg:trace_id="abc123" AND _time:1h坑 2:时间戳格式不一致导致日志重复或缺失
Fluent Bit 默认json_date_format是double,VictoriaLogs 期望的是秒级 Unix 时间戳或 ISO8601。我们刚开始没统一,结果看到同一条日志出现了两次,或者某段时间完全空白。
统一改成iso8601后问题解决。这个配置建议在迁移前就定好,否则后面洗数据很痛苦。
坑 3:Kibana 的某些聚合看板没法直接平迁
VictoriaLogs 不支持 ES 那种多层嵌套聚合和复杂的bucket_script。我们有几个 Kibana 看板需要重新设计,改成预聚合指标(用 VictoriaMetrics 或自定义 exporter)+ LogsQL 简单统计的方式。
经验是:不要试图 1:1 复制 Kibana,先把最常用的看板迁了,其他的慢慢改。
坑 4:多租户隔离 header 忘记加,团队数据串了
VictoriaLogs 用AccountID和ProjectIDheader 做租户隔离。我们测试环境忘了加 header,结果测试数据混进了生产租户。虽然后来清理了,但最好在一开始就通过 Nginx 统一注入 header,避免各个客户端自己配置。
location /insert/ { proxy_pass http://victorialogs:9428; proxy_set_header AccountID $account_id; proxy_set_header ProjectID $project_id; }写在最后
VictoriaLogs 不是银弹。它牺牲了部分全文检索和复杂聚合能力,换来了极低的存储和运维成本。如果你的日志场景以“按服务/级别/时间过滤 + 关键字检索”为主,它很可能是比 ELK 更划算的解法。
我们现在的策略是:
- 80% 的常规应用日志进 VictoriaLogs;
- 需要复杂全文检索和安全审计的日志继续留在 Elasticsearch;
- 所有新服务默认对接 VictoriaLogs,老服务逐步迁移。
如果你也在被 ELK 账单追着跑,不妨先找一台闲置机器,拿一天的日志做下对比测试。数据不会骗人。
参考资料:
- VictoriaLogs 官方文档:https://docs.victoriametrics.com/victorialogs/
- Fluent Bit HTTP 输出插件:https://docs.fluentbit.io/manual/pipeline/outputs/http
- Grafana VictoriaLogs 数据源:https://docs.victoriametrics.com/victorialogs/grafana/