可靠地发送日志:输出插件与缓冲
安装输出插件,将不同的 source 路由到不同的目的地,并配置缓冲,使后端宕机时不会丢失数据。
收集和解析日志只是管线的一半;另一半是可靠地把它们送到有用的地方,即使目的地暂时不可达。本指南介绍如何安装输出插件、将多个 source 路由到各自的目的地,以及真正让交付变得可靠、而不是尽力而为的缓冲配置。
安装输出插件
Fluentd 的内置输出涵盖了基本需求(file、stdout、转发到另一个 Fluentd 实例的 forward),但大多数真实的目的地 —— Elasticsearch、云存储、SaaS 日志平台 —— 都以独立插件的形式提供,你用 fluent-gem 安装它们,这是 Fluentd 管理的 RubyGems 包装器:
sudo fluent-gem install fluent-plugin-elasticsearch
使用 fluent-gem 而不是直接 gem install 很重要:它会安装到 Fluentd 自己实际使用的 Ruby 环境中,而不是系统默认的 Ruby,而后者是”我安装了插件但 Fluentd 仍然说它缺失”这类困惑的常见来源。如果你把 Fluentd 作为容器运行,请在构建基于官方镜像的自己的镜像时安装插件,而不是手工装进正在运行的容器,这样容器下次启动时插件才真正存在。
配置输出
安装之后,Elasticsearch 目的地只是又一个 <match> 块:
<match apache.access>
@type elasticsearch
host elasticsearch
port 9200
index_name fluentd-apache
</match>
将不同的 source 路由到不同的目的地
没有任何规定要求每个 source 都最终进入同一个地方。多个 <match> 块,每个限定于不同的标签,可以在同一份配置中把不同的数据发往不同的目的地:
<match apache.access>
@type elasticsearch
host elasticsearch
port 9200
index_name fluentd-apache
</match>
<match docker.**>
@type elasticsearch
host elasticsearch
port 9200
index_name fluentd-docker
</match>
<match app.**>
@type forward
<server>
host aggregator.internal
port 24224
</server>
</match>
应用日志进入一个索引,容器日志进入另一个,第三个 source 被转发到一个完全独立的 Fluentd 实例做进一步处理,这一切都在同一条管线中。同样,这也是让单个 source 同时发送到多个目的地的方式:定义两个匹配相同标签的独立 <match> 块(或使用 <copy> 部分),因为单条 Fluentd 管线并不局限于每个 source 只有一个输出。
缓冲:真正让交付可靠的东西
默认情况下,插件在写入其目的地之前会在内存中缓冲,这很快,但意味着 Fluentd 崩溃或重启会丢失尚未刷新的内容。对于任何不能容忍丢失传输中日志数据的场景,请改为配置显式的文件缓冲,使缓冲的条目持久化到磁盘,从而在重启后保留下来:
<match apache.access>
@type elasticsearch
host elasticsearch
port 9200
index_name fluentd-apache
<buffer>
@type file
path /var/log/fluent/buffer/apache
flush_interval 5s
retry_max_times 10
retry_wait 5s
</buffer>
</match>
这就是人们常说的”Fluentd 重启时不会丢数据”的行为,但需要说得精确一些:它之所以成立,是因为配置了文件缓冲,而不是自动成立的。使用内存缓冲时,刷新过程中崩溃仍会丢失已排队的内容。如果某个目的地的可靠交付很重要,请为它显式配置文件缓冲,而不要假定默认值已经覆盖了它。
处理暂时宕机的目的地
同样的缓冲配置也能优雅地处理后端宕机。如果 Elasticsearch(或任何目的地)变得不可达,条目会在缓冲中累积,而不是被丢弃,Fluentd 会按照 retry_wait 和 retry_max_times 所定义的计划重试交付,在此期间,如果你配置了文件缓冲,被缓冲的数据会安全地保存在磁盘上。一旦目的地恢复可达,Fluentd 会从中断的位置赶上进度。这使得短暂的网络抖动或后端重启成为一件无关紧要的事,而不是日志中的一段空白。
在最终输出之前脱敏和丰富
<filter> 仍然在匹配的输出之前运行,无论它位于 source 与 match 之间的哪个位置,因此可以在敏感数据到达目的地之前立刻将其剥离:
<filter apache.access>
@type record_transformer
remove_keys remote_addr, user_agent
</filter>
record_transformer 与用于丰富的是同一个插件(参见上一篇指南的 hostname 示例);remove_keys 只是另一个方向,删除字段而不是添加字段。这是在日志离开你自己的基础设施前往第三方目的地之前,对任何敏感信息 —— 电子邮件地址、会话令牌、内部 IP —— 进行脱敏的合理位置,而不是依赖目的地事后正确处理它。