概述

在可观测性的三大支柱(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数值支持范围查询和聚合
ipIP 地址支持 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": {}
        }
      }
    }
  }
}
阶段时间操作目的
Hot0-7 天Rollover(滚动创建新索引)高性能写入和查询
Warm7-30 天Shrink(缩减分片)+ Force Merge减少资源占用
Cold30-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 GB11
每日日志 5-50 GB2-31
每日日志 50-200 GB5-101
每日日志 > 200 GB10+ 或用热温冷架构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 / LuceneLogQL
资源消耗高(JVM,内存大)低(Go,内存友好)
部署复杂度高(ES 集群 + JVM 调优)低-中
适用场景全文检索、复杂分析日志监控、排障定位
生态集成自带 KibanaGrafana 生态

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,需要两者兼备时混合部署。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Elastic 官方文档 — Elastic,参考了Elastic 官方文档相关内容