起因
应用日志几个 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
踩过的坑
-
label cardinality 爆:把 trace_id 放 label → 几百万 stream →
Loki OOM。max_streams_per_user限制。 -
chunk size 配置:默认 1.5 MB chunk,业务量大改 5 MB 减 IO。
-
regex 慢:
|~ "complex.*regex"全 chunk 扫。先 label filter
再|=关键词 prefilter,最后 regex。 -
retention 策略:默认 chunks 不删。配
compactor+ retention
policy。 -
promtail 漏 log:log file rotate 时 inode 变 → promtail 没
follow。stat_config调 polling。
登录后参与评论。