日志采集架构:从源头说起

日志采集,说白了就是解决「日志怎么到平台」的问题。我刚开始做风控平台时,觉得这步最简单——不就是把日志文件读出来发走吗?结果第一个线上事故就给我上了一课:采集器挂了,风控模型跑了一周假数据,损失惨重。

所以今天咱们聊聊日志采集的几种主流方案,以及怎么搭一套高可用的采集架构。

常见日志采集方案

目前业界用得最多的三款工具:Filebeat、Logstash、Fluentd。我按自己的使用经验,一个个说。

Filebeat:轻量级采集器

Filebeat 是 Elastic 家的产品,主打轻量。它只做一件事:读日志文件,发出去。不做复杂处理,不占太多资源。

我在项目中遇到过这种情况:业务服务器上跑着 Java 应用,内存本来就紧张。如果用 Logstash 采集,内存直接飙到 1GB+,业务方投诉。换成 Filebeat,内存占用不到 50MB,问题解决。

核心特点:

  • Go 语言编写,二进制文件只有十几 MB
  • 支持断点续传(这个很重要,后面会讲)
  • 内置 Elasticsearch、Logstash、Kafka 输出
  • 不支持复杂数据处理

Filebeat 的配置很简单,举个例子:

filebeat.inputs:
- type: log
  paths:
    - /var/log/nginx/access.log
  fields:
    app: nginx
    env: prod

output.elasticsearch:
  hosts: ["http://es-cluster:9200"]
  index: "nginx-access-%{+yyyy.MM.dd}"

嗯,这里要注意:Filebeat 默认的采集速度是 5 秒一次。如果日志量特别大,可以调小 scan_frequency 参数,但别低于 1 秒,否则 CPU 会飙高。

Logstash:全能型处理器

Logstash 是 ELK 里的「L」,功能最全。它不仅能采集,还能做数据清洗、格式转换、字段提取。说白了,它是个管道,日志从一头进去,经过各种插件处理,从另一头出来。

我曾经用 Logstash 处理过风控日志里的 IP 地址。原始日志里 IP 是字符串,我需要把它转成整数,方便做地理定位。Logstash 的 mutate 插件一行搞定:

filter {
  mutate {
    convert => { "client_ip" => "integer" }
  }
}

但 Logstash 有个硬伤:吃内存。默认 JVM 堆内存 1GB,处理复杂规则时还会更高。所以我的建议是:Logstash 适合做集中式处理,不适合部署在业务服务器上

特性 Filebeat Logstash
资源占用 低(~50MB) 高(~1GB+)
数据处理 不支持 强大(200+ 插件)
适用场景 边缘采集 集中处理

Fluentd:数据路由专家

Fluentd 是 Treasure Data 开源的项目,在日本和国内都很流行。它的核心优势是数据路由——你可以把不同来源的日志,按规则分发到不同目的地。

举个例子,风控平台里,交易日志要发到 Kafka,系统日志要发到 Elasticsearch,审计日志要存到 S3。用 Fluentd 的 match 规则,配置起来很清晰:

<match transaction.*>
  @type kafka
  brokers kafka-cluster:9092
  topic transactions
</match>

<match system.*>
  @type elasticsearch
  host es-cluster
  port 9200
</match>

Fluentd 的插件生态也很丰富,但配置语法有点怪(用 XML 风格),刚上手时容易写错。我个人习惯用 Filebeat + Logstash 组合,但如果你团队熟悉 Ruby,Fluentd 是个好选择。

采集架构设计原则

做了这么多年风控,我总结了几条采集架构的设计原则。你想想看,日志采集就像自来水管道——源头脏、管道长、用户多。设计不好,要么堵,要么漏。

原则一:解耦采集与处理

采集器只负责读日志、发日志。数据处理交给下游。为什么?因为采集器挂了,至少日志还在磁盘上;处理节点挂了,采集器还能缓存数据。

我曾经见过一个团队,用 Logstash 既采集又处理,结果 Logstash 处理规则写错了,直接把日志全丢弃了。等发现时,已经丢了 3 小时的数据。嗯,从那以后我再也不敢把采集和处理混在一起。

原则二:引入缓冲层

缓冲层是日志采集的「保险丝」。常见的缓冲层有 Kafka、Redis、RabbitMQ。我个人最推荐 Kafka,原因有三:

  • 高吞吐:每秒几十万条没问题
  • 持久化:数据不会丢
  • 多消费者:一个日志可以被多个系统消费

没有缓冲层的架构,就像没有蓄水池的自来水厂——下游一停,上游就得停。

原则三:保证至少一次投递

日志采集最怕丢数据。Filebeat 和 Fluentd 都支持「至少一次」语义。原理很简单:采集器记录自己读到哪一行了(offset),如果发送失败,下次重试时从 offset 继续读。

但要注意:「至少一次」不等于「精确一次」。如果下游重复消费,需要业务层做去重。风控场景里,重复日志可能导致重复计算,所以我会在日志里加一个唯一 ID,下游用 ID 去重。

高可用采集方案

单机采集肯定不行。服务器宕机、网络抖动、磁盘写满,任何一个问题都能让采集中断。下面是我在风控平台里用的高可用方案。

方案一:多采集器 + 负载均衡

每台业务服务器部署两个 Filebeat 实例。一个主采集,一个备用。主采集挂了,备用自动接管。配合 Nginx 或 HAProxy 做负载均衡,把日志均匀分发到下游。

配置上,Filebeat 支持 output 多节点:

output.logstash:
  hosts: ["logstash-1:5044", "logstash-2:5044"]
  loadbalance: true

这样即使一个 Logstash 节点挂了,Filebeat 会自动切换到另一个。

方案二:Kafka 做缓冲层

这是最稳妥的方案。架构是这样的:

业务服务器 → Filebeat → Kafka → Logstash → Elasticsearch

Kafka 在这里起两个作用:

  • 削峰填谷:业务高峰期日志量暴增,Kafka 能扛住
  • 故障隔离:Logstash 挂了,Filebeat 照常写 Kafka;Kafka 挂了,Filebeat 本地缓存

Filebeat 支持 Kafka 输出,配置也很简单:

output.kafka:
  hosts: ["kafka-1:9092", "kafka-2:9092"]
  topic: "app-logs"
  partition.round_robin:
    reachable_only: true

方案三:本地缓存 + 重试机制

如果网络不稳定,或者下游服务经常挂,可以在采集器本地做缓存。Filebeat 默认使用 registry 文件记录 offset,即使进程重启,也能从断点续传。

我建议把缓存目录放在独立磁盘上,避免和系统日志抢 I/O。另外,缓存文件要定期清理,否则磁盘会满。

避坑指南:我曾经遇到过 Filebeat 缓存文件损坏,导致日志重复采集。后来我加了一个监控:每天检查 registry 文件大小,超过 100MB 就报警。另外,Filebeat 的 close_inactive 参数别设太大,否则文件句柄会一直占用。

架构图:一张图看懂采集链路

下面这张 SVG 图,展示了我最常用的采集架构。你一看就明白:

风控平台日志采集架构 业务服务器 Nginx / Java / Python Filebeat Kafka 集群 缓冲层 / 削峰填谷 Logstash 集群 数据清洗 / 格式转换 Elasticsearch 存储 / 检索 Filebeat (备用) 图例: 业务服务器 采集器 缓冲层 处理层 存储层 备用链路

这张图里,核心思路是:采集端轻量、中间层缓冲、处理端集中。每个环节都有冗余,任何一个节点挂了,整个链路不会断。

小提示:如果你刚开始搭建采集架构,别追求一步到位。先从 Filebeat + Elasticsearch 开始,等日志量上来了,再逐步加入 Kafka 和 Logstash。我见过太多团队一开始就上全套 ELK,结果运维成本比业务开发还高。

好了,日志采集架构就聊到这儿。下一节咱们会深入日志格式规范——毕竟,采集上来的日志如果格式乱七八糟,后面处理起来会非常痛苦。


专注资料整理