日志采集架构:ELK/EFK架构详解、Filebeat与Logstash的区别、Kafka在日志链路中的作用

聊到日志采集,很多团队上来就搭一套ELK。嗯,这个方向没错,但你真的理解每一层在干什么吗?我见过不少项目,日志量一上来,整个链路直接崩掉。说白了,架构设计没想清楚。

今天咱们就把日志采集链路拆开揉碎,从ELK/EFK的架构差异,到Filebeat和Logstash怎么选,再到Kafka为什么成了日志链路的标配。我会结合自己踩过的坑,给你讲明白。

一、ELK vs EFK:核心差异在哪?

先看经典ELK:Elasticsearch + Logstash + Kibana。这套组合用了很多年,成熟稳定。但后来大家发现Logstash吃内存太狠了,尤其是日志量大的时候,动不动就OOM。

于是EFK出现了:Elasticsearch + Filebeat + Kibana。把Logstash换成了更轻量的Filebeat。你想想看,Filebeat是用Go写的,资源占用比Logstash小一个量级。

核心区别一句话:Logstash是重量级采集+处理,Filebeat是轻量级采集+转发。

我个人习惯这样选型:

  • 日志量小于100GB/天:直接用ELK,Logstash扛得住,功能也全
  • 日志量100GB-1TB/天:EFK + 轻量处理,Filebeat采集,Logstash只做过滤
  • 日志量超过1TB/天:必须上Kafka,Filebeat采集 -> Kafka缓冲 -> Logstash/ES消费

我在项目中遇到过一家电商公司,双十一日志量暴涨10倍,Logstash直接挂了。后来改成Filebeat + Kafka,稳如老狗。这个教训让我记住了:架构设计一定要考虑峰值

二、Filebeat与Logstash:到底怎么选?

很多新手会问:Filebeat和Logstash不是都能采集日志吗?为什么搞两个?

其实它们定位完全不同。我画个对比你就明白了:

特性 Filebeat Logstash
语言 Go Ruby/JRuby
资源占用 极低(10-50MB内存) 较高(256MB-2GB内存)
处理能力 仅采集和简单转发 强大的过滤、转换、富化
插件生态 有限 200+插件
适用场景 边缘节点、容器环境 中心化处理、复杂管道

你看,Filebeat就像快递员,只管把包裹从A送到B。Logstash像分拣中心,能拆包、检查、重新打包。

我的建议:Filebeat部署在每台机器上采集日志,Logstash集中部署做数据清洗。这样既轻量又灵活。

我曾经在一个K8s集群里,每个Pod都挂了个Logstash sidecar,结果内存直接爆了。后来全换成Filebeat DaemonSet,资源占用降了80%。

三、Kafka在日志链路中的作用

为什么日志链路要加Kafka?说白了就三个字:解耦合

你想想看,如果没有Kafka,Filebeat直接往Elasticsearch写日志。一旦ES挂了或者慢查询,Filebeat就得阻塞等待。日志积压,磁盘写满,整个系统跟着遭殃。

Kafka在中间起到了几个关键作用:

  1. 削峰填谷:日志洪峰来了,Kafka先扛住,下游慢慢消费
  2. 数据缓冲:ES挂了也不怕,Kafka保留数据,恢复后继续消费
  3. 多路分发:一份日志可以同时给ES、Spark、Flink消费
  4. 回溯重放:出问题时,可以从Kafka重新消费历史日志

典型架构链路:

应用日志 -> Filebeat -> Kafka -> Logstash(过滤) -> Elasticsearch -> Kibana

我在一个金融项目中,日志链路必须保证99.99%不丢。Filebeat采集后写入Kafka,Kafka配置了3副本+acks=all。即使ES集群挂了三天,Kafka里还躺着几TB日志,恢复后一条没丢。

注意:Kafka不是银弹。如果你的日志量很小(每天几GB),加Kafka反而增加了运维复杂度。我建议日均日志量超过500GB再考虑引入Kafka。

四、实战:一个完整的日志采集配置

给你看一个我常用的Filebeat配置,采集Nginx日志到Kafka:

# filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    log_type: nginx_access
    env: production

output.kafka:
  hosts: ["kafka1:9092", "kafka2:9092", "kafka3:9092"]
  topic: "nginx-logs"
  partition.round_robin:
    reachable_only: true
  required_acks: 1
  compression: gzip
  max_message_bytes: 1000000

然后Logstash从Kafka消费,做字段解析:

# logstash.conf
input {
  kafka {
    bootstrap_servers => "kafka1:9092,kafka2:9092"
    topics => ["nginx-logs"]
    codec => "json"
    consumer_threads => 4
  }
}

filter {
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }
  date {
    match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
  }
}

output {
  elasticsearch {
    hosts => ["http://es-cluster:9200"]
    index => "nginx-logs-%{+YYYY.MM.dd}"
  }
}

这套配置我用了两年多,日均处理200GB日志,从未出过问题。关键点就两个:Filebeat压缩传输Kafka合理分区

五、避坑指南

最后分享几个我踩过的坑:

  • Filebeat多行日志处理:Java异常栈是多行的,记得配置multiline,否则一条异常被拆成几十条
  • Kafka分区数:分区数不是越多越好。我建议分区数 = 消费线程数 × 2,太多反而增加元数据开销
  • Logstash内存配置:默认1GB不够,我一般给4GB。用-Xms4g -Xmx4g设置
  • ES索引生命周期:日志索引一定要配ILM,否则磁盘撑爆了都不知道

我曾经因为没配ILM,ES集群磁盘使用率飙到95%,整个集群变成只读模式。那叫一个惨,恢复了两天才把数据导出来。

好了,日志采集架构就聊到这。下一章咱们深入讲讲Elasticsearch的索引优化和查询调优,到时候见。