如何搭建一个线上日志收集组件

2,163 阅读5分钟

背景

为了应对流量,保证服务的可用性和水平扩展性,现在服务节点多数都是无状态的,集群的方式部署在云上。

当流量被分发在各个机器接点上时,对应的日志同样也写在各个节点的文件里。

因为当集群规模很大的时候,不可能登录到每台机器上去查看日志,所以应该有一个公共的日志组件,来将日志收集到集中的一个地方。

一. 需求分析

首先考虑日志的应用场景,大致有以下几个作用:

  1. 业务方记录,排查问题
  2. 通过日志进行流处理分析,动态调整某些策略(例如用户行为日志)
  3. 根据日志产生离线统计分析报表

通过以上分析可得知,这个组件大致需要包含以下特点:

  • 可以准实时的提供给多个使用方
  • 收集的操作不侵入业务,保证业务代码只是写日志这一个操作
  • 尽可能的不丢日志(允许丢弃少量的情况发生)
  • 至少能够保存7-15天日志记录
  • 在落盘前,对数据进行清洗过滤
  • 能够多维度,准实时进行日志查询分析
  • 能够根据日志,进行离线数据分析

ok,大致需求分析完毕,下面进行具体方案的落地

二. 确定方案

首先想到的结构就是logstash + es + kibana的经典组合

  • es做日志数据的落地存储
  • logstash以日志文件作为输入,es作为输出端
  • kibana界面化操作展示es数据

这套方案已经非常的成熟了,但是方案选型不光要考虑成熟程度,还需要结合公司实际的支持程度进行适配,所以需要对这个方案进行一些调整。

1. 日志入kafka

由于现在公司内服务节点都上了云,对于节点容器公司支持的是flume收集日志,输出源支持「kafka」和「hdfs」文件。

我们的数据需要离线分析,需要落到hdfs,但是需要通过格式校验&清洗之后才能落入。

并且hdfs这只是其中一个数据落地流程,还需要支持多方的日志消费,所以最好的方法是用消息中间件来进行解耦。

通过将日志推入kafka中,各个使用方来订阅topic,达到流程解耦,多流程并行处理的目的。

2. kafka入es

将每条日志以消息形式推入kafka中后,我们需要将消息落入es当中,供kibana的图形界面查询。

2.1 方案选择

消费kafka消息写es的这个过程,可以自由搭建实现,不受限于公司技术支持,可以有以下方式可选

  • filebeat
  • logstash
  • flume
  • 个人开发消费

这里需要注意的是,在落入数据之前,要有一个格式校验&清洗的过程。

2.2 放弃logstash

这里第一版的方案定的流程是

  1. 使用kafka自带shell脚本拉取消息到本地文件A
  2. 通过脚本执行自定义过滤清洗逻辑,写入固定目录下分片文件B
  3. logstash监听固定目录push到es中

这个方案上线一段时间后存在几个问题

  1. 太重

消费消息->清洗数据->写入es这个过程其实是一个很简单的流处理形式,但是由于是跑在jvm上,机器的内存占用和cpu占用都偏高。

  1. 每条消息都需要写一次es

网络io密集,es集群压力大,导致需要控制写入速度,在流量高的时候消费延迟就会很高。

需要做的是将消息打包,批量提交到es集群,减轻es的压力,提高消费速度。

2.3 变更方案

最终选择使用go开发一个消费程序,理由是go语言对io频繁的场景比较适配,并且自定义清洗逻辑和写入的规则都能由自己支配,更加灵活。

消费程序的整体流程借鉴了一个工作流的实现

首先主协程根据主题的分区数来确定需要启动工作流的协程数

然后按照下图所示的流程进行消息的积攒批量发送

image.png

消息延迟量效果
优化前:平均4w左右
优化后:平均1.5k

3. kafka入hdfs

es非常依赖机器的内存资源,存储7-15天的数据还好,但是要存长时间的海量数据就不合适了。

为了能够离线统计分析和产出报表,还需要将日志持久化存储到hdsf中,做一个长久的存储。

公司大数据平台提供了flink集群和hdfs的支持,只需要在中间定义一个消费写入的java代码即可,过程很简单就不再赘述。

4. 最终方案

  1. 业务侧将消息以json形式写入日志
  2. flume监听日志文件,增量推入kafka
  3. go消费程序接收消息->清洗过滤->批量写入es
  4. java消费程序接收消息->清洗过滤->写入hdfs

三. 上线效果

日均日志条数:3.5亿
es写入次数:90w+
实时性:秒级
延迟情况:流量峰值最高7k

image.png