Logstash 数据采集与管道架构

如果你管过几套 ELK,应该会同意:Logstash 是整个体系里最容易被低估但也最容易出事的环节。数据进不来、处理慢、OOM 崩掉,八成都是管道设计没想清楚。这一章我把管道模型、插件使用、多路并行、性能调优和 OOM 排查串起来讲,每一点都配上我自己的实战经验。

事件处理管道模型:Input / Filter / Output

Logstash 的管道模型说白了就是三条带的流水线:Input → Filter → Output。每条数据从 Input 进入,经过 Filter 加工,最后从 Output 送走。理解了这个模型,你才能知道配置怎么写、性能瓶颈在哪。

阶段职责我常用的插件
Input从源头拉数据,支持文件、TCP、Kafka、Beats 等file, beats, kafka, tcp
Filter解析、转换、增强、脱敏grok, mutate, json, date, geoip
Output写到下游(ES、Kafka、文件、邮件等)elasticsearch, kafka, stdout

你想想看,每一个事件都要完整走完这三个阶段,如果其中任何一步慢了,整条管道就会堵住。我在项目中见过很多人把 filter 写得特别重,grok 匹配几十个正则,结果吞吐量直接掉到 100 条/秒。所以我的建议是:能不做的 filter 就不做,能提前过滤的就扔到 Input 侧。

插件系统:拧螺丝的思路

Logstash 有 200+ 官方插件,社区还有一堆。你不用全记住,只需要掌握最常用的那 10 来个。插件本质上就是一个个 Ruby 类,按照 Input / Filter / Output / Codec 分类。每个插件都有自己的一套配置参数。

举个例子,grok 是最强大的 filter,但也是最吃 CPU 的。我曾经在一个金融项目里用了 20 多个 grok 模式去解析不同格式的应用日志,结果 CPU 飙到 90%。后来我把模式拆成多个 if 分支,提前用 type 分流,每个事件只走一个分支,CPU 降到 30%。

避坑指南:我曾经遇到过一个线上的 Logstash 实例突然无法启动,排查发现是因为装了一个不兼容的第三方插件。从那以后我定了规矩:所有插件必须通过 CI 测试后再上线,生产环境只使用官方插件。

多管道并行采集

单一管道在处理多数据源时容易互相干扰——比如慢的 filter 拖慢所有输入。Logstash 从 6.x 开始支持多管道(multiple pipelines)。你可以在 pipelines.yml 里定义多个独立管道,每个管道有自己的一套 Input → Filter → Output,完全隔离。

# pipelines.yml 示例
- pipeline.id: app-log
  pipeline.workers: 8
  path.config: "/etc/logstash/conf.d/app-log.conf"

- pipeline.id: mysql-slow
  pipeline.workers: 4
  path.config: "/etc/logstash/conf.d/mysql-slow.conf"

- pipeline.id: metric
  pipeline.workers: 2
  path.config: "/etc/logstash/conf.d/metric.conf"

多管道的核心好处是“故障隔离”。如果 App 管道 clog 了,MySQL 管道依然能正常写数据。但注意:每个管道会独自占用内存和线程,总资源是固定的,别开太多管道把 JVM 撑爆。

性能调优参数

调优就是找平衡点。下面这几个参数是必须关注的,我每次上线前都会过一遍。

参数默认值说明与我的经验
pipeline.workersCPU 核心数建议不超过 CPU 核数的 2 倍,太大会因上下文切换变慢
pipeline.batch.size125批量处理事件数。IO 密集时调大(如 500-1000),但注意内存
pipeline.batch.delay50等待批量的时间(毫秒)。适当调小能降低延迟
queue.typememory生产环境建议用 persisted,防止意外重启丢数据
heap size1GBJVM 堆大小。我通常设为物理内存的 50%(不超过 32GB)

为什么建议 persisted 队列?因为内存队列一旦进程挂掉,正在排队的事件全丢。我曾经在压测时人为 kill 掉 Logstash 进程,重启后数据对不上,排查了好久才发现是队列丢失。调成 persisted 后,重启能正常恢复。

Logstash OOM 问题排查

说到这个我就想起一个真实的教训。有一次客户反馈日志中心突然没有新数据了,我一看 Logstash 进程已经 OOM killed。当时版本是 7.0,配置了多个管道,堆给了 4GB。

排查步骤:

  1. 看 JVM 监控——发现堆内存使用量不断攀升,GC 频率越来越密集,最后 Full GC 失败。
  2. 抓 heap dump——用 jmap 在崩溃前保存了 dump,分析发现是 json filter 解析一个超大 JSON 字段(某个业务日志里嵌了 50MB+ 的 Base64 图片数据)导致对象膨胀。
  3. 定位根因——那个字段本不该出现在日志中,是上游开发写入了调试信息。我们用 prune filter 提前去掉那个字段,并把 pipeline.batch.size 从 500 降回 125,OOM 再没出现。
重要教训:千万别让 Logstash 直接处理大对象(超过几 MB 的字段)。如果一定要处理,先在 Input 用 codec 截断,或者在 filter 里用 mutate 的 replace 替换为空。我曾经在另一个项目中也见过因为 gzip 解码后字符串太长导致的 OOM。

本章核心框架:Logstash 管道架构

下面这张图概括了多管道并行、每个管道内的三阶段模型、以及队列与 worker 的关系。你写配置、排问题的时候,脑子里一直有这个图就对了。

Logstash 多管道架构图 Logstash 多管道并行架构 管道 1 (app-log) Input (beats) Filter (grok + mutate) Output (ES) 管道 2 (mysql-slow) Input (file) Filter (json + date) Output (ES) 管道 3 (metric) Input (kafka) Filter (aggregate) Output (kafka) 内部队列 pipeline.batch.size queue.type heap size 💡 每个管道独立运行,互不阻塞。队列和 JVM 内存是所有管道的共享资源。 调参时要考虑总 worker 数、总 batch 大小,避免内存超限。