概述
在可观测性的三大支柱(Metrics、Logs、Traces)中,日志是最贴近应用层的数据。当线上服务出现异常,第一反应往往是"看日志"。ELK Stack(Elasticsearch + Logstash + Kibana)是日志分析领域的事实标准,在全文检索、日志解析、可视化分析方面功能强大。
随着 Loki 等"轻量级"日志方案兴起,ELK 面临着"存储成本高、运维复杂"的质疑。但 ELK 在全文检索、复杂文本分析、结构化日志聚合等方面的能力仍然无可替代。从架构原理到部署配置,详细梳理 ELK 日志分析平台的建设实践,并与 Loki 做对比分析,帮助你判断何时该选 ELK、何时该选 Loki。
参考来源:Elastic 官方文档
一、ELK Stack 架构
1.1 整体架构
┌──────────────────────────────────────────────────────────────┐
│ ELK Stack 完整架构 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App/Node│ │ App/Node│ │ App/Node│ ← 日志源 │
│ │ log file│ │ log file│ │ log file│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
│ │Filebeat │ │Filebeat │ │Filebeat │ ← 轻量采集器 │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └────────────┼────────────┘ │
│ ▼ │
│ ┌────────────┐ │
│ │ Logstash │ ← 日志解析/过滤/转换 │
│ │ (可选) │ │
│ └──────┬─────┘ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Elasticsearch 集群 │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │Node1│ │Node2│ │Node3│ ← 存储 + 检索│ │
│ │ │(数据)│ │(数据)│ │(主从)│ │ │
│ │ └─────┘ └─────┘ └─────┘ │ │
│ └──────────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │ Kibana │ ← 可视化 + 查询 │
│ └────────────┘ │
└──────────────────────────────────────────────────────────────┘
1.2 组件职责
| 组件 | 职责 | 语言 | 特点 |
|---|---|---|---|
| Filebeat | 日志采集 | Go | 轻量,资源消耗低,替代旧版 Logstash 采集端 |
| Logstash | 日志解析/转换 | JRuby | 功能强大,但内存消耗高 |
| Elasticsearch | 存储 + 检索 | Java | 全文搜索引擎,分布式 |
| Kibana | 可视化 | Node.js | 查询、仪表盘、告警 |
1.3 简化架构:Filebeat → Elasticsearch
现代 ELK 架构中,Logstash 通常被精简。Filebeat 自带处理能力(Ingest Node),可以直接写入 Elasticsearch:
日志文件 → Filebeat (采集 + 基础处理) → Elasticsearch (Ingest Pipeline) → Kibana
只有在需要复杂解析(如 Grok 多行解析、多源数据 enrichment)时才使用 Logstash。
二、Elasticsearch 索引管理
2.1 索引设计
Elasticsearch 中的索引(Index)类似于数据库中的表。日志数据通常按日期创建索引:
索引命名: logs-app-2026.07.10
│ │ └─ 日期(每天一个索引)
│ └────── 应用名
└──────────── 前缀
按日期分索引的优势:
- 易于按时间范围查询(只查询相关索引)
- 便于 ILM(索引生命周期管理)
- 删除旧数据只需删除整个索引
2.2 索引模板
索引模板(Index Template)定义了索引的 mapping、settings,自动应用到匹配名称模式的新索引:
PUT _index_template/logs-app
{
"index_patterns": ["logs-app-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.refresh_interval": "5s",
"index.lifecycle.name": "logs-ilm-policy",
"index.lifecycle.rollover_alias": "logs-app"
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"level": { "type": "keyword" },
"service": { "type": "keyword" },
"host": { "type": "keyword" },
"message": { "type": "text", "analyzer": "standard" },
"request_id": { "type": "keyword" },
"duration_ms": { "type": "integer" },
"status_code": { "type": "integer" },
"url": { "type": "keyword" }
}
}
},
"priority": 100
}
字段类型选择原则:
| 类型 | 适用场景 | 说明 |
|---|---|---|
keyword | 精确匹配、聚合、排序 | 不分词,如 service 名、level |
text | 全文检索 | 分词后索引,如 message |
date | 时间字段 | 支持时间范围查询 |
integer/long | 数值 | 支持范围查询和聚合 |
ip | IP 地址 | 支持 CIDR 查询 |
object | 嵌套 JSON | 默认类型 |
flattened | 动态 JSON | 减少字段爆炸 |
关键建议:将需要精确匹配和聚合的字段设为
keyword,需要全文检索的才设为text。错误地将高基数字段设为text会导致索引膨胀严重。
2.3 索引生命周期管理(ILM)
ILM 自动管理索引从创建到删除的全生命周期:
PUT _ilm/policy/logs-ilm-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_age": "1d",
"max_primary_shard_size": "50gb"
},
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 },
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
| 阶段 | 时间 | 操作 | 目的 |
|---|---|---|---|
| Hot | 0-7 天 | Rollover(滚动创建新索引) | 高性能写入和查询 |
| Warm | 7-30 天 | Shrink(缩减分片)+ Force Merge | 减少资源占用 |
| Cold | 30-90 天 | Freeze(冻结索引) | 节省内存,查询变慢 |
| Delete | > 90 天 | Delete(删除索引) | 释放磁盘空间 |
2.4 分片策略
分片数量直接影响查询性能和资源消耗:
// 查看索引分片分布
GET _cat/shards/logs-app-*?v
// 查看分片大小
GET _cat/indices/logs-app-*?v&h=index,pri,rep,docs.count,store.size,pri.store.size
分片设计原则:
- 每个分片大小控制在 30-50 GB
- 分片数 = 预估日志量 / 50GB
- 副本数:生产环境至少 1
- 单节点分片数不超过 20/GB 堆内存(如 32GB 堆 → 最多 640 分片)
三、Filebeat 日志采集
3.1 Filebeat 架构
┌──────────────────────────────────────────────┐
│ Filebeat │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Input │ │ Input │ │ Input │ │
│ │ (log) │ │ (stdin) │ │ (tcp) │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Harvester (采集器) │ │
│ │ 每个文件一个 Harvester,逐行读取 │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Spooler (事件池) │ │
│ │ 聚合事件,批量发送 │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Output (输出) │ │
│ │ Elasticsearch / Logstash / etc │ │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────────────┘
3.2 Filebeat 配置
# filebeat.yml
filebeat.inputs:
# 采集 Nginx 访问日志
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
service: nginx
env: production
fields_under_root: true
multiline:
pattern: '^\d{4}-\d{2}-\d{2}' # 匹配行首日期
negate: true
match: after # 不匹配的行追加到上一行
# 采集应用 JSON 日志
- type: log
enabled: true
paths:
- /var/log/app/*.json
fields:
service: my-app
env: production
json.keys_under_root: true # JSON 字段提升到顶层
json.add_error_key: true # 解析失败添加 error 字段
json.message_key: message # 指定 message 字段用于多行
# 采集容器日志(Docker)
- type: container
enabled: true
paths:
- /var/lib/docker/containers/*/*.log
stream: all
cri: parse
# 输出到 Elasticsearch
output.elasticsearch:
hosts: ["es-01:9200", "es-02:9200", "es-03:9200"]
index: "logs-%{[service]}-%{+yyyy.MM.dd}"
username: "elastic"
password: "${ES_PASSWORD}"
ssl.certificate_authority: ["/etc/filebeat/ca.crt"]
# 索引模板
setup.template:
name: "logs-app"
pattern: "logs-*-%{+yyyy.MM.dd}*"
enabled: true
# Kibana 仪表盘(可选)
setup.kibana:
host: "kibana:5601"
# 处理器
processors:
- add_host_metadata: ~ # 添加主机元数据
- add_cloud_metadata: ~ # 添加云元数据
- add_docker_metadata: ~ # 添加 Docker 元数据
- drop_fields:
fields: ["agent.ephemeral_id", "agent.id", "agent.type", "agent.version"]
ignore_missing: true
# 调优
queue.mem:
events: 4096 # 内存队列事件数
flush.min_events: 2048 # 最小批量
flush.timeout: 1s # 批量超时
logging.level: info
logging.to_files: true
3.3 多行日志处理
Java 异常堆栈是最常见的多行日志场景:
multiline:
# 匹配时间开头的行作为新日志起始
pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
negate: true
match: after
timeout: 5s # 超时后强制发送当前多行事件
处理效果:
原始日志:
2026-07-10 10:00:00 ERROR NullPointerException
at com.example.Service.handle(Service.java:45)
at com.example.Controller.process(Controller.java:23)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
合并后:
2026-07-10 10:00:00 ERROR NullPointerException
at com.example.Service.handle(Service.java:45)
at com.example.Controller.process(Controller.java:23)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
四、Logstash 日志解析
4.1 Logstash Pipeline
当 Filebeat 的 Ingest 能力不足以处理复杂日志时,引入 Logstash 做深入分析:
Filebeat → Logstash (Input → Filter → Output) → Elasticsearch
# logstash.conf
input {
beats {
port => 5044
}
}
filter {
# 解析 Nginx 访问日志
if [service] == "nginx" {
grok {
match => {
"message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:url} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code} %{NUMBER:bytes} "%{DATA:referrer}" "%{DATA:user_agent}" rt=%{NUMBER:request_time}'
}
overwrite => ["message"]
}
# 从 User-Agent 提取浏览器/操作系统
useragent {
source => "user_agent"
target => "ua"
}
}
# 解析 JSON 格式应用日志
if [service] == "my-app" {
json {
source => "message"
target => "app"
}
# 转换字段类型
mutate {
convert => {
"[app][duration_ms]" => "integer"
"[app][status_code]" => "integer"
}
}
}
# 通用处理
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
# GeoIP 解析(从 IP 提取地理位置)
geoip {
source => "client_ip"
target => "geo"
}
# 移除不需要的字段
mutate {
remove_field => ["user", "agent", "ecs", "input", "log"]
}
}
output {
elasticsearch {
hosts => ["es-01:9200", "es-02:9200", "es-03:9200"]
index => "logs-%{[service]}-%{+YYYY.MM.dd}"
user => "elastic"
password => "${ES_PASSWORD}"
ssl_certificate_verification => false
}
}
4.2 Grok 模式
Grok 是 Logstash 最强大的日志解析工具,本质是预定义的命名正则:
# 常用 Grok 模式
%{IP:ip} # 匹配 IP 地址
%{WORD:method} # 匹配单词
%{NUMBER:status} # 匹配数字
%{HTTPDATE:timestamp} # 匹配 HTTP 日期格式
%{IPORHOST:host} # 匹配 IP 或主机名
%{DATA:path} # 匹配非贪婪数据
%{GREEDYDATA:message} # 匹配贪婪数据
自定义 Grok 模式:
# 在 patterns/ 目录下定义
# nginx_patterns
NGINX_ACCESS %{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:url} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code} %{NUMBER:bytes}
# 在 filter 中使用
grok {
patterns_dir => ["/etc/logstash/patterns"]
match => { "message" => "%{NGINX_ACCESS}" }
}
4.3 Ingest Pipeline:替代 Logstash
Elasticsearch 5.0+ 引入 Ingest Node,可以在 ES 内部做日志处理,无需 Logstash:
PUT _ingest/pipeline/logs-pipeline
{
"description": "日志解析管道",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"%{IPORHOST:client_ip} %{WORD:method} %{URIPATHPARAM:url} %{NUMBER:status_code} %{NUMBER:duration_ms}"
]
}
},
{
"convert": {
"field": "status_code",
"type": "integer"
}
},
{
"convert": {
"field": "duration_ms",
"type": "integer"
}
},
{
"geoip": {
"field": "client_ip",
"target_field": "geo"
}
},
{
"date": {
"field": "@timestamp",
"formats": ["ISO8601"]
}
}
],
"on_failure": [
{
"set": {
"field": "tags",
"value": "parse-failed"
}
}
]
}
Filebeat 直接指定 pipeline:
output.elasticsearch:
hosts: ["es:9200"]
pipeline: "logs-pipeline"
建议:优先使用 Ingest Pipeline,只有在需要复杂 enrichment(如查数据库)或多输出目标时才引入 Logstash。
五、Kibana 可视化
5.1 Discover:日志搜索
Kibana Discover 是日志查询的核心界面:
KQL (Kibana Query Language) 查询示例:
# 精确匹配
service: "nginx" and level: "ERROR"
# 全文搜索
message: "NullPointerException"
# 范围查询
status_code >= 500 and status_code < 600
# 时间范围
@timestamp >= "2026-07-10T00:00:00" and @timestamp < "2026-07-11T00:00:00"
# 组合查询
service: "my-app" and (level: "ERROR" or level: "WARN") and duration_ms > 1000
5.2 Dashboard:仪表盘
创建常用的日志分析仪表盘:
| 可视化类型 | 用途 | 示例 |
|---|---|---|
| Line Chart | 时间趋势 | 请求量/错误率随时间变化 |
| Pie Chart | 占比分布 | 按服务/状态码分布 |
| Data Table | 明细列表 | 慢请求 Top 20 |
| Metric | 关键数字 | 今日总请求数、错误率 |
| Tile Map | 地理分布 | 访问来源 IP 地理分布 |
| Tag Cloud | 关键词 | 日志中高频关键词 |
| Gauge | 仪表盘 | 实时错误率 |
5.3 Kibana 告警
Kibana 7.x+ 内置告警功能(Alerting),可以基于 ES 查询创建告警规则:
POST /api/alerts/rule
{
"name": "High Error Rate",
"consumer": "alerts",
"rule_type_id": ".es-query",
"params": {
"query": [
{
"filter": {
"bool": {
"filter": [
{ "term": { "level": "ERROR" } }
]
}
},
"timeWindowSize": 300,
"timeWindowUnit": "s"
}
],
"size": 100,
"threshold": [
{ "comparator": ">", "threshold": [10] }
],
"index": ["logs-*"]
},
"actions": [
{
"id": "webhook-action",
"params": { "message": "过去 5 分钟错误日志超过 10 条" }
}
]
}
六、性能优化
6.1 Elasticsearch 性能调优
JVM 堆内存:
# jvm.options
-Xms31g # 初始堆 = 最大堆,避免动态调整
-Xmx31g # 不超过物理内存的 50%,留一半给 Lucene 文件缓存
-XX:+UseG1GC # 使用 G1 垃圾回收器
-XX:MaxGCPauseMillis=200
索引刷新间隔:
// 写入密集时降低刷新频率
PUT logs-app-*/_settings
{
"index.refresh_interval": "30s" // 默认 1s,改为 30s 减少写入压力
}
批量写入:
POST /_bulk
{ "index": { "_index": "logs-app-2026.07.10" } }
{ "@timestamp": "2026-07-10T10:00:00Z", "level": "INFO", "message": "..." }
{ "index": { "_index": "logs-app-2026.07.10" } }
{ "@timestamp": "2026-07-10T10:00:01Z", "level": "ERROR", "message": "..." }
批量写入比单条写入效率高 10-100 倍,建议每批 5-15 MB。
分片和副本:
| 场景 | 分片数 | 副本数 |
|---|---|---|
| 每日日志 < 5 GB | 1 | 1 |
| 每日日志 5-50 GB | 2-3 | 1 |
| 每日日志 50-200 GB | 5-10 | 1 |
| 每日日志 > 200 GB | 10+ 或用热温冷架构 | 1-2 |
6.2 Filebeat 性能调优
# filebeat.yml 调优
queue.mem:
events: 8192 # 增大队列
flush.min_events: 4096 # 增大批量
flush.timeout: 1s
output.elasticsearch:
worker: 4 # 并发写入 worker
bulk_max_size: 2048 # 每批最大文档数
# 调整 harvester 数量
filebeat.inputs:
- type: log
paths: ["/var/log/app/*.log"]
harvester_buffer_size: 16384 # 读取缓冲区
max_bytes: 10485760 # 单行最大字节(10MB)
6.3 存储优化
| 优化手段 | 效果 | 说明 |
|---|---|---|
| Force Merge | 减少段数量 | warm 阶段合并为 1 个段 |
| Shrink | 减少分片数 | warm 阶段缩减到 1 分片 |
| Freeze | 节省内存 | cold 阶段冻结索引 |
| Best Compression | 减少存储 | 使用 DEFLATE 压缩 |
| 删除不需要字段 | 减少存储 | mapping 中排除无用字段 |
// 使用 best_compression 压缩
PUT logs-app-*/_settings
{
"index": {
"codec": "best_compression"
}
}
七、ELK vs Loki 对比
7.1 设计理念对比
| 维度 | ELK (Elasticsearch) | Loki |
|---|---|---|
| 索引方式 | 全文倒排索引 | 仅索引标签,正文不索引 |
| 存储成本 | 高(索引膨胀 3-5x) | 低(仅标签索引 + 压缩正文) |
| 查询能力 | 全文检索、复杂聚合 | 标签过滤 + 正则匹配 |
| 查询语言 | KQL / Lucene | LogQL |
| 资源消耗 | 高(JVM,内存大) | 低(Go,内存友好) |
| 部署复杂度 | 高(ES 集群 + JVM 调优) | 低-中 |
| 适用场景 | 全文检索、复杂分析 | 日志监控、排障定位 |
| 生态集成 | 自带 Kibana | Grafana 生态 |
7.2 成本对比
假设:每日 100GB 日志,保留 30 天
ELK 方案:
存储量: 100GB × 3-5 (索引膨胀) × 30 天 = 9-15 TB
服务器: 3-5 台 ES 节点 (32GB RAM, 4TB SSD each)
月成本: ~$2,000-4,000
Loki 方案:
存储量: 100GB × 0.1 (仅标签+压缩) × 30 天 = 300 GB
服务器: 1-2 台 Loki 节点 + S3 对象存储
月成本: ~$200-500
7.3 何时选 ELK
- 需要全文检索能力(搜索日志正文中的任意关键词)
- 需要复杂聚合分析(按多个维度交叉统计)
- 需要结构化日志的深度分析(如 API 请求分析)
- 日志中包含大量文本内容需要分词搜索
- 团队已有 ES 运维经验
7.4 何时选 Loki
- 主要需求是日志监控和排障定位
- 需要与 Grafana / Prometheus 联动
- 对存储成本敏感
- 日志量大但查询不频繁
- 团队偏好轻量级方案
建议:如果不确定,先用 Loki 起步。Loki 能满足 80% 的日志场景需求,成本仅为 ELK 的 1/10。当全文检索需求明确时再引入 ELK。
八、生产部署实践
8.1 集群拓扑
┌─── 生产环境 ELK 集群 ──────────────────────────┐
│ │
│ Hot Nodes (3 × 64GB RAM, 4TB NVMe SSD) │
│ ├── 近 7 天索引 │
│ └── 高写入和高查询 │
│ │
│ Warm Nodes (2 × 32GB RAM, 8TB HDD) │
│ ├── 7-30 天索引 │
│ └── 只读,低查询频率 │
│ │
│ Cold Nodes (1 × 16GB RAM, 16TB HDD) │
│ ├── 30-90 天索引(冻结状态) │
│ └── 偶尔查询 │
│ │
│ Coordinator Nodes (2 × 8GB RAM) │
│ └── 查询路由 + 聚合 │
│ │
│ Kibana (2 × 4GB RAM) │
│ └── 负载均衡 │
└────────────────────────────────────────────────┘
8.2 Docker Compose 部署
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms2g -Xmx2g
- xpack.security.enabled=false
- cluster.name=elk-cluster
- bootstrap.memory_lock=true
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
kibana:
image: docker.elastic.co/kibana/kibana:8.14.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
filebeat:
image: docker.elastic.co/beats/filebeat:8.14.0
user: root
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
depends_on:
- elasticsearch
volumes:
es_data:
8.3 监控 ELK 自身
# Prometheus 采集 ES 指标
scrape_configs:
- job_name: 'elasticsearch'
static_configs:
- targets: ['es-01:9208']
metrics_path: /metrics
关键告警规则:
groups:
- name: elasticsearch
rules:
- alert: ElasticsearchClusterHealthRed
expr: elasticsearch_cluster_health_status{color="red"} == 1
for: 5m
labels:
severity: critical
annotations:
summary: "ES 集群状态为 RED"
- alert: ElasticsearchDiskSpaceLow
expr: |
1 - (elasticsearch_filesystem_data_available_bytes /
elasticsearch_filesystem_data_size_bytes) > 0.85
for: 10m
labels:
severity: warning
annotations:
summary: "ES 磁盘空间不足: {{ $labels.instance }}"
- alert: ElasticsearchJVMHeapHigh
expr: |
elasticsearch_jvm_memory_used_bytes{area="heap"} /
elasticsearch_jvm_memory_max_bytes{area="heap"} > 0.85
for: 10m
labels:
severity: warning
annotations:
summary: "ES JVM 堆使用率过高: {{ $labels.instance }}"
总结
ELK Stack 经过多年发展,在日志分析领域依然是功能最全面的方案:
- Elasticsearch 的全文检索能力无可替代——当你需要搜索日志正文中的任意关键词时,ELK 是唯一的选择
- 索引管理 是 ELK 的核心——合理的 mapping、ILM 策略、分片规划直接决定集群性能和成本
- Filebeat + Ingest Pipeline 是现代 ELK 的推荐采集架构——比传统 Logstash 更轻量,只有复杂解析才需要 Logstash
- 性能优化 需要系统调优——JVM 堆、刷新间隔、批量写入、分片策略、压缩算法缺一不可
- 成本是 ELK 的主要短板——全文索引导致存储膨胀 3-5 倍,大规模日志场景下成本显著高于 Loki
- 与 Loki 互补而非互斥——全文检索用 ELK,日志监控用 Loki,两者可以在同一 Grafana 中共存
选择 ELK 还是 Loki,取决于你的核心需求是"全文搜索"还是"日志监控"。前者选 ELK,后者选 Loki,需要两者兼备时混合部署。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Elastic 官方文档 — Elastic,参考了Elastic 官方文档相关内容