基于容器的日志管理

2013年以来docker迅速火了起来,它的理念带来了非常大的便利性,不过在实际应用过程中会发现还有各种各样的问题需要我们自己解决完善,例如:日志、监控、网络等等,本文会结合实例分享容器日志的管理经验。
1.日志系统简介 日志收集是大数据的基础,为了许多公司的业务平台每天都会产生大量的日志数据,为了实现数据分析,需要将生产服务器上的所有日志收集后进行大数据处理分析,高可用性,高可靠性和可扩展性是日志收集系统所必备的要素。ELK是现在比较流行的日志一体化解决方案,提供了日志收集、处理、存储、搜索、展示等功能。但是因为容器的隔离性,收集容器内的日志很不方便,对于容器标准输出日志的话我们最常用的查询方式是通过docker命令 docker logs containerid来查看docker的日志。但是当面对一个大型的系统面对成百上千甚至更多的时候,我们是不可能通过单一的命令对日志进行管理的,所以我们需要一个对于容器日志统一检索管理的方案。基于ELK我们实现了一套自己的容器日志管理方案,如图:
基于容器的日志管理
文章图片
Paste_Image.png 2.日志采集 传统的日志采集有一些比较成熟解决方案,例如:flume、logstash等等,但是对于容器的日志这样采集是非常不方便的。docker本身提供了log driver的功能,可以利用不同的driver把日志输出到不同的地方,logdriver具体有以下几种:

  • none
  • json-file
  • syslog
  • journals
  • self
  • fluent
  • awslogs
  • splunk
  • etwlogs
  • gcplogs
【基于容器的日志管理】docker默认的logdriver是json-file,是把日志以json文件的方式存储在本地,none是把日志设置成不在输出,syslog可以把标准输出的日志通过syslog的方式传输出去,对于这些logdriver就不一一详细介绍了,大家有兴趣可以去docker官网查看。可见docker对于日志的处理还是提供了比较丰富的处理方式。另外还有一些比较优秀的开源项目例如logspout等也为我们提供了更多的选择。然而这并不是我们想要的最终结果,如果只是对于容器的标准输出日志我们可以从其中选择一种认为对自己合适的方式,通过收集需求其实对于传统用户它们比较接受的方式还是文件输出日志,所以对于这种容器内的文件日志docker并没有提供采集的能力,如果通过把日志内的文件挂载出来进行采集,对于多个实例同名日志将会混在一起无法区分,所以我们需要一种无感知的方式能够同时收集容器内的文件日志,这其中还包括一些多行错误日志处理等功能。所以现在是自己动手丰衣足食的时候。
(1).标准日志输出 数人云的调度环境是marathon+mesos。针对数人云的环境我们开发了自己的日志采集工具。docker的标准输出日志json-file默认持久化在本地上,除了这一份以外mesos同样对于标准输出日志也存了一份在sandbox下面:
基于容器的日志管理
文章图片
Paste_Image.png
所以对于标准输出日志也可以通过mesos文件的方式进行采集。
(2).容器内文件日志 数人云支持的文件存储是overlay,避免了许多复杂环境的处理。关于overlay这里盗用一张图:
基于容器的日志管理
文章图片
Paste_Image.png
容器的存储驱动运用的是写时复制(Copy On Write),overlay主要分为lower和upper, 当需要修改一个文件时,使用CoW将文件从只读的lower层复制到可写层upper层进行修改,在docker中,底部的只读层是image,可写层是container,因此容器内的文件日志在宿主机上通过upper层的文件存储系统是可以找到的,例如我在容器内的/var/log/test.log中写了一个test字符,如图:
基于容器的日志管理
文章图片
Paste_Image.png
所以无论是标准输出日志还是容器内的文件日志,都可以通过文件的方式进行采集处理,也可以同时把logdriver关闭可以减轻docker本身的压力。
3.数人云日志采集 基于上述方式开发了一个日志采集工具对日志进行统一收集管理,日志最后通过tcp把json格式化的日志输出到logstash。其中包括应用id,容器name,容器id,taskid等等,当然开发的过程中也遇到许多其他的问题,例如:日志的断点续传和多行错误日志进行合并等等,这其中参考了elastic的filebeat对于日志的处理方式,filebeat是采用go开发的,个人认为如果对于传统日志文件进行处理filebeat是一个不错的选择,对这个工具进行了简单的测试对于稳定可靠性方面感觉做的不错。数人云日志采集器功能第一步支持:
  • 容器标准输出日志采集
  • 容器内文件日志采集,支持同时采集多个文件
  • 断点续传 (如果agent崩溃,从上次offset采集)
  • 多行日志合并 (如:多行错误日志合并)
  • 日志文件异常处理 (如:日志被rotate可以重新采集)
  • tcp传输
  • --add-env --add-label标签,可以通过指定命令把container的env或者label加到日志数据里,如(--add-env hostname=HOST --add-env test=ENV_NAME1 --* add-label tlabel=label_name)
  • prometheus指标数据
日志处理需要提供快速的数据处理能力,在开发的过程中发现程序遇到了程序性能问题,发现cpu占用非常高,对程序作了一个调优的工作,使用的是golang内置的包net/http/pprof,对于golang程序调优非常好用,可以把程序中每个函数占用cpu内存的比例通过生成svg的方式非常直观的反映出来,这里再次盗用一站图(与日志采集程序无关):
基于容器的日志管理
文章图片
Paste_Image.png
最后发现golang内置包encoding/json json的序列化、正则、反射、字节转字符串对于资源的消耗比较高,针对这几个方面以及程序本身进行了调整。
4.后端架构介绍 对于日志汇聚功能有很多方案logstash、heka、fluentd等,logstash是基于ruby的,支持功能非常丰富,但是性能方面应该是大家诟病最多的地方。fluentd也是基于ruby的,没有做过对比。heka基于go,性能方面比logstash好很多,不过heka好像已经不维护了。综合考虑到社区活跃度、迭代速度以及稳定性方面最终选择了logstash,实际应用过程中有几个比较重要的参数:
  • --pipeline-workers (命令行参数)
  • --pipeline-batch-size (命令行参数)
  • LS_HEAP_SIZE=${LS_HEAP_SIZE} (根据自己的实际情况填写,可以写到环境变量活着命令行参数里面)
  • workers => 8 (根据自己实际情况,一般等于cpu数,配置文件参数)
  • flush_size => 3000 (根据自己的实际情况测试)
针对以上参数可以根据自己的实际环境进行调试。
如果日志量较大,考虑到架构的稳定性可以在中间加一层消息队列,比较常用的应该是kafka、redis等,相信大家对这方面的应用比较多,这里不在赘述。
es应该是索引存储的不二选择,整个架构的缓解包括es通过docker的方式部署,压测时用marvel对es的索引方式监控,对于es网上也有很多调优的文章,可自行实验。日志的展示是通过自己定制的,kibana本身的功能比较强大的同时也略微有些学习成本,最终客户想要的是很简单的东西。
压测工具选择的是分布式压测工具tsung,通过压测一个应用产生日志然后通过log-agent对日志进行采集,模拟真实环境日志采集。
5.日志告警 通过需求收集,对于日志处理有根据关键字报警的功能,对于监控报警这块我们主要是采用的prometheus+alertmanager实现的。应用运行过程中,根据日志关键字告警部的应用场景,从logstash部分对日志做分流,具体方案可以看上面图的报警部分,自研grok_export对于日志进行过滤分析生成prometheus格式的数据,然后从prometheus配置报警策略通过alertmanager报警。log-agent本身也支持prometheus数据,prometheus通过特定的规则查看日志的统计信息。
  • prometheus:
    prometheus是开源的监控告警系统,通过pull的方式采集时间序列,以及用http传输,数据存储在本地,支持丰富的查询语法和简单的dashboard展示。
  • alertmanager:
    alertmanager作为prometheus的组件,所有达到阀值的时间都通过alertmanager报警,alertmanager支持非常强大的告警功能,包括http,email通知,以及静默重复报警屏蔽等功能。

    推荐阅读