3. 日志采集与集中管理

日志采集这件事,说简单也简单,说复杂也复杂。我早年在一个电商公司做安全运维时,服务器上百台,日志散落在各个机器上。每次排查入侵,都得 ssh 登录到每台机器去翻 /var/log,那感觉就像在垃圾堆里找一枚戒指。后来我学乖了——日志必须集中管理,否则你连攻击者什么时候进来的都不知道。

这一章,我就带大家看看主流的日志采集工具,以及怎么把它们串成一套可用的架构。

3.1 为什么需要日志集中管理?

你想想看,日志如果不集中,会有什么问题?

  • 分散难查——攻击者清掉单机日志,你就彻底失明。
  • 检索慢——grep 几 GB 的文件?等得花儿都谢了。
  • 无法关联——同一笔攻击请求经过 Web、App、DB 三台机器,你得手动拼时间线。
  • 存储无策略——磁盘满了就删,删完发现缺了关键证据。

说白了,集中管理就是给日志建一个「中央情报局」。采集工具负责把散落在各地的日志运回来,然后由搜索引擎建索引,最后你用一个仪表盘就能全局搜索。

核心原则:日志采集必须做到「不丢、不重、不乱序」。这三条做到,后面的事情就好办了。

3.2 Logstash — ELK 生态的核心采集器

Logstash 是 Elastic 官方的采集工具,也是 ELK 里那个“L”。它的设计哲学很直白:用 pipeline 的方式处理日志。一条日志从输入到输出,经过 filter 做解析、清洗、富化,最后送到 Elasticsearch。

我记得第一次用 Logstash 是在一个金融项目里,当时的日志格式五花八门——有 JSON、有 multiline 的堆栈、还有自定义的 key=value 格式。Logstash 的 filter 插件库真的很全,grok 正则解析、mutate 字段重命名、date 时间戳标准化,几行配置就搞定了。

input {
  file {
    path => "/var/log/nginx/access.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}

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

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

这个配置看着简单,但实际生产里要注意的地方不少。比如 sincedb_path 你如果不管理好,重启后可能会重复读取。我就在项目里踩过这个坑,导致 ES 里一堆重复文档。

注意:Logstash 默认使用 JVM,内存占用偏高。如果你在边缘节点采集,建议给 JVM heap 留够 1-2GB,否则 GC 会把吞吐拖垮。

3.3 Fluentd — 轻量级日志采集利器

Fluentd 是云原生社区更偏爱的选择,尤其在 Kubernetes 环境里。它的核心优势是资源占用小、插件生态丰富、数据转发灵活。

它的架构跟 Logstash 类似,也是 input → buffer → output 的 pipeline。但 Fluentd 内置了一个「缓冲层」,数据先落盘或放内存,再批量发出去。这个设计让 Fluentd 在面对网络抖动时比 Logstash 更稳定。

我曾在一次容器迁移项目里用 Fluentd 替代 Logstash,原因是容器节点内存只有 512MB,Logstash 跑起来就占了大半,而 Fluentd 只用了不到 100MB。配置也很简洁:

<source>
  @type tail
  path /var/log/containers/*.log
  pos_file /var/log/fluentd-containers.log.pos
  tag kubernetes.*
  format json
</source>

<match kubernetes.**>
  @type elasticsearch
  host localhost
  port 9200
  logstash_format true
  flush_interval 5s
</match>

这里有个小细节:pos_file 的作用类似 Logstash 的 sincedb,记录文件读取位置。我见过有人部署时忘了配这个,结果每次重启都重读全量日志,ES 差点被冲垮。

我的建议:在 Kubernetes 里,直接用 Fluentd 的 DaemonSet 模式,每个节点跑一个 pod,采集宿主机上的容器日志。资源控制好,基本不会出问题。

3.4 Logstash 与 Fluentd 对比

经常有人问我:到底该用 Logstash 还是 Fluentd?我的回答是:看场景。下面是几个关键维度的对比:

维度 Logstash Fluentd
开发语言 Java (JRuby) Ruby + C
内存占用 较高 (默认 1GB+) 较低 (约 50-200MB)
启动速度 较慢 (JVM 预热) 快 (秒级启动)
过滤语法 丰富,grok 支持强 插件化,语法简洁
缓冲机制 内存/持久化 (较弱) 内置 buffer,稳定可靠
Kubernetes 友好度 一般 优秀 (CNCF 毕业项目)
社区生态 Elastic 生态,插件多 600+ 插件,云原生友好

其实说白了,如果你团队已经用了 ELK,Logstash 是无缝衔接的选择。如果你在云原生环境、对资源敏感,或者需要更灵活的数据路由,Fluentd 更合适。

3.5 ELK 架构归纳总结

ELK 不是一个工具,而是一套组合拳。我用一张图来展示它的核心流程:

日志采集与集中管理核心架构 日志源 应用 / 系统 / 容器 采集层 Logstash / Fluentd 消息队列 Kafka / Redis 存储检索 Elasticsearch 可视化告警 Kibana ELK 架构核心链路:采集 → 缓冲 → 索引 → 检索 → 可视化 数据流单向传递,每一层可水平扩展,实现高可用与高吞吐 安全日志必须经过校验与隔离,防止日志注入攻击污染下游

整个链路其实就五步:采集 → 缓冲 → 索引 → 检索 → 可视化。每一层都可以横向扩展,这也是 ELK 能扛住海量日志的原因。

关键认知:日志集中管理不只是「把日志放到一起」,而是建立一个可搜索、可告警、可追溯的数据平台。ELK 只是实现方式之一,但它的生态最成熟,社区最活跃。

我在多个项目中总结了几条实战建议,分享给大家:

  • 采集端一定要做限速和过滤——别把 debug 日志全塞进去,否则 ES 的存储会爆炸。我见过一个团队没配过滤,一天产生了 2TB 的 DEBUG 日志,ES 集群直接挂了。
  • 消息队列是必须的——哪怕你现在流量不大,也建议加一层 Kafka。它能解耦采集和存储,避免 ES 抖动导致日志丢失。
  • 日志格式统一化——所有应用输出 JSON 格式,字段名遵循命名规范。这样 filter 阶段的解析成本最低,检索效率最高。
  • 安全日志单独走一个索引——不要跟业务日志混在一起。安全日志的保留周期、访问权限、告警规则都不一样,分开管理更清晰。

警告:日志注入攻击在集中管理架构里更容易扩散。攻击者在日志内容里插入恶意 payload,如果 Kibana 或告警系统没有做转义,可能导致 XSS 或命令执行。采集层必须对日志内容做清洗和校验,不要「原封不动」地存进去。

一个小技巧:在 Logstash 的 filter 阶段,用 sanitize 或 htmlentities 插件对 message 字段做转义。Fluentd 的话,可以用 record_modifier 配合正则清洗。别嫌这一步麻烦——它可能帮你挡住一次攻击。