从文件、HTTP 和容器收集日志
配置三种最常见的输入源:磁盘上的文件、HTTP 端点,以及 Docker 主机上的所有容器。
大多数真实部署需要同时具备多种输入源:一个已有的、写入日志文件的应用,一个可以改为通过 HTTP 推送日志的服务,以及主机上运行的每一个容器。本指南配置这三者,基于上一篇指南中的 source/filter/match 结构。
跟踪日志文件
tail 输入插件会像 tail -f 一样跟踪文件,在新行被写入时读取它们:
<source>
@type tail
path /var/log/apache2/access.log
pos_file /var/log/fluent/apache-access.log.pos
tag apache.access
<parse>
@type apache2
</parse>
</source>
尽管很容易被跳过,pos_file 值得认真对待:它记录了 Fluentd 已经读取到文件中的确切位置。没有它,重启 Fluentd 就意味着从头重新读取整个文件,把已经发送过的内容全部重复一遍。有了它,重启后会从上次中断的位置精确继续。插件自己的文档正是出于这个原因,将它描述为强烈推荐。
通过 HTTP 接收日志
并非所有东西旁边都能安装 Fluentd。托管的云服务、无服务器函数,以及任何在你无法于操作系统层面控制的基础设施之外的东西,都无法在它正在写入的文件旁边运行 tail 输入源。对于这些情况,http 输入插件让数据源把日志发送给 Fluentd,而不是由 Fluentd 去读取:
<source>
@type http
port 9880
bind 0.0.0.0
body_size_limit 32m
keepalive_timeout 10s
</source>
任何能发出 HTTP POST 请求的东西 —— 自定义应用、配置为转发到此处的日志库、脚本里的一条 curl 调用 —— 现在都可以把日志数据直接送达。body_size_limit 和 keepalive_timeout 值得有意识地设置,而不是任由它们取你所用版本附带的默认值,因为 HTTP 输入源会暴露给任何能到达它的流量,不受限制的请求大小或长时间保持打开的连接,正是你希望自己掌控、而不是听由默认行为的那类东西。
收集主机上每个容器的日志
Docker(以及一般的容器运行时)会将每个容器的 stdout/stderr 写入主机上 JSON 格式的日志文件,每个容器一个目录。用通配符让一个 tail 输入源指向所有这些文件,就足以收集每个容器的输出,而无需逐个配置:
<source>
@type tail
path /fluentd/log/containers/*/*-json.log
pos_file /var/log/fluent/docker.log.pos
tag docker.*
read_from_head true
<parse>
@type json
</parse>
</source>
要使其生效,主机的 Docker 日志目录(默认为 /var/lib/docker/containers)需要被实际挂载到运行 Fluentd 的地方,挂载位置要与该输入源的 path 模式所指向的路径一致。这也正是 Fluentd 作为 Kubernetes DaemonSet 的工作方式:每个节点一个 Fluentd Pod,各自挂载该节点自己的容器日志目录,因此在整个集群范围内,每个节点的容器都以相同方式被收集,无需为每个容器单独配置。
read_from_head true 告诉 Fluentd 从它尚未见过的文件的开头开始读取,这对在此输入源配置之前就已经在运行(并已在记录日志)的容器很重要;没有它,Fluentd 只会从开始监视某个文件的那一刻起读取新增的行。
用上下文丰富日志:哪个容器、哪台主机
原始的容器日志行本身并不说明它们来自哪个 pod、节点或容器;这些上下文必须附加上去,通常是使用带有 record_transformer 插件的 filter:
<filter docker.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
</record>
</filter>
这会给所有以 docker. 为标签的条目附加当前主机的名称,这使你之后能够分辨某一行日志究竟来自负载均衡器后面或集群中的哪个副本。同样的模式可以扩展到 pod 名称、命名空间,或任何值得在收集时附加、而不是事后试图重建的元数据。
独立验证每个输入源
在接入真实的输出目的地之前,先把每个输入源指向一个简单的 @type stdout match,并确认它产生了你期望的结构:
<match apache.access>
@type stdout
</match>
生成一个请求(或向文件写入一行,或向 HTTP 端点 POST,或启动一个容器),并观察解析后、带标签的输出出现。在叠加过滤器或真实目的地之前,先单独确认每个输入源,这样一旦管线后面的某处看起来不对,就更容易判断是哪个阶段引入了问题。