编辑推荐: |
本文主要介绍了基于ELK构建一个云时代收集并分析log的解决方案的相关内容,希望对您能有所帮助。
本文来自微信公众号ithellas,由火龙果软件Luca编辑、推荐。 |
|
一、概述
随着现在各种软件系统的复杂度越来越高,特别是部署到云上之后,再想登录各个节点上查看各个模块的log,基本是不可行了。因为不仅效率低下,而且有时由于安全性,不可能让工程师直接访问各个物理节点。而且现在大规模的软件系统基本都采用集群的部署方式,意味着对每个service,会启动多个完全一样的POD对外提供服务,每个container都会产生自己的log,仅从产生的log来看,你根本不知道是哪个POD产生的,这样对查看分布式的日志更加困难。
所以在云时代,需要一个收集并分析log的解决方案。首先需要将分布在各个角落的log收集到一个集中的地方,方便查看。收集了之后,还可以进行各种统计分析,甚至用流行的大数据或maching
learning的方法进行分析。当然,对于传统的软件部署方式,也需要这样的log的解决方案,不过本文主要从云的角度来介绍。
ELK就是这样的解决方案,而且基本就是事实上的标准。ELK是三个开源项目的首字母缩写,如下:
E: Elasticsearch
L: LogStash
K: Kibana
LogStash的主要作用是收集分布在各处的log并进行处理;Elasticsearch则是一个集中存储log的地方,更重要的是它是一个全文检索以及分析的引擎,它能让用户以近乎实时的方式来查看、分析海量的数据。Kibana则是为Elasticsearch开发的前端GUI,让用户可以很方便的以图形化的接口查询Elasticsearch中存储的数据,同时也提供了各种分析的模块,比如构建dashboard的功能。
我个人认为将ELK中的L理解成Logging Agent更合适。Elasticsearch和Kibana基本就是存储、检索和分析log的标准方案,而LogStash则并不是唯一的收集log的方案,Fluentd和Filebeats也能用于收集log。所以现在网上有ELK,EFK之类的缩写。
一般采用的架构如下图所示。通常一个小型的cluster有三个节点,在这三个节点上可能会运行几十个甚至上百个容器。而我们只需要在每个节点上启动一个logging
agent的实例(在kubernetes中就是DaemonSet的概念)即可。
二、Filebeats、LogStash、Fluentd三者的区别和联系
这里有必要对Filebeats、LogStash和Fluentd三者之间的联系和区别做一个简要的说明。Filebeats是一个轻量级的收集本地log数据的方案,官方对Filebeats的说明如下。可以看出Filebeats功能比较单一,它仅仅只能收集本地的log,但并不能对收集到的Log做什么处理,所以通常Filebeats通常需要将收集到的log发送到Logstash做进一步的处理。
Filebeat is a log data shipper for local files. Installed
as an agent on your servers, Filebeat monitors the
log directories or specific log files, tails the files,
and forwards them either to Elasticsearch or Logstash
for indexing
LogStash和Fluentd都具有收集并处理log的能力,网上有很多关于二者的对比,提供一个写得比较好的文章链接如下。功能上二者旗鼓相当,但LogStash消耗更多的memory,对此LogStash的解决方案是使用Filebeats从各个叶子节点上收集log,当然Fluentd也有对应的Fluent
Bit。
https://logz.io/blog/fluentd-logstash/
另外一个重要的区别是Fluentd抽象性做得更好,对用户屏蔽了底层细节的繁琐。作者的原话如下:
Fluentd’s approach is more declarative whereas Logstash’s
method is procedural. For programmers trained in procedural
programming, Logstash’s configuration can be easier
to get started. On the other hand, Fluentd’s tag-based
routing allows complex routing to be expressed cleanly.
虽然作者说是要中立的对二者(LogStash和Fluentd)进行对比,但实际上偏向性很明显了:)。本文也主要基于Fluentd进行介绍,不过总体思路都是相通的。
额外说一点,Filebeats、LogStash、Elasticsearch和Kibana是属于同一家公司的开源项目,官方文档如下:
https://www.elastic.co/guide/index.html
Fluentd则是另一家公司的开源项目,官方文档如下:
https://docs.fluentd.org/v1.0/articles/quickstart
三、logging agent (Fluentd)
前面已经说过,只要在每个物理节点上启动一个logging agent的实例即可(本文以fluentd为例)。但是在每个节点上,往往运行着几十个甚至上百个容器,而且提供不同的服务,每个节点上的logging
agent会收集到当前节点上所有容器的log。而有时我们只关心其中一部分容器产生的log,有时也需要对收集的log做一些简单的处理,这时就需要对fluentd配置一些filter。如果不需要做任何过滤或其它处理,那filebeat就能满足需求了。
Fluentd以pipeline的方式来处理收集到的每一条log消息,由各种plugin来处理log。典型的处理逻辑如下图所示:
首先是input plugin收集log,fluentd既可以直接读取log文件中的内容,也可以接受socket传过来的log消息。关于input
plugin的具体信息,可以参考下面的链接:
https://docs.fluentd.org/v1.0/articles/input-plugin-overview
例如下面的例子是从/var/lig/docker/container/*/*.log中读取log消息,至于为什么要从这个目录读取log,后面会解释。
<source>
@type tail
path /var/lib/docker/containers/*/*.log
tag fluentd <parse>
@type json
time_key time
keep_time_key true </parse>
refresh_interval 5 </source> |
grep filter作用是过滤掉我们不感兴趣的log消息。这个很好理解,就和我们平时用grep命令搜索文件内容一样。关于grep
filter,参考下面的链接:
https://docs.fluentd.org/v1.0/articles/filter_grep
例如下面的例子就是只有匹配模式"myproject.*hello"的log消息才会保留下来,进入pipeline的下一个环节继续处理。
<filter **>
@type grep <regexp>
key log
pattern myproject.*hello </regexp>
</filter> |
parser plugin则是告诉fluentd按照特定的格式解析log消息,具体可参考下面的链接:
https://docs.fluentd.org/v1.0/articles/parser-plugin-overview
例如下面的例子就是让fluentd按照json格式解析某个field的内容。这里下文会进一步解释。
<filter **>
@type parser
format json
key_name log
reserve_data true
hash_value_field log </filter> |
最后就是通过output plugin将log数据发送出去,具体参考:
https://docs.fluentd.org/v1.0/articles/output-plugin-overview
下面的例子就将处理之后的log发送到elasticsearch,
<match fluentd>
@type elasticsearch
host elasticsearch
port 9200
flush_interval 10s </match> |
四、Docker logging driver
讨论logging,就无法避开Docker logging driver这个话题。现在部署在云上各种应用都是运行在容器中的,当我们的应用将log消息输出到stdout或者stderr的时候,Docker
engine是按照配置的logging driver来将log消息输出到特定的目的地。Docker支持的logging
driver有很多,默认使用的logging driver是json-file,也就是说将各个应用输出到stdout或stderr的log默认按照json格式输出到下面的文件中:
/var/lib/docker/containers/${container_id}/*.log |
这就是为什么为fluentd配置的input plugin要从/var/lib/docker/containers/*/*.log读取log的原因。
另外,json-file会将应用程序产生的每条log消息,封装到field
"log"中。例如假设某个APP输出下面的log到stdout,
{"level":"info",
"msg":"hello world"} |
的
那么json-file会产生下面的log,
{"log":
"{\"level\":\"info\",
\"msg\":\"hello world\"}",
"stream":"stdout","time":"2018-01-27T02:38:16.382229755Z"} |
这就是为什么上面要为fluentd配置一个parser的原因。
可以编辑/etc/docker/daemon.json来修改默认的logging
driver,
$cat /etc/docker/daemon.json
{ "log-driver": "json-file",
"log-opts": { "max-size":
"10m"
}
} |
可以参考下面的链接,来了解更多的关于docker logging driver的内容,
https://docs.docker.com/config /containers/logging/configure/
五、如何支持multi-tenant
部署在cluster中的应用一般都会多个租户提供服务,那么如何区分不同租户的数据就是不得不面对的安全问题。也就是说每个租户只能看到自己的数据(自然包含本文讨论的log数据)。Elasticsearch对这个问题的建议的解决方案就是为不同的租户建立不同的Index。
Index就是一组具有相同特性的文档的集合。关于Elasticsearch的基本概念,请参考下面的链接:
https://www.elastic.co/guide/en /elasticsearch/reference/current/_basic_concepts.html
首先需要改写上面为fluentd配置的output plugin,使fluentd可以智能地将不同tenant的log消息发送到elasticsearch中不同的index。我们只要在每条log消息中加入tenantid的值,output
plugin解析出该值后,就可以很容易的分别不同tenant的数据。
Kibana自从版本6.0之后,Kibana通过X-Pack提供了基于角色的访问控制(Role-based
Access Control),就是可以给不同的用户分配不同的角色,而针对不同的角色赋予访问不同Index的权限。这样就控制了每个租户登录后只能访问属于自己的Index中的数据。
将本文开始的架构图针对multi-tenant可以做如下修改,
六、数据分析
对于收集上来的log数据,可以从下面三个方面进行分析:
1、Kibana提供了一个交互式的查询接口,可以近乎实时的查询我们感兴趣的log数据。
2、借助Kibana的visualization和dashboard可以很方便地对log数据进行可视化展现。
3、利用X-Pack提供的Maching learning进行大数据分析。
这里暂时不深入展开讨论,将来可能会分享更多相关的心得和体会。 |