采集层设计:日志采集器选型与性能调优

采集层是整个审计分层的入口,说白了就是“怎么把日志‘捞’起来的”一部分。我见过不少团队上来就追求高大上的架构,结果光采集就丢数据、卡瓶颈。嗯,今天我就拿经验聊聊这东西。

日志采集器选型:Filebeat vs. Fluentd

主流的日志采集器无非两个:Filebeat 和 Fluentd。你可能要问:“为什么不是Logstash?”其实Logstash太重了,一般只做服务端处理,采集层我会优先选轻量的选手。

核心原则:采集器要轻、快、稳。不要因为在采集层做复杂处理导致主业务受影响。
对比维度 Filebeat Fluentd
资源占用 极低,Go语言编写,通常 < 30MB内存 中低,Ruby + C实现,基础约 50-70MB
性能上限 单节点约 10万行/秒(纯转发) 单节点约 3-5万行/秒(插件较多时)
插件生态 有限,主要输出到ES/Logstash/Kafka 丰富,输入/输出/过滤插件 1000+
数据处理 弱,仅支持简单字段裁剪和多行合并 强,支持正则解析、yaml转换、路由
部署难度 简单,二进制直接跑 中等,依赖Gem或Docker

我个人习惯:

  • 如果只是简单转发JSON日志,无复杂过滤 → 我选 Filebeat。我之前帮人调一个电商的网关日志,用 Filebeat 配合 Elasticsearch,CPU 常年稳定在 5%。
  • 如果还需要做数据清洗、格式转换甚至下发告警 → 我选 Fluentd。它有“input + filter + output”三段管道,几乎每个环节都能插自定义插件。
小经验:Filebeat 的 multiline 配置可以帮你把 Java 堆栈的多行异常合并成一条。我曾经处理过一个微服务 Full GC 日志,就是用 Fluentd 的 multiline_flush 插件避免数据分裂。

数据源接入:Kafka 和 HTTP

有了采集器,日志往哪送?一般两种情况:异步进消息队列,或同步直接把日志发到某个采集端点。

接入 Kafka

这是大型审计系统的标配。Kafka 充当缓冲区,下游消费能力不足时,日志不会丢。

典型配置(Filebeat 输出到 Kafka):

output.kafka:
  hosts: ["192.168.1.10:9092", "192.168.1.11:9092"]
  topic: "audit-log"
  codec.json:
    pretty: false
  compression: gzip
  max_message_bytes: 1048576   # 1MB

我见过有人在生产环境把 max_message_bytes 调得太大,导致 Kafka 内存占用飙升,甚至 OOM。这里我建议:

  • 单条日志不超过 1MB(默认就差不多);
  • 启用压缩(gzip/zstd),可以节省 60-70% 带宽;
  • 分区数最好 3-5 个,不多不少,避免重复消费。

接入 HTTP

如果你没有中间件,或者日志源是一个不支持输出到 Kafka 的遗留系统,那就走 HTTP 接入。

注意:HTTP 接入容易导致采集层被拖垮。我有个血的教训——之前做内部 API 审计,一小时内一个服务挂了,因为它的 HTTP 端点被消费端堵塞,日志积压导致内存溢出,整个实例挂了。后来引入了 Nginx 限流 + 本地队列缓冲才解决。

建议用 Fluentd 的 HTTP 输入插件做接收端,再结合限流 @type throttle:

<source>
  @type http
  port 9880
  bind 0.0.0.0
  add_http_headers true
  <transport tls>
    cert_path /etc/td-agent/cert.pem
    private_key_path /etc/td-agent/key.pem
  </transport>
</source>

<filter audit.**>
  @type throttle
  threshold 1000
  interval 1
</filter>

采集性能优化:不是堆机器就行

优化采集性能,说白了就是要解决三个矛盾:CPU 不够用、IO 跟不上、网络打宽了也没用。

  1. 调整并发和缓冲区
    • Filebeat:harvester_buffer_size 默认 16384,可以适当调到 32768 提升吞吐,但注意内存。
    • Fluentd:设置 flush_interval 5s 和 chunk_limit_size 4m,别让块太小或太大。
  2. 启用磁盘缓冲

    这是保命手段。Filebeat 默认自带 registry 文件,记录了每个文件的读取位置,如果宕机重启也不会重复读取全部日志。Fluentd 则需要显式配置 buffer_type file:

    <buffer>
      @type file
      path /var/log/fluentd/buffer/
      flush_interval 10s
      retry_max_times 5
      retry_wait 10s
    </buffer>

    我曾经遇到流量突增时,Fluentd 的 memory buffer 全丢了。换成 file buffer 后,即使进程崩溃,日志还在磁盘上,重启就能恢复。

  3. 多级队列:采集端与服务端分离

    别把采集器和数据处理混一起。我一般用这种模式:

架构建议:采集端只负责“收”,不负责“算”。所有解析、转换扔到下游 Fluentd/Logstash 集群。这样采集端不会变慢,也不会把压力传导到核心业务。
采集层核心逻辑关系 数据源 应用日志 / 网关 容器 stdout / syslog 采集器 Filebeat / Fluentd 多级队列 文件 / 网络轮询 缓冲/队列 Kafka 集群 HTTP 端点 磁盘文件缓冲区 下游消费 Fluentd 处理 存储 / 告警 反压/限流 避坑点 · HTTP 接入一定要加限流,否则一个故障源会拖垮整个采集层 · Kafka 分区不宜过多(建议 3-5),否则负载不均或浪费连接 · Filebeat 多行合并正确配置,避免 Java 堆栈被截断成多条无序记录
性能指标参考(经验值):
  • 单节点 Filebeat + Kafka:稳定 50000 ~ 80000 行/秒,CPU 15% 以内;
  • 单节点 Fluentd + Kafka(带正则解析):约 15000 ~ 30000 行/秒,CPU 30% 左右;
  • 如果超过上限,加机器比加配置更经济——反正采集器本身是无状态的。

总结一下:性能问题 80% 是因为“数据积压 + 缓冲区配错”导致的。在项目初期预留磁盘缓冲、控制单条日志大小、选对采集器,就能避开大部分坑。我之前见过有人因为讨厌配置 Fluentd,直接把数据裸写到 ES,结果写入节点压垮,排了一周的错……所以,采集层稳了,后面才稳。