正文
以下内容由word文档直接导入,虽然排版差劲一点,但是可以方便大家可以在线查阅。
K8s 容器日志采集 – fluent bit
Fluentd原先是ruby+c写的,现官方又做了一个新产品叫fluentd bit是纯C写的。
官方的比较:Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
观察了一圈,阿里云是用的fluent bit,这个新产品和k8s的结合非常深,配置起来更简单粗暴,性能高,资源占用少,无非是发展时间不长,所以我还是用fluent bit来采集k8s的container输出。
而且这是纯C写的,找问题也有能力,ruby的fluentd就完全不懂了。
官方文档:Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
容器日志格式
观察/var/log/containers/中的容器输出日志,统一采用了json结构,这是docker默认的行为:
{“log”:”I1220 02:52:42.276818 1 controller_utils.go:1034] Caches are synced for scheduler controller\n”,”stream”:”stderr”,”time”:”2018-12-20T02:52:42.281400963Z”}
三个字段,log,stream,time,对我们有意义的就是log,也就是容器应用输出的原始日志,我们只要把整个json结构丢到ES里面就可以查问题了。
当然,仅仅这3个字段不够,还需要知道容器属于哪个POD,信息越详细越好,这些fluent-bit都通过插件支持好了。
工作流程
整体流程:
Input
有好多input插件可以用,最常见的就是读磁盘上的文件:
Parser
Input把原始数据取回来,接着就得走parser。
比如input读进来:
192.168.2.20 – – [28/Jul/2006:10:27:10 -0300] “GET /cgi-bin/try/ HTTP/1.0” 200 3395
需要Parser插件把日志行解析成结构化的:
{
“host”: “192.168.2.20”,
“user”: “-“,
“method”: “GET”,
“path”: “/cgi-bin/try/”,
“code”: “200”,
“size”: “3395”,
“referer”: “”,
“agent”: “”
}
常见的parser一种就是json,也就是说原始日志就是个json格式,比如docker的容器日志。
通用的就是regex parser,正则提取所需信息到各种字段里。
Filter
对结构化数据进行处理,提供了多种插件。
Buffer
Filter过后的数据,输出到output之前可以缓冲一下,比如打包发送。
Routing
INPUT的时候可以给日志打tag,然后OUTPUT的时候可以Match匹配对应tag,Match支持通配。
Output
把日志发到某个地方去,比如ES,Kafaka。
配置采集/var/log/containers/*
我们将参考开源项目:GitHub - fluent/fluent-bit-kubernetes-logging: Fluent Bit Kubernetes Daemonset
它配置了daemonset的fluent-bit,直接跑到k8s集群里,采集每个node上的/var/log/containers目录,日志发到ES存储。
打开这个页面:Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual,我们要用到的基本语法都在里面,我们对照着快速上手。
分析fluent-bit配置
接下来,我们分析一下开源的配置文件:fluent-bit-kubernetes-logging/fluent-bit-configmap.yaml at master · fluent/fluent-bit-kubernetes-logging · GitHub
service
[SERVICE] | |
Flush 1 | |
Log_Level info | |
Daemon off | |
Parsers_File parsers.conf | |
HTTP_Server On | |
HTTP_Listen 0.0.0.0 | |
HTTP_Port 2020 |
Flush是刷新间隔,buffer里的数据每隔1秒写到output插件里,也就是写到ES里。
Log_level是fluent-bit的日志级别
Daemon写Off,因为fluent-bit一会要跑到k8s里,作为container的入口程序,不能后台运行。
没配置log_file,估计默认fluent-bit输出日志到终端。
Parsers_file指向了另外一个配置文件,里面配置所有的parser。
Include
这是包含其他文件的意思,我们接着看input-kubernetes.conf。
Input
Name指定了input插件的类型,这里是tail类型,也就是扫描文件增量。
Tail插件的配置说明在这里: Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
Path用通配符指定要抓取的文件。
Tag就是给采集的日志打个标签,kube.*中的*会根据Path被替换为var.log.containers.xxx。
DB是记录哪个文件采集到哪一行了。
一旦buffer里的数据超过Mem_buf_limit,tail就会暂停采集,直到buffer数据被flush到output。
Parser指定了解析器,也就是input -> parser的这个 关系,一会我们会看到docker这个parser。
Skip_long_lines跳过太长的行,默认是32K,有其他参数可以配置这个大小。
Refresh_interval是定时扫描磁盘上的新文件的间隔,这里是10秒。
Parser
Input把采集的原始数据交给parser,刚才配置的parser叫做docker,在Parser.conf中:
Parser的配置说明在这里: Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
Name就是给这个parser起个名字,这个parser就是做这样一个事情:
把Input原始日志,按照format解析成一种内部的格式(k-v字典),同时把时间戳加上。
Format是input传来的日志的原始格式,docker日志都是json结构,所以直接json decode即可解析为内部k-v的日志结构。
Time_key指定了把docker日志中的time字段按照time_format进行解析得到unix时间戳,time_keep会time字段不删除,也就是除了内部时间戳以外,在k-v 字典里还有一个time字段。
docker把容器的输出行放在log字段里,但是放之前估计做了一次escape,所以docker日志文件长这样:
{“log”:”{\”status\”: \”up and running\”}\r\n”,”stream”:”stdout”,”time”:”2018-03-09T01:01:44.851160855Z”}
做json decode后这样:
所以即便使用json parser,仍旧需要对log字段做一次unesacape,所以decode_fields_as就是这个作用,它对json decode后的log字段做了一次解转义,完全属于正常操作。
filter
Parser得到的内部K-v字典,会经过filter。
Filter会根据tag机制去匹配日志,因为source的时候打上了kube.前缀的tag,所以就会匹配这个filter。
文档在这里: Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
Name是插件的类型,就是kubernetes插件。
这个插件会根据tag提取出k8s namespace,pod name, container name, container id这些信息,这些在input阶段生成的tag中可以得到,因为tag是根据tail的path动态生成的。
然后这个插件还会监听k8s apiserver,获知这个pod的k8s labels和annotations,都添加到k-v字典中去。
所以,经过这个filter插件,除了原先docker日志中的time,stream,log三个k-v,又会追加一些k8s字段,最终所有这些kv会存到ES中,对于容器日志来说,在ES中的mapping是稳定的。
回到filter配置中:
Kube_url配置了apiserver的地址,fluent-bit会以daemonset运行在k8s的所有node上,可以直接用in-cluster domain访问apiserver,但需要有足够的权限,我们在配置daemonset的时候会给fluent-bit配置serviceaccount,fluent-bit默认就会加载标准路径下的token。
Merge_log会检查指定的字段,如果是字段是一个json map,就把内部字段提到外部k-v中,这对ES来说就会出现动态mapping问题,也许不同的容器输出的同名字段类型不同,导致ES存储日志失败,我建议off掉。
K8S-Logging.Parser没找到相关文档,保留着吧。
Output
最后就是输出环节:
Name就是es插件: Fluent Bit v1.8 Documentation - Fluent Bit: Official Manual
Match所有的tag,或者kube.*也可以。
Host,port指定ES地址,这里可以通过${}的形式引用环境变量,稍后在配置daemonset的时候传进去就行。
Logstash_format就是输出前按logstash的风格输出字段:logstash message format · GitHub
就是最终ES结构式这样的一个外层结构。
一旦开始logstash_format,ES的index就会按照logstash- YYYY.MM.DD 的风格滚动,具体都可以根据fluent bit手册配置。
实际操作
现在实际操作一下看看。
安装java: apt-get install openjdk-8-jdk
先下载ES: Download Elasticsearch | Elastic
修改sysctl -w vm.max_map_count=262144
修改config/elasticsearch.yml,让network.host: 0.0.0.0
启动ES:nohup bin/elasticsearch &
根据fluent bit kubernetes项目指引:GitHub - fluent/fluent-bit-kubernetes-logging: Fluent Bit Kubernetes Daemonset
先把serviceaccount以及role做好:
$ kubectl create namespace logging
$ kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-service-account.yaml
$ kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role.yaml
$ kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/fluent-bit-role-binding.yaml
然后把fluent bit的配置文件传到configmap里:
kubectl create -f https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-configmap.yaml
下载fluent bit的daemonset配置:
wget https://raw.githubusercontent.com/fluent/fluent-bit-kubernetes-logging/master/output/elasticsearch/fluent-bit-ds.yaml
修改一下ES的地址就行,其他都是通用的:
然后apply一下,可以看到volume把node的日志目录映射到fluent bit容器里了。
查看pod启动情况:kubectl get pod –all-namespaces,过一会就会拉起来了。
然后看一下fluent bit日志:
root@ubuntu:~/deploy/fluentd# kubectl logs fluent-bit-glpnw -n logging
Fluent-Bit v0.14.9
Copyright (C) Treasure Data
[2018/12/20 09:09:15] [ info] [engine] started (pid=1)
[2018/12/20 09:09:15] [ info] [filter_kube] https=1 host=kubernetes.default.svc.cluster.local port=443
[2018/12/20 09:09:15] [ info] [filter_kube] local POD info OK
[2018/12/20 09:09:15] [ info] [filter_kube] testing connectivity with API server…
[2018/12/20 09:09:16] [ info] [filter_kube] API server connectivity OK
[2018/12/20 09:09:16] [ info] [http_server] listen iface=0.0.0.0 tcp_port=2020
一看就是启动成功,连上了apiserver。
然后看一下ES里有日志没:
索引名默认是logstash-日期,打印几条记录看看:
curl localhost:9200/logstash-2018.12.20/_search?pretty
很完善的日志,遵循logstash+kibana规范。