4. 日志采集技术
4.1 采集工具在日志链路中的定位
在集群环境中,日志并非天然汇聚,而是分散在各节点、各容器的文件或标准输出中。采集工具要解决的核心问题有三个:从哪读(源端适配)、怎么传(传输可靠性)、送到哪(目标端解耦)。理解这三层,才能看懂不同工具的设计取舍。
核心链路:日志源 → 采集器(读取/解析/缓冲) → 传输通道 → 目的地(ES、Kafka、S3等)
Filebeat、Fluentd、Logstash 是当前最主流的三种开源采集器。它们都能完成“采集-传输”的基本任务,但在资源占用、数据处理能力、插件生态和部署模式上差异显著。下面先分别拆解其原理,再给出选型对比。
4.2 Filebeat:轻量级采集的默认选择
Filebeat 由 Elastic 公司用 Go 语言开发,设计目标就是轻量、稳定、易部署。它不承担复杂的数据加工,而是把“读取日志文件并可靠地送出去”这件事做到极致。
其内部核心组件包括:
- Harvester(收割器):负责监听单个文件,逐行读取内容。每个文件对应一个 Harvester,它记录当前读取的偏移量。
- Spooler(队列):Harvester 读到的事件进入内存队列,Spooler 批量将事件发送到配置的输出端。
- Registry 文件:记录每个文件已读取的偏移量。Filebeat 重启后,会从 registry 中恢复状态,避免重复采集或丢失数据。
一个典型的 Filebeat 配置如下:
filebeat.inputs:
- type: filestream
id: app-log
paths:
- /var/log/app/*.log
parsers:
- ndjson: ~
output.elasticsearch:
hosts: ["es-cluster:9200"]
# 若需要发送到 Kafka,则替换 output 段:
# output.kafka:
# hosts: ["kafka1:9092", "kafka2:9092"]
# topic: "app-logs"
# partition.hash:
# reachable_only: true
Filebeat 的背压机制值得一提:当输出端(如 ES)响应变慢时,Filebeat 会自动降低读取速度,并将数据暂存到磁盘缓冲(默认最多 1GB),防止内存溢出。这种设计使其在节点上长时间稳定运行成为可能。
4.3 Fluentd:标签路由与插件生态
Fluentd 由 Treasure Data 发起,用 Ruby 编写,核心思想是“一切皆事件”。每条日志被封装为一个 JSON 事件,包含 tag、time 和 record 三个字段。tag 用于路由,time 是时间戳,record 是原始日志内容。
Fluentd 的处理流程是典型的管道模式:
- Input 插件:从文件、TCP/UDP、HTTP 或容器标准输出读取数据。
- Filter 插件:对事件进行解析、字段修改、丢弃或富化(如添加主机名)。
- Output 插件:根据 tag 匹配规则,将事件写入不同目的地。
Fluentd 的缓冲机制非常灵活,支持内存、文件及两者混合。其配置示例:
<source>
@type tail
path /var/log/nginx/access.log
tag nginx.access
<parse>
@type nginx
</parse>
</source>
<filter nginx.access>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
</record>
</filter>
<match nginx.**>
@type elasticsearch
host es-cluster
port 9200
index_name fluentd-nginx
<buffer>
@type file
path /var/log/fluentd-buffer
flush_interval 5s
</buffer>
</match>
Fluentd 在 Kubernetes 环境中有天然优势:官方提供 Fluentd DaemonSet 部署方案,配合 kubernetes_metadata_filter 插件,可以自动给日志添加 Pod 名称、命名空间、容器名等元数据,极大方便了后续的关联分析。
4.4 Logstash:强大的处理管道
Logstash 是 ELK 技术栈中的老牌成员,基于 JRuby 实现,功能最为全面。它的管道由三阶段组成:
- Input:支持 file、beats、kafka、http、jdbc 等数十种输入。
- Filter:这是 Logstash 的强项。提供 grok(正则解析)、mutate(字段修改)、date(时间解析)、geoip(IP 地理定位)、useragent(浏览器解析)等 50+ 过滤插件。
- Output:输出到 ES、Kafka、S3、Email、Webhook 等。
Logstash 最著名的能力是 grok 正则解析。对于非结构化日志,可以用 grok 将其拆分为结构化字段:
filter {
grok {
match => { "message" => "%{IPORHOST:client_ip} - - \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}\" %{NUMBER:status} %{NUMBER:bytes}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
target => "@timestamp"
}
geoip {
source => "client_ip"
}
}
但 Logstash 的代价是高资源消耗。默认 JVM 堆内存建议 4GB 以上,启动时间也较长(通常 30 秒以上)。因此在实际集群中,Logstash 更多作为集中式处理层部署,而非每节点一个。
4.5 三者对比与选型建议
| 维度 | Filebeat | Fluentd | Logstash |
|---|---|---|---|
| 开发语言 | Go | Ruby + C | JRuby |
| 资源占用 | 极低(~10MB内存) | 中(~100MB) | 高(1GB+) |
| 数据处理能力 | 弱(仅简单解析) | 中(Filter 插件) | 强(Grok/GeoIP等) |
| 插件数量 | 较少(约30个) | 丰富(500+) | 丰富(200+) |
| 缓冲可靠性 | 内置磁盘缓冲 | 需配置 buffer | 需配置 queue |
| K8s 适配 | 好(Sidecar模式) | 极好(DaemonSet+元数据) | 一般(较重) |
| 典型场景 | 节点日志采集 | 容器环境/数据路由 | 集中解析/复杂清洗 |
选型建议:若集群以 Kubernetes 为主,且需要丰富元数据,优先考虑 Fluentd;若追求极简部署和低资源占用,Filebeat 是稳妥选择;若日志格式复杂、需要大量 grok 解析和字段富化,则 Logstash 更合适。实践中常组合使用:Filebeat/Fluentd 负责节点采集,Logstash 负责集中处理。
4.6 常见误区与复盘
误区一:认为 Filebeat 不能做任何解析。实际上 Filebeat 支持简单的 JSON、multiline 等解析,只是不支持 grok 这类复杂模式。若只是提取 JSON 字段,Filebeat 完全够用。
误区二:忽略 Fluentd 的 tag 路由设计。很多用户把 Fluentd 当作普通采集器,只配置一个 source 和一个 match,完全没利用 tag 做多路分发。实际上,通过 tag 前缀匹配,可以轻松实现“访问日志送 ES,错误日志送 Kafka,审计日志送 S3”的精细化路由。
误区三:Logstash 管道中 filter 顺序随意。filter 是按配置顺序执行的,若先执行 geoip 再执行 mutate 删除字段,可能导致 geoip 解析失败。正确做法是先做字段清理,再做富化操作。
复盘案例:某电商集群曾用 Logstash 直接采集 200 个节点的应用日志,结果频繁 OOM。排查发现每个节点部署了独立 Logstash 实例,JVM 堆各分配 2GB,导致集群总内存超限。优化方案是:节点侧改用 Filebeat 采集并发送到 Kafka,下游部署 3 个 Logstash 实例消费 Kafka 做集中解析。改造后节点内存占用从 2GB 降至 20MB,Logstash 实例数从 200 减至 3,整体稳定性大幅提升。
这个案例说明:采集层与处理层应当分离。轻量采集器负责“快而稳”地收集,重量级处理器负责“深而全”地加工,中间用消息队列解耦,是集群日志采集架构的最佳实践。