4. 日志收集架构:常见日志收集系统(ELK、Loki、Flume)及其在熔断下的表现

日志收集是整个可观测性的基石。平时风平浪静时,这套系统看上去都挺靠谱。一旦熔断发生,流量瞬间被切断,大量日志产生的模式也突变。这时候,不同架构的真实差距就暴露出来了。我参与过不少系统的故障复盘,很多日志丢失的问题其实不是熔断本身导致的,而是收集系统扛不住压力,自己先垮了。

核心观点: 熔断降级下,日志收集架构的核心矛盾不在于“能否收集到所有日志”,而在于“如何在系统濒临崩溃时,优先保护日志通道的存活”。

下面这张图,梳理了我们这章要聊的三个主流架构,以及它们在熔断场景下的核心差异。你可以先看一眼,有个整体印象。

熔断场景下三大日志收集架构表现对比 ELK(Logstash + ES + Kibana) - Logstash 推模式,易出现背压 - ES 写入熔断(429)后丢失风险高 - 内存队列有限(默认 4KB/事件) - 适合写入可控的稳定场景 Flume(Source + Channel + Sink) - File Channel 持久化到磁盘 - 背压下可堆积大量事件在磁盘 - Sink 恢复后自动重放 - 吞吐受磁盘 I/O 限制 Grafana Loki(拉模式) - Promtail 主动拉取日志 - 拉不到时自动重试/跳过 - 自身不产生背压给应用 - 熔断期间天然低侵入性 关键差异点 推模型(Logstash):应用主动推,易被下游拖死。 拉模型(Promtail):由代理主动拉,对应用几乎无影响。 内存缓冲 vs 磁盘缓冲:内存丢数据更快,磁盘能扛更久。 熔断降级下的选型建议:优先关注缓冲机制和拉模型

4.1 ELK(Elasticsearch + Logstash + Kibana)家族

ELK 是目前使用最广泛的组合。但说实话,它在熔断下的表现,我持保留态度。为什么?因为它的日志传输是典型的“推模型”。

Logstash 作为中间件,从应用端拉取日志后,会经过 filter 处理,然后批量推送给 Elasticsearch。这个过程本身没什么问题。问题在于,当下游 Elasticsearch 因为高负载触发写入熔断(HTTP 429)时,Logstash 的默认处理策略非常简单:要么阻塞,要么丢弃。

常见陷阱: Logstash 的默认内存队列大小非常有限。我曾经遇到过一个线上问题,ES 集群的 master 节点因为 GC 暂停了 10 秒。这 10 秒内 Logstash 的队列被打满,后续来的日志直接触发了 `blocking` 模式,进而导致上游的应用端 Logstash 输出线程卡死,最终影响了业务接口的响应时间。还好只是日志丢了,要是影响核心交易就出大事了。

如何优化?我个人的习惯是,必须启用 Logstash 的持久化队列。这是一种基于磁盘的缓冲机制。

配置示例:

# 在 pipeline.yml 或 logstash.yml 中
queue.type: persisted
queue.max_bytes: 10gb
queue.drain: true  # 优雅关闭时等待队列清空

这相当于给 Logstash 加了一层“蓄水池”。当 ES 熔断时,日志会堆积在磁盘上而不是直接丢弃。等 ES 恢复后,Logstash 自动重放。不过要注意,磁盘 I/O 和队列管理本身也有开销,千万别设成无限大。

4.2 Grafana Loki

聊完 ELK,我们看看 Loki。它跟 ELK 的理念完全不同。ELK 是全文索引,Loki 则“索引标签,不索引日志内容”。这个设计差异,在熔断场景下带来了巨大的好处。

Loki 的日志采集采用的是拉模型。Promtail 作为代理部署在每台机器上,主动去本地文件或系统日志中拉取内容,然后推给 Loki。你不妨想想,这里的关键点在哪?日志的生产者和采集者解耦了。

就算 Loki 服务端挂了,Promtail 也只会不断重试。它不会反过来拖死你的应用程序。因为应用写日志到本地文件,这是磁盘操作,基本不受网络影响。

实战经验: 我去年维护过一个电商系统,双11大促时部分服务触发了熔断。我们当时用了 Loki。熔断期间,应用节点的日志全部及时写入了磁盘。等运维把 Loki 恢复后,Promtail 自动补发了所有积压的日志,一份都没丢。事后复盘时,所有人都觉得这个“拉模型”帮了大忙。

Loki 也有短板。它的查询性能不如 ES 灵活,特别是对日志全文内容做模糊搜索时。但在“保障数据完整性”这个核心指标上,它很省心。

4.3 Apache Flume

Flume 在大数据生态里用的比较多,但它处理日志流的思路非常经典。它把整个流程分成三块:Source、Channel、Sink。

  • Source:从数据源(如日志文件、网络端口)采集数据。
  • Channel:缓冲数据。这是核心。可以选择 Memory Channel(快但丢数据)或 File Channel(慢但安全)。
  • Sink:把数据写出到目标(如 HDFS、Kafka、ES)。

熔断降级发生时,通常影响的是 Sink 这一侧。比如 Sink 要写入的 HDFS 集群挂了,或者写入目标响应超时。这时候 Flume 本身会怎样?

如果配置了 File Channel,Flume 会把事件数据持久化到磁盘的 checkpoint 和 data 目录。Sink 那边的线程会不断重试。这个机制,说白了就是用磁盘空间换数据安全。我见过一个生产系统,File Channel 扛住了长达 2 小时的 HDFS 宕机,事后数据零丢失。当然,代价是需要预留足够的磁盘空间,监控 Channel 的填充率。

关键指标: Flume 的数据完整性,很大程度上取决于 Channel 的配置。建议在生产环境至少使用 File Channel,并估算好一天的日志量,给磁盘预留 3 倍的空间。同时设置 `keep-alive` 和 `transactionCapacity` 避免死锁。

4.4 三者在熔断场景下的关键对比

我整理了一张表格,把它们的核心差异放在一起,方便你快速参考。

对比维度 ELK(Logstash + ES) Grafana Loki Apache Flume(File Channel)
日志传输模式 推模型(应用→Logstash→ES) 拉模型(Promtail→Loki) 推模型(Source→Channel→Sink)
熔断时数据完整性 低。默认内存缓冲,极易丢数据 高。文件拉取+重试机制,基本不丢 高。File Channel 持久化,稳扎稳打
对上游应用的影响 中。Logstash 阻塞可能拖拽应用 低。应用只管写文件,完全无感 低。Source 只读一次,写入 Channel 后和上游无关
资源消耗 高。ES 索引和存储很吃资源 低。对象存储为主,计算资源少 中。磁盘 I/O 是主要开销
配置复杂度 高。需要调优 ES 熔断器、Logstash 队列 低。开箱即用,运维友好 中。需要合理设置 Channel 参数
最佳场景 查询需求频繁、需要全文检索 注重可靠性和低成本 大数据生态、对数据完整性要求极高
再次提醒: 很多人以为日志收集是“写到文件里就行”。但在分布式高并发场景下,如果你的日志收集系统本身就是推模型,并且没有链路隔离,一旦日志系统故障,它完全可能反噬你的业务系统。这不是危言耸听,我曾经见过因为 ES 熔断导致 Logstash 堆积,最终引发 Full GC 的案例。

最后留一点题外话。不管你选哪个架构,我建议一定要加上日志分级的兜底策略。比如,核心业务日志走一条高可靠的通道,非核心审计日志可以容忍适当丢弃。别让普通错误日志,冲垮了你最重要的接口轨迹。