数人云工程师手记 | 容器日志管理实践

业务平台每天产生大量日志数据,为了实现数据分析,需要将生产服务器上的所有日志收集后进行大数据分析处理,Docker提供了日志驱动,然而并不能满足不同场景需求,本次将结合实例分享日志采集、存储以及告警等方面的实践经验。

2013年以来Docker迅速火了起来,它的理念带来了非常大的便利性,不过实际应用中会发现还有监控、日志、网络等问题尚待解决,本文会结合实例分享数人云做容器日志系统的经验。

基于ELK的日志管理系统架构

日志收集是大数据的基础,业务平台每天产生大量日志数据,为了实现数据分析,需要将生产服务器上的所有日志收集后进行分析处理;高可用性,高可靠性以及可扩展性是日志收集系统的必备要素。

ELK是目前较流行的日志一体化解决方案,提供日志收集、处理、存储、搜索、展示等功能。容器标准输出日志常用的查询方式是通过Docker命令 docker logs containerid来查看,容器内日志受容器隔离性影响不便于收集,因此当面对大型系统,用单一命令管理日志是不可行的,需要一个对于容器日志统一检索管理的方案。基于ELK实践了一套容器日志管理系统,架构如下:

日志采集

传统的日志采集有较成熟的解决方案,如Flume、Logstash等,但传统的采集方案不适用于容器日志。Docker本身提供了LogDriver功能,可以利用不同的driver把日志输出到不同地方,LogDriver具体有以下几种:

  • None(将日志设置成不再输出)
  • json-file(Docker默认的LogDriver,将日志以JSON文件的方式存储在本地)
  • Syslog(标准输出日志可通过该方式传输)
  • Journal
  • SELF
  • Fluent
  • awslogs
  • Splunk
  • etwLogs
  • gcplogs

对于这些LogDriver就不一一详细介绍了,大家有兴趣可以去Docker官网查看。可见Docker对日志提供了较为丰富的处理方式,供选择的还有优秀的开源项目Logspout等,然而这并不能满足所有的使用场景。

容器的标准输出日志可从以上驱动中选择,由于大多数用户选择标准化输出日志,故Docker没有提供采集功能,如果将日志内的文件挂载出来进行采集,多个实例同名日志则会无法区分,容器内文件日志处理、错误日志多行处理等问题时有发生,若想标准输出日志和容器内文件日志兼得,则需自己动手丰衣足食,以下为数人云日志采集系统实践。

1. 标准输出日志

针对Marathon + Mesos环境开发了一套日志采集工具,Docker的标准输出日志json-file默认持久化在本地上,Mesos对于标准输出日志也存了一份在Sandbox下: 因此标准输出日志也可以通过Mesos文件的方式进行采集。

2. 容器内文件日志

平台支持的文件存储是Overlay,避免了许多复杂环境的处理。关于Overlay盗用一张图: 容器的存储驱动运用写时复制(Copy On Write),Overlay主要分为lower和upper, 当需要修改一个文件时,使用CoW将文件从只读的Lower层复制到可写层Upper层进行修改,在Docker中,底部的只读层是image,可写层是Container,因此容器内日志在宿主机上通过Upper的文件系统可找到,例如在容器内的/var/log/test.log中写一个test字符,如图: 同理,无论是标注输出的日志还是容器内文件日志,都可以通过文件的方式进行处理,也可以同时把json-file关闭,以减轻Docker本身的压力。

3. 自研日志采集工具

基于上述方式开发了一款日志采集工具,对日志进行统一收集管理,日志通过TCP把JSON格式化的日志输出到Logstash,包括应用ID,容器Name,容器ID,TaskID等,当然开发的过程中也遇到许多问题,如断点续传和错误日志多行处理等功能,这其中参考了Filebeat(Go语言开发)对于日志处理的方式,个人认为如果是对于传统文件日志处理,Filebeat是不错的选择,日志采集功能第一步支持:

  • 容器标准输出日志采集
  • 容器内文件日志采集,支持同时采集多个文件
  • 断点续传 (如果Agent崩溃,从上次offset采集)
  • 多行日志合并 (如:多行错误日志合并)
  • 日志文件异常处理 (如:日志被rotate可以重新采集)
  • TCP传输
  • --add-env --add-label标签,可以通过指定命令把container的env或者label加到日志数据里,如(--add-env hostname=HOST --add-env test=ENVNAME1 --add-label tlabel=labelname)
  • Prometheus指标数据

日志处理需要提供快速的数据处理能力,在开发过程中遇到了性能问题,CPU占用非常高,针对该问题对程序作调优,使用Golang内置的包net/http/pprof,对Golang程序调优很好用,可将程序中每个函数占用CPU内存的比例通过生成SVG的方式直观的反映出来,如图: Golang内置包encoding/json json的序列化、正则、反射、字节转字符串对于资源的消耗也比较高,可针对以上几方面以及程序本身进行调整。

日志存储后端架构

日志存储功能有Logstash、Heka、Fluentd等方案,Logstash基于R uby,支持功能丰富,但性能方面诟病较多;Heka基于Go,性能方面比Logstash好很多,不过Heka好像已经不维护了。综合考虑社区活跃度、迭代速度以及稳定性方面最终选择了Logstash,实际应用过程中比较重要的参数如下:

  • --pipeline-workers (命令行参数)
  • --pipeline-batch-size (命令行参数)
  • LSHEAPSIZE=${LSHEAPSIZE} (根据自己的实际情况填写,可以写到环境变量活着命令行参数里面)
  • workers => 8(根据自己实际情况,一般等于CPU数,配置文件参数)
  • flush_size => 3000(根据自己的实际情况测试)

以上参数仅供参考,可根据实际环境进行调试。如果日志量较大,为了确保架构的稳定性,可以在中间加一层消息队列,比较常用的有Kafka、Redis等,相信大家对这方面应用比较多,不再赘述。

ES应该是索引存储的不二选择,整个架构的缓解包括ES通过Docker的方式部署,压测时用Marvel对ES的索引方式监控等,网上有很多调优资料,可自行实验。日志的展示是通过自己定制的,Kibana本身的功能比较强大的同时也略微有些学习成本,最终客户想要的是很简单的东西。

压测工具选择的是分布式压测工具Tsung,通过压测一个应用产生日志然后通过Log-Agent对日志进行采集,模拟真实环境日志采集。

日志告警

日志处理中,关键字报警是一个重要功能,对于监控报警主要用Prometheus + Alertmanager实现。应用运行过程中,根据日志关键字告警部的应用场景,从Logstash部分对日志做分流(具体方案可以看上面图的报警部分),自研grok_export对日志进行过滤分析生成Prometheus格式的数据,然后从Prometheus配置报警策略通过alertmanager报警。Log-Agent本身也支持Prometheus数据,Prometheus通过特定的规则查看日志的统计信息。

  • Prometheus: Prometheus是开源的监控告警系统,通过pull的方式采集时间序列,以及用http传输,数据存储在本地,支持丰富的查询语法和简单的Dashboard展示。

  • Alertmanager: Alertmanager作为Prometheus的组件,所有达到阀值的时间都通过Alertmanager报警,Alertmanager支持非常强大的告警功能,包括http、email通知,以及静默重复报警屏蔽等功能。

以上是数人云在实践容器日志系统过程中遇到的问题,更高层次的应用包括容器日志分析等,还有待继续挖掘和填坑,欢迎大家提出建议,一起交流。

Q&A

Q:Overlay 是没有实现 inotify 接口的,是咋获取文件日志增量数据的?

A:通过循环读取文件的方式,记录文件offset。

Q:既然主要框架是 ELK,采集端不直接用 Filebeat 是因为 Filebeat 有局限性吗?

A:Filebeat没有满足我们产品基于Docker的需求,等于上面加了Docker的逻辑。

Q:自研的日志系统,打出来的每条日志格式都是规定好的吗?开发中每个人都要按这个规范来做吗?不管是什么级别的日志?

A:其实并没有,但是如果是内部使用,能规约好当然更好,可以更方便的处理,而且可以做更细粒度的分析。

Q:日志收集有做分析展示处理吗?用什么处理的。

A:对于日志内容的分析还没做,例如Nginx请求日志还是有分析意义的。

Q:采集方面有考虑直接使用系统的Syslog和Logrotate吗?

A:用过Syslog后来因为容器内的文件日志需求重新开发的。