前言

几十台机器各自 tail -f 的日子该结束了。集中式日志的价值:一处搜索全部日志、跨服务排障关联、图表化趋势、留存审计。ELK 是这个领域的经典答案。

一、架构与组件分工

应用/中间件
   │  输出日志文件
   ▼
Filebeat(轻量采集器, 装在每台机器)
   │  汇聚 / 容错缓冲
   ▼
[可选] Kafka(削峰 / 解耦 / 丢不起的日志)
   │
   ▼
Logstash(解析: grok 拆字段、过滤、富化)
   │
   ▼
Elasticsearch(索引 + 全文检索)
   │
   ▼
Kibana(查询 / 可视化 / 告警)

组件选型说明:

组件 职责 备注
Filebeat 采集 Go 写的, 内存占用 ~30MB, 几乎无感
Logstash 解析转换 JVM 重(1G+内存); 简单场景可用 Filebeat 自带 processor 替代
Elasticsearch 存储检索 JVM 大胃王; 单机实验给 2G 堆即可
Kibana 界面 中规中矩 600MB 内存

二、快速搭建(单节点实验版)

# docker-compose.yml
services:
  es:
    image: elasticsearch:8.14.1
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false      # 实验方便, 生产必开!
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports: ["9200:9200"]
    volumes: [esdata:/usr/share/elasticsearch/data]

  kibana:
    image: kibana:8.14.1
    environment:
      - ELASTICSEARCH_HOSTS=http://es:9200
    ports: ["5601:5601"]
    depends_on: [es]

volumes:
  esdata:
docker compose up -d
# 浏览器打开 http://IP:5601 —— Kibana 就绪

三、Filebeat:装到每台被采集机

curl -L -o filebeat.rpm https://mirrors.tuna.tsinghua.edu.cn/elasticstack/8.x/yum/...
rpm -ivh filebeat.rpm

/etc/filebeat/filebeat.yml 核心配置:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log*
    - /var/log/nginx/error.log*
  fields:
    service: nginx
    env: production
  fields_under_root: true

output.elasticsearch:           # 简单场景: 直连 ES
  hosts: ["http://es-node:9200"]
  index: "nginx-%{+yyyy.MM.dd}"

# 简单解析不劳 Logstash, Filebeat 自带模块:
filebeat.modules:
- module: nginx
  access: {var.paths: ["/var/log/nginx/access.log*"]}
systemctl enable --now filebeat
# Kibana -> Discover 创建 data view: nginx-* 即可检索!

四、结构化解析(Logstash 或 Ingest Pipeline)

Nginx 日志拆成字段才能按状态码/接口统计。推荐 ES Ingest Pipeline(省掉 Logstash):

PUT _ingest/pipeline/nginx-parse
{
  "processors": [
    {"grok": {
      "field": "message",
      "patterns": [
        "%{IP:client_ip} - \\S+ \\[%{HTTPDATE:ts}\\] \"%{WORD:method} %{URIPATHPARAM:uri} HTTP/%{NUMBER:http_ver}\" %{NUMBER:status:int} %{NUMBER:bytes:int} \"%{DATA:referer}\" \"%{DATA:ua}\" rt=%{NUMBER:rt:float}"
      ]
    }},
    {"date": {"field": "ts", "formats": ["dd/MMM/yyyy:HH:mm:ss Z"]}},
    {"remove": {"field": ["ts", "message"]}}
  ]
}
# filebeat.yml 里挂上 pipeline
output.elasticsearch:
  hosts: ["http://es:9200"]
  pipeline: nginx-parse

检索验证:

GET nginx-*/_search
{
  "query": {"match": {"status": 500}},
  "sort": [{"rt": "desc"}]
}

五、Kibana 实用操作

Discover      按时间轴 + 条件过滤(KQL): status:500 and service:nginx
             uri:/api/* and rt>1
Visualize     画图: 状态码饼图 / 每分钟 5xx 趋势线 / Top10 慢接口(数据表)
Dashboard     拼大盘: 一屏看完服务健康度
Alerting      告警: "5分钟内 status:500 超过 50 次" → 邮件/webhook

高频 KQL:

message: "connection refused"          # 全文
status >= 500                          # 数值比较
not service: nginx                     # 排除
service:(nginx OR mysql)               # 多值

六、索引生命周期(ILM):日志的生老病死

日志无限增长会撑爆磁盘,ILM 自动分阶段管理:

PUT _ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot":    {"actions": {}},
      "warm":   {"min_age": "1d",  "actions": {"shrink": {"number_of_shards": 1}}},
      "delete": {"min_age": "30d", "actions": {"delete": {}}}
    }
  }
}

hot(当天,可查询)→ warm(1天后收缩副本降配)→ delete(30天删除)。任何 ELK 上线都必须配 ILM,这是磁盘的保命符。

七、规模化的中间形态

< 10 GB/天:    Filebeat → ES 直连(本文方案)
10-100 GB/天:  Filebeat → Logstash → ES(解析富化)
> 100 GB/天:   Filebeat → Kafka → Logstash 集群 → ES 集群

Kafka 层的价值:削峰(日志洪峰不冲垮 ES)、缓冲(ES 故障时日志不丢)、多消费(同时给实时告警和离线数仓)。

八、替代方案速览

方案 特点 适合
ELK 功能全, 生态最广, 资源吃得多 全文检索刚需、复杂分析
Loki + Grafana 只索引标签不索引全文, 便宜 10 倍 日志量随查询少的团队(云原生新宠)
ClickHouse 列存 SQL 分析, 极快 结构化日志分析为主
云托管 阿里云 SLS / 腾讯云 CLS 不想自运维

我的建议:中小团队直接 Loki(与 Grafana 监控同一面板),除非你重度依赖全文检索。

九、常见坑

现象 原因
Filebeat 收不到日志 权限(nginx 日属 root);paths 通配写错
ES 起不来 vm.max_map_count=262144(sysctl)
Kibana 无数据 data view 的 index pattern 没建/时间范围不对
磁盘水位锁只读 _disk watermark 触发 → 清索引 + 扩容;先配 ILM 预防
日志乱序/丢失 加 Kafka 层;确认 filebeat registry 正常推进

小结

层 组件 核心配置
采集 Filebeat inputs.paths + fields
解析 Ingest/Logstash grok 拆字段 + date 时间
存储 ES 索引模板 + ILM 生命周期
使用 Kibana Discover KQL + Dashboard + Alerting

本文是「云原生」系列第 11 篇。