日志采集工具(Filebeat):从架构到实战

日志采集是整个链路的第一步,也是最容易被低估的一步。我见过太多团队在 ELK 搭建好后才发现采集端丢数据、性能扛不住、配置混乱。说白了,采集工具选对了,后面的事就顺了。

今天咱们聊 Filebeat。它是 Elastic 官方出品的轻量级日志采集器,Go 语言写的,资源占用极低。我习惯把它比作「日志管道工」——不负责存储,不负责解析,只负责把日志从源头安全、高效地送到下游。

Filebeat 架构与原理

先看整体架构,我画了张图帮你快速建立认知。

Filebeat 核心架构 日志文件 标准输出 Syslog Harvester 池 Harvester 1 Harvester 2 Harvester N Spooler 内存队列 / 磁盘队列 输出 核心流程: 1. Harvester 监听每个日志文件,逐行读取新内容 2. 读取到的日志行发送给 Spooler 暂存 3. Spooler 按批次将数据发送到配置的输出端(Elasticsearch / Logstash / Kafka)

Filebeat 内部有两个核心组件:Harvester 和 Spooler。

Harvester 负责读取日志文件。每个文件对应一个 Harvester,它打开文件句柄,按行读取新内容。嗯,这里要注意——如果文件被轮转(rotate),Harvester 会根据 inode 检测到变化,自动跟踪新文件。

Spooler 负责聚合和发送。Harvester 读到的日志不会立刻发出去,而是先放进 Spooler 的队列里。Spooler 攒够一批数据后,再批量发送到下游。这样做的好处很明显:减少网络连接次数,提高吞吐量。

关键设计理念:Filebeat 采用「至少一次」的投递语义。它会记录每个日志文件的读取偏移量(registry 文件),即使进程重启,也能从断点处继续读取,不会重复也不会遗漏。

Filebeat 安装与配置

安装 Filebeat 其实很简单。我习惯用 RPM 包部署,生产环境也推荐这种方式。

# 下载并安装(以 7.17.x 为例)
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.15-linux-x86_64.tar.gz
tar -xzf filebeat-7.17.15-linux-x86_64.tar.gz
cd filebeat-7.17.15-linux-x86_64/

核心配置文件是 filebeat.yml。我挑几个关键配置项说说。

配置项 说明 我的建议
filebeat.inputs 定义日志采集源 每个 input 只采集一类日志,方便管理
filebeat.config.modules 启用内置模块(如 Nginx、MySQL) 能用模块就别手写配置
output.elasticsearch 直接输出到 ES 小规模场景可用,生产推荐走 Logstash
output.logstash 输出到 Logstash 生产环境首选,方便做数据清洗
logging.level Filebeat 自身日志级别 调试时用 debug,正常用 info

一个最简单的配置示例:

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

output.elasticsearch:
  hosts: ["http://localhost:9200"]

小技巧:启动前先用 ./filebeat test config 检查配置语法,用 ./filebeat test output 测试下游连通性。我每次改配置都会跑一遍,省得重启后才发现问题。

Filebeat 多文件采集

实际项目中,一台机器上往往有多个日志文件需要采集。比如 Nginx 访问日志、错误日志、应用业务日志。你想想看,如果每个日志都单独部署一个 Filebeat 进程,那得多浪费资源?

Filebeat 支持在一个实例中配置多个 input,每个 input 可以指定不同的路径和标签。

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    log_type: nginx_access
  fields_under_root: true

- type: log
  enabled: true
  paths:
    - /var/log/nginx/error.log
  fields:
    log_type: nginx_error
  fields_under_root: true

- type: log
  enabled: true
  paths:
    - /var/log/myapp/*.log
  exclude_files: ['\.gz$']
  multiline:
    pattern: '^\d{4}-\d{2}-\d{2}'
    negate: true
    match: after
  fields:
    log_type: app_biz
  fields_under_root: true

这里有个坑,我曾经踩过。多文件采集时,如果某个日志文件写入量特别大,会占满 Harvester 的处理能力,导致其他日志采集延迟。解决办法是给每个 input 设置 harvester_buffer_size 和 max_bytes 参数,限制单次读取量。

注意:多文件采集时,registry 文件会记录每个文件的读取位置。如果日志文件被删除或轮转,registry 中的记录不会自动清理。时间长了,registry 文件会越来越大。建议定期用 filebeat export registry 检查,必要时手动清理。

Filebeat 与 Logstash 集成

直接输出到 Elasticsearch 虽然简单,但生产环境我强烈建议中间加一层 Logstash。为什么?

  • 数据清洗:Logstash 可以用 filter 插件做字段解析、格式转换、数据脱敏
  • 缓冲能力:Logstash 支持持久化队列,即使 ES 挂了也不会丢数据
  • 多路分发:一份日志可以同时发给 ES、Kafka、监控系统

Filebeat 端配置输出到 Logstash:

output.logstash:
  hosts: ["192.168.1.10:5044", "192.168.1.11:5044"]
  loadbalance: true
  worker: 4

Logstash 端配置接收:

input {
  beats {
    port => 5044
    host => "0.0.0.0"
    ssl => true
    ssl_certificate => "/etc/logstash/certs/logstash.crt"
    ssl_key => "/etc/logstash/certs/logstash.key"
  }
}

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

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

集成要点:

  • Filebeat 和 Logstash 之间建议开启 SSL 加密,防止日志在传输中被截获
  • Logstash 的 worker 参数建议设置为 CPU 核心数,充分利用多核
  • 如果日志量超过 10MB/s,考虑在中间加 Kafka 做缓冲层

我记得有一次线上事故,就是因为 Filebeat 直接连 ES,ES 集群 GC 停顿导致写入拒绝,Filebeat 的重试队列积压,最终 OOM 挂了。后来改成 Filebeat → Logstash → ES 的架构,Logstash 的持久化队列扛住了 ES 的抖动,再也没出过类似问题。

说白了,Filebeat 是轻骑兵,负责冲锋陷阵;Logstash 是后勤部,负责整理和分发。两者配合,才能构建一个稳定、可扩展的日志采集管道。