日志收集神器:Fluentd 的简明指南,收好了~
优采云 发布时间: 2022-08-13 23:42日志收集神器:Fluentd 的简明指南,收好了~
因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩分享
如果你的应用运行在分布式架构上,你很可能会使用集中式日志系统来收集它们的日志,其中我们使用比较广泛的一个工具就是 fluentd,包括在容器化时代用来收集 Kubernetes 集群应用日志, fluentd 也是使用非常多的。
本文我们将解释它是如何工作的,以及如何根据需求来调整 fluentd 配置。
基本概念我们可能有在 bash 中执行过 tail -f myapp.log | grep “what I want” > example.log 这样的命令,这其实就是 fluentd 比较擅长做的事情,tail 日志或者接收某种形式的数据,然后过滤转换,最后发送到后端存储中,我们可以将上面的命令分成多段来分析。输入tail -f myapp.log
我们要对一个文件进行长期的 tail,每当有什么日志信息被添加到文件中,它就会显示在屏幕上。这在 fluentd 中叫做输入插件,tail 只是其中之一,但还有很多其他可用的插件。
过滤| grep "what I want"
在这里,我们从尾部 -f 的输出中,只过滤包含我们想要的字符串的日志行,在 fluentd 中这叫做过滤插件。
输出> example.log
在这里,我们将 grep 命令过滤后的输出保存到一个名为 example.log 的文件中。在 fluentd 中,这就是输出插件,除了写到文件之外,fluentd 还有很多插件可以把你的日志输出到其他地方。
这就是 fluentd 的最基本的运行流程,你可以读取日志,然后处理,然后把它发送到另一个地方做进一步的分析。接下来让我们用一个小 demo 来实践这些概念,看看这3个插件是如何在一起工作的。
Demo
在这个demo 中,我们将使用 fluentd 来读取 docker 应用日志。
设置这里我们将 demo 相关的配置放置到了 Github 仓库:,克隆后最终会得到以下目录结构。fluentd/ ├── etc/ │ └── fluentd.conf ├── log/ │ └── kong.log └── output/
其中的 output/ 是 fluentd 写入日志文件的目录,在 log/kong.log 中,有一些来自本地运行的 kong 容器的日志,它们都是 docker 格式的日志。
{ "log":"2019/07/31 22:19:52 [notice] 1#0: start worker process 32\n", "stream":"stderr", "time":"2019-07-31T22:19:52.3754634Z"}
这个文件的每一行都是一个 json 文件,这就是 docker 默认驱动的日志格式。我们将对这个文件进行 tail 和解析操作,它有应用日志和访问日志混合在一起。我们的目标是只获取访问日志。etc/fluentd.conf 是我们的 fluentd 配置,其中有一个输入和一个输出部分,我们稍后会仔细来分析,首先运行 fluentd 容器。
运行 fluentd$ chmod 777 output/$ docker run -ti --rm \-v $(pwd)/etc:/fluentd/etc \-v $(pwd)/log:/var/log/ \-v $(pwd)/output:/output \fluent/fluentd:v1.11-debian-1 -c /fluentd/etc/fluentd-simplified-finished.conf -v
注意上面的运行命令和我们要挂载的卷
运行容器后,会出现如下所示的错误信息:
2020-10-16 03:35:28 +0000 [info]: #0 fluent/log.rb:327:info: fluentd worker is now running worker=0这意味着 fluentd 已经启动并运行了。现在我们知道了 fluentd 是如何运行的了,接下来我们来看看配置文件的一些细节。Fluentd 配置输入输出首先查看 input 部分 @type tail path "/var/log/*.log" tag "ninja.*" read_from_head true @type "json" time_format "%Y-%m-%dT%H:%M:%S.%NZ" time_type string >
我们来仔细查看下这几个配置:
然后是输出 output 部分的配置。
# Output**> @type file path /output/example.log timekey 1d timekey_use_utc true timekey_wait 1m
在这个配置中,有两个重要的部分。
output├── example│ ├── buffer.b5b1c174b5e82c806c7027bbe4c3e20fd.log│ └── buffer.b5b1c174b5e82c806c7027bbe4c3e20fd.log.meta├── example.20190731.log└── example.20200510.log有了这个配置,我们就有了一个非常简单的输入/输出管道了。
现在我们可以来看看 fluentd 创建的一个文件中的一些日志 example.20200510.log。
2020-05-10T17:04:17+00:00 ninja。var。log。kong。log {"log":"2020/05/10 17:04:16 [warn] 35#0: *4 [lua] globalpatches。lua:47: sleep(): executing a blocking 'sleep' (0。004 seconds), context: init_worker_by_lua*\n","stream":"stderr"}2020-05-10T17:04:17+00:00 ninja。var。log。kong。log {"log":"2020/05/10 17:04:16 [warn] 33#0: *2 [lua] globalpatches。lua:47: sleep(): executing a blocking 'sleep' (0。008 seconds), context: init_worker_by_lua*\n","stream":"stderr"}2020-05-10T17:04:17+00:00 ninja。var。log。kong。log {"log":"2020/05/10 17:04:17 [warn] 32#0: *1 [lua] mesh。
lua:86: init(): no cluster_ca in declarative configuration: cannot use node in mesh mode, context: init_worker_by_lua*\n","stream":"stderr"}2020-05-10T17:04:30+00:00 ninja。var。log。kong。log {"log":"172。17。0。1 - - [10/May/2020:17:04:30 +0000] \"GET / HTTP/1。1\" 404 48 \"-\" \"curl/7。59。0\"\n","stream":"stdout"}2020-05-10T17:05:38+00:00 ninja。var。log。kong。log {"log":"172。17。0。1 - - [10/May/2020:17:05:38 +0000] \"GET /users HTTP/1。1\" 401 26 \"-\" \"curl/7。59。0\"\n","stream":"stdout"}2020-05-10T17:06:24+00:00 ninja。var。log。kong。log {"log":"172。17。0。1 - - [10/May/2020:17:06:24 +0000] \"GET /users HTTP/1。1\" 499 0 \"-\" \"curl/7。59。0\"\n","stream":"stdout"}
注意上面的日志,每行都有3列,格式为:
log>log>
注意:标签都是 “ninja” 字符串加上目录路径和文件名,之间使用”. “分隔。
过滤
现在我们已经在 fluentd 中实现了日志的收集,接下来让我们对它进行一些过滤操作。
到目前为止,我们已经实现了前面那条命令的2个部分,tail -f /var/log/*.log 和 > example.log 工作正常,但是如果你看一下输出,我们有访问日志和应用日志混合在一起,现在我们需要实现 grep ‘what I want’ 过滤。
在这个例子中,我们只想要访问日志,丢弃其他的日志行。比如说,通过 HTTP 进行 grepping 会给我们提供所有的访问日志,并将应用日志排除在外,下面的配置就可以做到这一点。
@type grep key log pattern /HTTP/
我们来分析下这个过滤配置:
现在我们停止并再次运行容器。我们应该在输出日志中看到一些不同的日志了,没有应用日志,只有访问日志数据。
2020-05-10T17:04:30+00:00 ninja.var.log.kong.log {"log":"172.17.0.1 - - [10/May/2020:17:04:30 +0000] \"GET / HTTP/1.1\" 404 48 \"-\" \"curl/7.59.0\"\n","stream":"stdout"}2020-05-10T17:05:38+00:00 ninja.var.log.kong.log {"log":"172.17.0.1 - - [10/May/2020:17:05:38 +0000] \"GET /users HTTP/1.1\" 401 26 \"-\" \"curl/7.59.0\"\n","stream":"stdout"}2020-05-10T17:06:24+00:00 ninja.var.log.kong.log {"log":"172.17.0.1 - - [10/May/2020:17:06:24 +0000] \"GET /users HTTP/1.1\" 499 0 \"-\" \"curl/7.59.0\"\n","strea解析访问日志
为了熟悉我们的配置,下面让我们添加一个解析器插件来从访问日志中提取一些其他有用的信息。在 grep 过滤器后使用下面配置。
@type parser key_name log @type nginx
同样我们来仔细查看下这个配置:
现在我们的管道是下面这个样子了。
我们再次重新运行容器,现在的访问日志应该是这样的了。
2020-05-10T17:04:30+00:00 ninja.var.log.kong.log {"remote":"172.17.0.1","host":"-","user":"-","method":"GET","path":"/","code":"404","size":"48","referer":"-","agent":"curl/7.59.0","http_x_forwarded_for":""}
这是之前日志中的第一个访问日志,现在日志内容完全不同了,我们的键从日志流,变成了 remote、host、user、method、path、code、size、referer、agent 以及 http_x_forwarded_for。如果我们要把这个保存到 Elasticsearch 中,我们将能够通过 method=GET 或其他组合进行过滤了。
当然我们还可以更进一步,在 remote 字段中使用 geoip 插件来提取我们我们 API 的客户端的地理位置信息,大家可以自行测试,不过需要注意的时候需要我们的镜像中包含这些插件。
总结
现在我们知道了如何用 docker 来让 fluentd 读取配置文件,我们了解了一些常用的 fluentd 配置,我们能够从一些日志数据中来过滤、解析提取有用的信息。
福利
系统/网络/应用运维人员的交流圈子,不定期为您提供运维技术领域前沿资讯/培训课程/独家技术手册/运维工具/文档模板等,行业大咖分享交流/同行经验分享互动,期待你的加入!扫码即可加入。
随着材料不断增多社群会不定期涨价早加入更优惠
扫码添加客服微信免费获取以下资料:
1.网络安全技术资料--回复"1"
2.容器云运维资料包--回复"2"
3.Python学习资料--回复"3"
4.Devops学习视频课件--回复"4"
5.Linux运维技术资料--回复"5"