容器内应用日志采集方案介绍与实践

作者简介:crystonesc,研究PaaS运维开发方向,博客地址:https://blog.csdn.net/crystonesc

本文主要分为两部分,第一部分介绍容器内日志收集主要的解决方案,并对各解决方案的优劣进行比较。第二部分首先介绍镜像内集成日志采集组件部署方案,再介绍Sidecar(边车)模式日志采集部署方案。文章中若有不正之处,请指出。

第一部分:容器内日志收集主要的解决方案

方案1.在宿主机上实现日志采集(Logging at the node Level)

方案1的实现方式如下图1所示,通过在容器所在的宿主机部署日志采集插件(Log-collector)来对容器中的日志进行采集,采集预处理后输出到外部的消息队列当中(入Kafka等),再通过类似于ELK的框架进行处理,最终日志持久化到ElasticSearch当中。接下来将详述该实现方式。

图1

我们知道,容器内应用输出到标准输出(stdout)和(标准错误输出)stderr的日志会被容器引擎所处理,例如docker就可以配置其日志引擎(logging driver),默认的logging driver是json-file,那么通过在宿主机中查看对应容器的logs文件,就可以查看到应用容器输出到标准输出的日志内容。同样在Kubernetes环境下也是一样的,pod内应用容器打印到控制台的日志也会被按照json格式存储在宿主机当中。基于这样的原理,如果我们在宿主机上部署日志采集工具(Log-collector),我们就能够实现容器内应用日志的采集。这样方式的部署,优点在于部署简单,日志采集资源开销小,但是会带来以下几个问题:

问题1.应用容器的日志输出到stdout和stderr,同时被容器引擎处理后存储到宿主机本地,随着日志的增多(如果应用容器不停止),会大量占用宿主机磁盘空间。

解决办法:引入滚动日志工具,将日志进行切割,并滚动生成,这样落盘的日志就不会一直消耗本地存储空间。

问题2.我们在宿主机上进行日志采集,会统一部署一套日志采集工具,那么如何来标记日志是属于哪个应用以及日志属于哪个容器?

解决办法:对日志采集工具进行开发或者引入开源的日志采集工具,例如:阿里的log-pilot ( https://github.com/AliyunContainerService/log-pilot )。核心思路是日志采集工具能够通过容器引擎提供的API(如:docker api)监听到容器的启动和停止,从而能建立容器与日志的对应关系,从而在采集日志的时候标识出日志属于哪个容器。那么如何标识日志属于哪个应用呢?实现方式较多,既可以通过应用层在输出日志时进行标识,也可以在启动容器的时候通过环境变量来标识容器所属应用。

问题3.json-file格式的日志,对于某些开发语言(例如JAVA)异常堆栈信息是被分行处理,日志采集后还需要进行二次加工。

解决办法:目前来看只能通过对日志采集工具进行二次开发或者对日志进行二次处理来解决。

可以看出,为了解决上述问题,我们不得不进行一些二次开发和处理工作,这增加了工作量和难度,所以除非有充足资源的情况下,可以采取该方案。

方案2.将镜像内集成日志采集组件部署方案(Logging at the App Level)

如图2所示,日志采集插件(Log-collector)与应用(App)部署在一起,日志采插件作为后台成运行,应用将日志写入容器环境的操作系统目录当中,日志采集插件读取日志并进行预处理,完成后送到MQ当中,最后与方案1一样,将日志持久化到ElasticSearch当中,相面详细介绍该方案。

图2

为了解决宿主机日志收集存在的问题,同时不增加工作量和难度,我们可以考虑将日志采集工具集成到应用的基础镜像之中,应用基于基础镜像来生成应用镜像,在镜像启动的时候会把日志采集工具以后台进程的方式拉起来,搜集到日志后,日志采集工具可以将日志送到消息中间件,如Kafka,再由Logstash解析入ElasticSearch,最终用户可以通过Kibana进行日志检索。基于上述方式的部署,我们将采集工具和应用部署到一起,这样我们就能轻松的在日志采集工程中标识出日志所属的应用,同时因为日志是按照文件格式落到容器内部,一般的日志采集工具都能够通过multiline的方式来处理异常堆栈信息。对于日志增多的问题,还是需要应用引入滚动日志工具来解决。

该方案优点在于实现难度小,不增加工作量,同时能够准确的标识出日志所述的应用和所属的文件,使得应用方能够更加精确的管理日志。缺点在于每个应用镜像中都会引入额外的日志采集工具,增加了应用容器额外的资源开销,同时日志采集工具和应用基础镜像紧耦合。

方案3.基于Sidecar日志采集方式(Logging base on Sidecar)

如图3所示,该方案仅针对于Kubernetes环境,将日志通过Sidecar(边车)方式部署到应用Pod中,实现日志采集,下面将详细介绍该方案。

图3

在Kubernetes中,Pod中可以存在多个容器,这些容器之间可以通过emptyDir的方式共享文件系统,基于上述的原理,我们能够将日志采集工具从应用基础镜像中剥离出来,单独部署于一个容器中,这个日志容器与应用容器存在于一个Pod之中,他们之间共享应用容器的日志文件,从而实现应用容器将日志写入本地容器内部文件,日志采集容器读取文件内容并进行解析和传输。

该方案是第二个方案的优化版本,其减小了日志采集工具与应用基础镜像的耦合,缺点在于每个应用Pod内都需要启动一个Sidecar日志采集容器,不过通过实际观察来看,该Sidecar日志采集容器只需要很小的资源,即可完成日志采集。

三种方案选择思路
三种方案各有优劣点,方案一是比较理想的实施方式,其部署方式资源开销小,同时对应用侵入小,如果能够解决日志标识等问题,该方案应该是首选。方案二和方案三实施技术难度低,但对应用侵入较大,系统资源开销也稍多一些。当在这三种方案中进行抉择的时候,需要根据实际情况进行决定。

第二部分:详细方案介绍

1.方案2部署实践(Logging at the App Level)

1.1 部署环境

  • 应用镜像: Tomcat
  • 日志采集工具: filebeat6.2.4
  • 容器引擎:Docker
  • 消息组件:Kafka

1.2 应用镜像介绍

首先看一下基础镜像的Dockerfile,可以看到在应用的镜像中添加了filebeat-6.2.4-x86_64.rpm,并进行了安装,最后通过start.sh将Tomcat进行启动,我们继续看看start.sh做了什么。

在start.sh中我们首先通过环境变量来生成Filebeat的配置文件(filebeat.yml),环境变量主要包含日志的位置、应用标识、Kafka brokers的地址和Kafka Topic名称,接下来通过后台方式启动Filebeat,完成启动后首先检查Filebeat是否启动成功,如果启动成功,则以前台方式启动Tomcat。(注意:一定要以前台方式启动Tomcat,不然可能出现应用已经退出,而容器还存在)

接下来我们看下config_filebeat.sh脚本的片段,可以看到,我们主要通过环境变量来配置filebeat.yml文件

最终生成的filebeat.yml效果如图4所示:

图4

完成上述工作后,我们的镜像就制作好了,这时候应用只需要将日志输出到指定的位置,Filebeat就采集日志并输出到Kafka当中,最后通过ELK框架完成持久化和展示分析。如图5所示,是Kibana中查询到的日志信息。

图5

2.方案3详细部署实践

2.1 部署环境

  • 容器编排平台:Kubernetes
  • 应用镜像:Tomcat
  • 日志采集工具: Fluentd
  • 消息组件:Kafka

2.2 日志采集镜像介绍

日志采集镜像是基于Fluentd +Kafka plugin来的制作的,下面是Dockerfile内容.

在entrypoint.sh当中,我们完成Fluentd配置文件的生成,entrypoint.sh内容如下:

生成的Fluentd配置文件如下所示,通过在日志中添加{SYS}.{HOST} 来标识日志所属的系统和POD IP地址,这里要说明的是,虽然Kubernetes中Pod IP地址发生变化,但是在运行态中,其能够唯一标识出日志对应的容器位置,这样方便开发者定位到输出日志的容器。

2.3 Kubernetes 应用Yaml文件介绍

完成日志采集组件镜像制作后,我们就可以进行部署,下面是Deployment Yaml文件内容,可以看到在Pod中我们启动了两个容器,一个为应用容器,另一个为日志采集容器,应用容器共享/usr/local/tomcat/logs目录给日志采集容器,日志采集容器采集该目录下的所有日志。


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/23104.html
分享到:
相关文章
条评论

登录后才可以评论

crystonesc 发表于19-04-04
0