日志收集后端:Loki vs Elasticsearch(成本 vs 功能)

起因

应用日志几个 GB / day 起,需要:

  • 集中查询(多机器日志一个地方搜)
  • 告警(错误率 spike)
  • 长期保留(compliance / debug 历史)

主流方案:

  • ELK / OpenSearch:Elasticsearch + Logstash + Kibana
  • Loki + Grafana:Grafana Labs 出,"日志的 Prometheus"
  • Datadog / NewRelic:SaaS 全套,贵但省事

Loki 跟 ES 的核心差异:Loki 只索引 metadata,不索引日志正文
存储成本 10-100x 低,但全文搜索弱。

ES 方式

[app] → filebeat → [Logstash] → [Elasticsearch (集群)] → [Kibana]

每条日志 parse + 索引每个 token:

ts: "2025-03-14T10:23:00Z"
level: "ERROR"
service: "api"
msg: "Failed to connect to db at host pg-1, error: timeout"
trace_id: "abc123"

索引后:

  • 任意 token 模糊搜索 < 100ms
  • 字段聚合 / 统计快
  • Kibana 仪表盘 / 告警丰富

代价:

  • 存储 5-10x 原日志 size(倒排索引膨胀)
  • ES 集群 RAM 重(每 node 8-32 GB)
  • 索引 CPU 高
  • 1 TB/day 日志 → 10-100 TB 索引存储

Loki 方式

[app] → Promtail / Fluent Bit → [Loki] → [Grafana]

日志按 stream 存(compressed chunk),只索引 stream label:

labels: {service="api", level="ERROR", instance="api-1"}
log line: "2025-03-14T10:23:00Z Failed to connect..."

查:

{service="api", level="ERROR"} |= "timeout"
  • 先按 label 过滤 → 找到匹配的 chunk
  • 全文 grep chunk 内日志

结果:

  • 存储 1-2x 原 size(gzip 压缩,无倒排)
  • 单实例几 GB RAM 撑大量数据
  • 全文搜限定 label 范围内 grep(不是全局倒排)

实测对比

我们一个项目 100 GB/day 日志:

ELK Loki
存储 (30 day retention) 30 TB 3 TB
节点数 5 × 16GB ES + 3 logstash 2 × 8GB Loki
月成本 (AWS) ~$3500 ~$400
简单 query < 100ms 1-3s
复杂搜索 中(label-bound)
仪表盘 Kibana 强 Grafana 强
告警 watcher / ElastAlert Loki alert rule

Loki 十分之一存储成本 + 十分之一计算成本
搜索体验稍弱但够用。

label 设计是关键

Loki 的 label 不能 high-cardinality(如 user_id / trace_id 不行)。
原则:

  • low cardinality(< 100k unique values):service / instance / level / env
  • 把 high cardinality 数据放 log line 里(grep 找)
✅ {service="api", level="ERROR"}                   # 几十个 service × 4 level
❌ {user_id="12345"}                                # 百万 user → label 爆炸

high cardinality label → Loki 索引膨胀 → 慢 + 内存 OOM。

LogQL

类 PromQL:

# 简单
{service="api"} |= "error"

# 多条件
{service=~"api|worker", level="ERROR"} |~ "timeout|connection refused"

# rate (metric from logs)
rate({service="api", level="ERROR"}[5m])

# parse json + filter
{service="api"} | json | status >= 500

|= 包含, |~ regex, != / !~ 排除。

部署

Loki

# docker-compose.yml
services:
  loki:
    image: grafana/loki:3.0.0
    ports:
      - 3100:3100
    volumes:
      - ./loki-config.yml:/etc/loki/local-config.yaml
      - loki-data:/loki

  promtail:
    image: grafana/promtail:3.0.0
    volumes:
      - /var/log:/var/log
      - ./promtail-config.yml:/etc/promtail/config.yml
# promtail-config.yml
clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        target_label: 'container'

Grafana 连 Loki

Grafana 加 Loki data source http://loki:3100。Explore 直接查。

chunked storage

Loki 支持 S3 / GCS / 本地:

storage_config:
  aws:
    s3: s3://my-bucket/loki
    region: us-east-1

老数据存 S3(几 $0.02/GB/月)→ 长期保留极便宜。
查询时 Loki 拉对应 chunk 解压扫。

alert rule

groups:
  - name: api-alerts
    rules:
      - alert: HighErrorRate
        expr: sum(rate({service="api", level="ERROR"}[5m])) > 10
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "API error rate > 10/s for 5min"

Loki ruler 跑 alert,发 alertmanager → PagerDuty / Slack。

与 SaaS 对比

  • Datadog logs:1 GB/day 跑 $30/月 起,100 GB/day = $3000+/月
  • New Relic:类似
  • Self-host Loki:100 GB/day = $400/月

scale 上 SaaS 贵 10x+。中小 scale 时间成本 vs 现金成本 trade-off。

何时仍选 ES

  • 全文搜索是核心(不只是 grep):日志做"搜索引擎",ES 倒排是杀手锏
  • 复杂聚合 / OLAP-like 分析:ES aggregation 比 LogQL 强
  • 业务已有 Kibana dashboard 多:迁移成本高
  • 多种数据 type 混搜(log + APM trace + metric 一处)

与 Quickwit 对比

Quickwit 是另一选项:log 优化 + S3-native + 全文搜索(用 tantivy / Rust)。
比 ES 便宜 + 比 Loki 搜索强。

ES Loki Quickwit
索引 full label only full + S3-native
存储成本 极低
全文搜 极强
部署 复杂 简单

新项目 Quickwit 在评估,但 Loki 仍是 mainstream 简单选择。

我的选择

  • 新项目,云原生 → Loki(成本 + Grafana 一体化)
  • 既有 ES 团队 → 保留 ES
  • 极致全文搜需求 → ES
  • 超大 scale + 成本敏感 → Quickwit

踩过的坑

  1. label cardinality 爆:把 trace_id 放 label → 几百万 stream →
    Loki OOM。max_streams_per_user 限制。

  2. chunk size 配置:默认 1.5 MB chunk,业务量大改 5 MB 减 IO。

  3. regex 慢|~ "complex.*regex" 全 chunk 扫。先 label filter
    |= 关键词 prefilter,最后 regex。

  4. retention 策略:默认 chunks 不删。配 compactor + retention
    policy。

  5. promtail 漏 log:log file rotate 时 inode 变 → promtail 没
    follow。stat_config 调 polling。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。