Fluentd 日志拆分

大部分 Kubernetes 應(yīng)用,我們都會(huì)將不同類型的日志記錄到 stdout 中,比如在《Fluentd 簡(jiǎn)明教程》中提到的應(yīng)用日志和訪問日志,這兩者都是非常重要的信息,因?yàn)樗麄兊娜罩靖袷讲灰粯樱晕覀冃枰獙?duì)他們分別進(jìn)行解析,這就需要我們使用到 Fluentd 的一些插件來配合。本文我們將介紹如何將這些日志拆分為并行的日志流,以便可以進(jìn)一步處理它們。
設(shè)置
同樣使用前面我們配置的倉(cāng)庫(kù) https://github.com/r1ckr/fluentd-simplified,Clone 后會(huì)得到如下所示的目錄結(jié)構(gòu):
fluentd/
????├──?etc/
????│???└──?fluentd.conf
????├──?log/
????│???└──?kong.log
????└──?output/
其中 output/ 目錄是 fluentd 最后輸出文件的地方,在 log/kong.log 中,里面是一些運(yùn)行 kong 容器的日志,日志格式如下所示:
{
????"log":"2019/07/31?22:19:52?[notice]?1#0:?start?worker?process?32\n",
????"stream":"stderr",
????"time":"2019-07-31T22:19:52.3754634Z"
}
etc/fluentd.conf 就是我們的 fluentd 配置文件,里面包含輸入和輸出配置,首先我們先來運(yùn)行 fluentd 容器。
#?Input
<source>
??@type?tail
??path?"/var/log/*.log"
??tag?"ninja.*"
??read_from_head?true
??<parse>
????@type?"json"
????time_format?"%Y-%m-%dT%H:%M:%S.%NZ"
????time_type?string
??parse>
source>
#?Filter?(grep)
<filter?ninja.var.log.kong**?>
??@type?grep
??<regexp>
????key?log
????pattern?/HTTP/
??regexp>
filter>
#?Output
<match?**>
??@type?file
??path?/output/example
??append?true
??<buffer>
????timekey?1d
????timekey_use_utc?true
????timekey_wait?1m
??buffer>
match>
運(yùn)行 fluentd
使用下面的命令在項(xiàng)目根目錄下面啟動(dòng)一個(gè) fluentd 的 Docker 容器:
$?docker?run?-u?root?-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
注意上面的運(yùn)行命令和我們要掛載的卷
etc/是掛載在容器內(nèi)部的/fluentd/etc/目錄下的,以覆蓋 fluentd 的默認(rèn)配置。log/掛載到/var/log/,最后在容器里面掛載到/var/log/kong.log。output/掛載到/output,以便能夠看到 fluentd 寫入磁盤的內(nèi)容。
運(yùn)行容器后,會(huì)出現(xiàn)如下所示的信息:
2020-10-16?03:35:28?+0000?[info]:?#0?fluent/log.rb:327:info:?fluentd?worker?is?now?running?worker=0
這意味著 fluentd 已經(jīng)啟動(dòng)并運(yùn)行了?,F(xiàn)在我們知道了 fluentd 是如何運(yùn)行的了,接下來我們來看看配置文件的一些細(xì)節(jié)。
拆分日志
現(xiàn)在我們的日志已經(jīng)在 fluentd 中工作了,我們可以開始對(duì)它做一些更多的處理。
現(xiàn)在我們只有一個(gè)輸入和一個(gè)輸出,所以我們所有的日志都混在一起,我們想從訪問日志中獲取更多的信息。要做到這一點(diǎn),我們首先要確定哪些是訪問日志,比方說通過 /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}.*/ (將查找以 IP 開頭的行)來 grepping 我們所有的訪問日志,并將排除應(yīng)用日志,配置如下所示。
<match?ninja.var.log.kong**>
??@type?rewrite_tag_filter
??<rule>
????key?log
????pattern?/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}.*/
????tag?access.log.${tag}
??rule>
??<rule>
????key?log
????pattern?.*
????tag?app.log.${tag}
??rule>
match>
上面的配置信息:
:表示我們將匹配所有以ninja.var.log.kong開頭的標(biāo)簽。@type rewrite_tag_filter:我們將要使用的插件類型。第一個(gè) 部分:我們?cè)谌罩局衅ヅ?/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}.*/正則表達(dá)式,對(duì)于找到的每個(gè)日志行,添加一個(gè)access.log.的標(biāo)簽前綴,比如access.log.ninja.var.log.kong.log這樣的標(biāo)簽。第二個(gè) 部分:我們?cè)谌罩局衅ヅ淦渌袃?nèi)容,添加app.log這樣的標(biāo)簽前綴,類似于app.log.ninja.var.log.kong.log這樣的標(biāo)簽。
通過這個(gè)配置,我們?cè)诠艿乐性黾恿艘粋€(gè)新的配置。

現(xiàn)在,如果我們?nèi)ミ\(yùn)行容器則會(huì)報(bào)錯(cuò),因?yàn)?rewrite_tag_filter 不是 fluentd 的核心插件,所以我們?cè)谶\(yùn)行 fluentd 之前先安裝它。
$?docker?run?-u?root?-ti?--rm?\
-v?$(pwd)/etc:/fluentd/etc?\
-v?$(pwd)/log:/var/log/?\
-v?$(pwd)/output:/output?\
fluent/fluentd:v1.11-debian-1?bash?-c?"gem?install?fluent-plugin-rewrite-tag-filter?&&?fluentd?-c?/fluentd/etc/fluentd.conf?-v"
我們?cè)谶\(yùn)行 fluend 命令之前先執(zhí)行 gem install fluent-plugin-rewrite-tag-filter 來安裝插件。
現(xiàn)在我們應(yīng)該在輸出日志中看到一些不同的新了,讓我們檢查一下之前同樣的6行日志。
2020-05-10T17:04:17+00:00?app.log.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?app.log.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?app.log.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?access.log.ninja.var.log.kong.log?{"log":"35.190.247.57?-?-?[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?access.log.ninja.var.log.kong.log?{"log":"35.190.247.57?-?-?[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?access.log.ninja.var.log.kong.log?{"log":"35.190.247.57?-?-?[10/May/2020:17:06:24?+0000]?\"GET?/users?HTTP/1.1\"?499?0?\"-\"?\"curl/7.59.0\"\n","strea
與之前唯一不同的是,現(xiàn)在訪問日志和應(yīng)用日志有不同的標(biāo)簽l ,這對(duì)它們做進(jìn)一步的處理是非常有用的。
解析訪問日志
接下來我們來添加一個(gè)解析器插件來從訪問日志中提取有用的信息,在 rewrite_tag_filter 之后使用這個(gè)配置。
<filter?access.log.ninja.var.log.kong.log>
??@type?parser
??key_name?log
??<parse>
????@type?nginx
??parse>
filter>
我們來分析下這個(gè)配置:
我們明確地過濾了 access.log.ninja.var.log.kong.log這個(gè)標(biāo)簽過濾器的類型是 parser我們將對(duì)日志內(nèi)容的 log這個(gè) key 的內(nèi)容進(jìn)行解析由于這些都是 nginx 的訪問日志,所以這里我們使用 @type nginx的解析器進(jìn)行解析。
這是我們的日志收集管道現(xiàn)在的樣子。

我們?cè)僦匦逻\(yùn)行 docker 容器,查看日志,Kong 的訪問日志應(yīng)該是這樣的了。
2020-05-10T17:04:30+00:00?access.log.ninja.var.log.kong.log?{"remote":"35.190.247.57","host":"-","user":"-","method":"GET","path":"/","code":"404","size":"48","referer":"-","agent":"curl/7.59.0","http_x_forwarded_for":""}
這是之前日志中的第一個(gè)訪問日志,標(biāo)簽是一樣的,但是現(xiàn)在日志內(nèi)容完全不同了,我們的鍵從 log 和 stream,變成了 remote、host、user、 method、path、code、size、referer、agent 和 http_x_forwarded_for,如果我們把這個(gè)日志輸出到 elasticsearch 中,我們就可以通過method=GET 或者其他任何組合方式進(jìn)行過濾了。
Geoip 插件
此外,我們還可以使用 geoip 插件應(yīng)用到 remote 字段中,來獲取訪問我們接口的地理位置。配置如下所示:
#?Geoip?過濾器
<filter?access.log.ninja.var.log.kong.log>
??@type?geoip
??#?指定一個(gè)或多個(gè)有?ip?地址的?geoip?查詢字段
??geoip_lookup_keys?remote
??#?Set?adding?field?with?placeholder?(more?than?one?settings?are?required.)
??<record>
????city????????????${city.names.en["remote"]}
????latitude????????${location.latitude["remote"]}
????longitude???????${location.longitude["remote"]}
????country?????????${country.iso_code["remote"]}
????country_name????${country.names.en["remote"]}
????postal_code?????${postal.code["remote"]}
??record>
??#?避免?elasticsearch?的?stacktrace?錯(cuò)誤
??skip_adding_null_record??true
filter>
再來分析下這個(gè)配置:
明確過濾 access.log.ninja.var.log.kong.log這個(gè)標(biāo)簽的日志。過濾器的類型是 geoip 我們將使用日志中的 remote 這個(gè) key 來進(jìn)行 geoip 查找 其余的都是標(biāo)準(zhǔn)配置
同樣要在 docker 容器中使用 geoip 這個(gè)插件,我們需要首先安裝,但是這個(gè)插件的安裝稍微麻煩一點(diǎn),因?yàn)槲覀冃枰惭b一些 debian 依賴包,我們可以使用如下所示的命令來進(jìn)行安裝配置(當(dāng)然也可以重新自定義一個(gè) Docker 鏡像):
$?docker?run?-u?root?-ti?--rm?\
-v?$(pwd)/etc:/fluentd/etc?\
-v?$(pwd)/log:/var/log/?\
-v?$(pwd)/output:/output?\
fluent/fluentd:v1.11-debian-1?bash?-c?"apt?update?&&?apt?install?-y?build-essential?libgeoip-dev?libmaxminddb-dev?&&?gem?install?fluent-plugin-rewrite-tag-filter?fluent-plugin-geoip?&&?fluentd?-c?/fluentd/etc/fluentd.conf?-v"
我們可以看到在啟動(dòng)命令中我們添加了額外的 apt 命令,并添加了一個(gè) fluent-plugin-geoip 插件,運(yùn)行該命令后,我們可以在日志中看到一些額外的字段,這就是 geoip 插件的配置結(jié)果:
2020-05-10T17:04:30+00:00?access.log.ninja.var.log.kong.log?{"remote":"35.190.247.57","host":"-","user":"-","method":"GET","path":"/","code":"404","size":"48","referer":"-","agent":"curl/7.59.0","http_x_forwarded_for":"","city":"Mountain?View","latitude":37.419200000000004,"longitude":-122.0574,"country":"US","country_name":"United?States","postal_code":"94043"}
總結(jié)
在這篇文章中,我們使用 fluent-plugin-retwrite-tag-filter 插件來拆分我們的日志,并使用 fluent-plugin-geoip 插件來獲取訪問我們接口的客戶端地理位置信息,fluentd 功能是非常強(qiáng)大的,有著豐富的插件可以幫助我們實(shí)現(xiàn)很多強(qiáng)大的需求。
原文鏈接:https://medium.com/swlh/fluentd-splitting-logs-2a778cd6bdfa
訓(xùn)練營(yíng)推薦


?點(diǎn)擊屏末?|?閱讀原文?|?即刻學(xué)習(xí)