【.com原稿】日志数据是海量数据中最常见的一类。以一个拥有大量用户群体的电商平台为例,在双11大促期间,他们每小时的日志量可能达到数百亿条,海量日志数据激增,这给技术带来了严峻的挑战。团队。本文将从海量日志系统如何在优化、部署、监控等方面更好地适应业务需求入手,着重对比各种日志系统的架构设计;后续调优过程:横向扩展和纵向扩展、聚类、数据划分、重写数据链路等实际现象和问题进行扩展。日志系统架构基准有过项目开发经验的朋友都知道:从平台的初期搭建到核心业务的实现,都需要一个日志平台为各项业务保驾护航。如上图所示,对于一个简单的日志应用场景,通常会准备master/slave两个应用。我们只需要运行一个shell脚本,看看是否有错误信息。随着业务复杂度的增加,应用场景也变得复杂。虽然监控系统可以显示某台机器或应用程序的错误。但是在实际的生产环境中,由于隔离的实现,一旦上图下方红框内的某个应用出现BUG,就无法访??问对应的日志,也无法取出日志。另外,一些对日志平台依赖性比较强的应用,可能会在日志产生的时候直接收集,然后删除原有的日志文件。这些场景都给我们日志系统的维护带来了困难。参考Logstash,一般有两个日志业务流程:正常情况下的简单流程是:应用产生日志→根据预先定义的日志文件大小或时间间隔,通过执行Logrotation,不断刷新新文件→定时查看→定时删除。复杂应用场景的流程是:应用生成日志→采集→传输→按需过滤转换→存储→分析查看。我们可以从实时性和错误分析两个维度来区分不同的日志数据场景:实时性,一般适用于我们常说的一级应用,比如:直接面向用户的应用。我们可以自定义各种关键字,以便在出现各种错误或异常时第一时间通知相关业务人员。准实时一般适用于一些项目管理平台。比如工时需要补的时候有停工,但这不影响工资的发放。几分钟后平台重启,我们就可以重新登录填写了。这种情况不影响原理。因此,我们可以将其归类为准实时级别。除了直接收集错误和异常,我们还需要对其进行分析。比如:只知道一个人的体重是没有意义的,但是如果加上性别和身高两个指标,就可以判断这个人的体重是不是标准体重了。也就是说:如果可以给出多个指标,就可以对庞大的数据进行去噪处理,然后通过回归分析让收集到的数据更有意义。此外,我们还要不断还原数字的真实性。尤其是实时的一级应用,我们需要能够快速的让用户理解他们遇到的现象的真正含义。例如:商品上架时,商家将商品标价误标为10元,而不是100元。这可能会导致商品立即售罄。不过这种现象并不是业务问题,很难被发现。因此,我们只能通过日志数据进行逻辑分析,及时反馈,确保库存在几十秒后变为零,才能有效解决这个问题。可见在这种应用场景下,实时分析是非常有用的。最后是可追溯性。我们需要在获取历史信息的同时,实现跨时间的比较和汇总,溯源才能在各种应用中发挥相关作用。上面提到的每个元素都是我们如何管理日志的基线。如上图所示,我们的日志系统采用了开源的ELK模式:ElasticSearch(以下简称ES),负责后端的集中存储和查询工作。单独的Beats负责日志收集。FileBeat提高了Logstash的资源利用率;TopBeat负责收集和监控资源,类似于系统命令top获取CPU性能。由于日志服务只是对业务起到维护稳定和保障的作用,我们需要实现快速、轻量级的数据采集和传输,所以不应该占用过多的服务器资源。在方法上,我们采用插件的方式,包括:输入插件,输出插件,中间负责传输和过滤的插件。这些插件具有不同的规则和自己的格式,支持各种安全传输。日志系统优化思路结合上述日志架构,我们针对各种实际应用场景进一步提出四个方面的优化思路:基础内存优化:如何分配内存、垃圾回收、增加缓存和锁。网络:网络传输序列化,添加压缩、策略、散列、不同的协议和格式。CPU:通过多线程提高利用率和负载。这里的utilization和load是两个不同的概念:utilization:一个core用完后,再使用下一个core,utilization逐渐增加。负载:如果一下子把8个核心都用上,虽然负载满了,但是利用率很低。即每个核都被占用,但是占用的资源不多,计算率比较低。磁盘:尽量合并文件,减少碎片文件的产生,减少寻道次数。同时在系统层面,可以通过修改设置关闭各种无用的服务。平台扩展的加减法,或者备选方案:无论是互联网应用还是日常应用,我们在查询的时候都加入了分布式缓存,有效提升查询效率。此外,我们将直接关闭或移除平台未使用的地方。垂直扩展:如增加扩展磁盘和内存。水平扩展:加法/减法/并行扩展,使用分布式集群。数据分区是根据数据的不同维度对数据进行分类和分类。比如:我们从日志中区分error、info、debug,甚至直接过滤掉info和debug级别的日志。数据热点:比如某类日志数据在白天的某个时间段呈现出暴涨的趋势,但只有在晚上才会稳定产生。我们可以根据热点情况将它们取出来分别处理,以对抗散热。系统降级在有效区分整体业务的基础上,我们制定一些降级方案,停止一些不重要的功能以满足核心业务。日志系统优化实践面对数据量的不断增长,虽然我们增加了很多资源,但是并不能从根本上解决问题。尤其体现在以下三个方面:日志产生量巨大,每天都有数百亿条日志。由于生产环境的隔离,我们无法直接查看数据。由于代理资源的限制,我们的各种日志采集和系统资源采集操作不能超过一个业务资源核心。一级业务架构我们的日志系统层次结构比较清晰,可以简单分为三部分:数据访问、数据存储和数据可视化。具体包括:Rsyslog,这是目前我们接触到的最节省性能的收集工具。Kafka具有持久化作用。当然,当它达到一定程度的数据时,就会出现错误。Fluentd与Rsyslog类似,也是日志传输工具,但更偏向于传输服务。ES和Kibana。该架构将以Golang、Ruby、Java、JS等不同语言实现。在后期的改造中,我们会将符合Key-Value模式的数据快速导入到HBase中。我们根据HBase自身的特点,将其B+树实现在内存层,并持久化到我们的磁盘中,从而达到理想的快速插入速度。这也是我们愿意选择HBase作为日志方案的原因。二级业务架构我们直接看二级业务架构的功能图,由以下流程串联组成:数据采集完成后,为了节省磁盘空间,很多应用会完全依赖我们的日志系统。所以在数据收集之后,我们添加了一个持久化缓存。系统在缓存完成后执行传输。传输过程包括:过滤和转换,这个过程可以进行数据细化。值得强调的是,如果业务方尽早配合,给我们一些约定,我们可以通过格式化实现结构化数据。接下来就是分流,主要包括两部分:一是源A的数据走通道A,源B的数据走通道B。二是让A的数据流入我们的存储设备,并触发保护机制。也就是说,为了保护存储系统,我们增加了一个额外的队列。例如:队列是100,里面的一个chunk是256MB。我们现在将高水位线设置为0.7,将低水位线设置为0.3。在写操作的累加中,由于我们设置0.7,也就是100兆赫。那么当一个256MB累积到70个chunk的时候,我们写入存储平台的速度就跟不上了。此时会触发高水位线,不允许继续写入,直到整个写入过程消化完chunk,数量降到30后才继续写入。我们使用这种保护机制来保护后台和存储设备。然后是存储。由于整个数据流的体量会比较大,所以在存储环节的主要任务是存储索引、压缩和查询。最后是UI的一些分析算法,以及SQL的一些查询语句,用来进行简单快速的查询。通常从集合(logstash/rsyslog/heka/filebeat)到面向缓存的Kafka是典型的宽依赖。所谓宽依赖,就是每个App都可能关联到每个Broker。在Kafka,每次传输都必须在将数据写入每个Broker之前进行哈希处理。窄依赖是指每个Fluentd进程只对应一个Broker进程。最后通过广依赖过程写入ES。Rsyslog等集合不仅占用资源最少,而且可以添加各种规则,还可以支持TSL、SSL等安全协议。Filebeat是轻量级的,在5.x版本中,Elasticsearch具有解析功能(类似于Logstash过滤器)—Ingest。这也意味着数据可以通过Filebeat直接推送到Elasticsearch,让Elasticsearch既做解析又做存储。卡夫卡之后是卡夫卡。Kafka主要实现顺序存储。它通过主题和消息队列机制实现数据的快速存储。而它的缺点:由于所有的数据都写到Kafka,会导致topic过多,造成磁盘竞争,严重拖累Kafka的性能。另外,如果所有的数据都使用一个统一的标签,我们将很难分而治之,因为我们不知道收集到的数据的具体类别。所以在后面优化传输机制方面,我们自己改造实现了顺序存储的过程,从而解决了持久化的安全需求。FluentdFluentd有点类似于Logstash,它的文档和插件都非常完备。它的各种插件保证了与Hadoop或ES的直接接口。就接入而言,我们可以采用FluentdtoFluentd的方式。也就是在原有数据访问层的基础上,再次连接Fluentd。它还支持安全传输。当然,后期我们也着重优化了。ES+Kibana最后我们用到了ES和Kibana。ES的优点是通过Lucene实现了快速的倒排索引。由于大量日志是非结构化日志,我们使用ES的Lucene进行打包,以满足普通用户对非结构化日志的搜索。Kibana提供了基于Lucene的可视化展示工具。问题定位及解决方案下面就我们遇到的问题和现象进行介绍。以下是我们优化的出发点:传输服务器CPU利用率低,各核负载未满。传输服务器Fullgc的频率太高。由于我们是使用Ruby来实现流程,内存中默认设置的数据量有时会过大。存储服务器存在单峰现象,即存储服务器磁盘的性能有时会突然直线上升或直线下降。频繁触发高水位。上面在高水位保护机制中提到,一旦存储盘触发高水位,将不再提供服务,只能手动“清理”磁盘。如果其中一台ES机器“挂了”,集群就会挂掉。即当发现一台机器无法通信时,集群会认为它“挂了”,并迅速开始数据恢复。但是,如果系统很忙,这样的数据恢复操作会更加拖累系统的整体性能。由于所有的数据都写入了Kafka,而我们只使用了一个topic,这就导致每一类数据都要经过一系列与其不一定相关的规则,做出不一定适用的规则判断,所以传输数据整体效率降低。Fluentd的主机轮询机制导致频繁出现高水位标记。由于Fluentd在与ES对接时遵循一个默认的策略:优先选择前五个设备进行数据写入,即与前五个设备的前五个接口进行交互。在我们的生产环境中,Fluentd是用CRuby编写的。每个进程都属于一个Fluentd进程,每个进程对应一个host文件。host文件的前五个默认值是ES写入口,所以所有的机器都会去寻找这五个入口。如果一台机器出现故障,它将轮询下一台。这直接导致了高水位的频繁出现和写入速度的下降。众所周知,日志的查询是一种低频查询,即只有出现问题才会去查看。但是在实际操作中,我们往往通过检索的方式将它们全部检索出来,所以意义不大。另外,ES为了获得更好的性能,会将数据存储在raid0中,存储时间跨度往往超过7天,因此其成本相对较高。通过对数据的实时线路分析,我们发现并没有达到write/writebalance。为了提高Fluentd的利用率,我们在使用Kafka时增加了数据量。以前是5兆,现在改成了6兆。如果只是单纯的传送,不考虑计算的话,其实是可以更上一层楼的。只是我们只提到6兆字节,因为我们认为这里涉及一些计算。我们的Fluentd是基于JRuby的,因为JRuby可以多线程,但是我们的CRuby就没有任何意义了。为了提高内存,我了解了Ruby所有的内存机制,就是一些host文件是hash的,因为我们每个进程只需要选择前五列,我就多开了几个端口。至于ES的优化,在上ES之前,我们已经做了一个优化。因为根据我刚才说的,有的时候日志量很大,有的时候日志量很小。我们会考虑做动态配置。因为ES支持动态配置,动态配置的时候,我们可以在一些场景下提高它的写入速度,在一些场景下支持它的查询效率。我们可以尝试做一些动态配置加载。Retrofit1:StorageReduction整体架构没有太大变化。我们只是减少天数,在转Fluentd的时候改成1天。同时,我们直接将数据拆分,将数据写入到Hadoop中,将一些符合Kibana的数据直接放到ES中。如上所述,日志查询并不频繁。一般来说,你需要查询超过两天的数据的可能性很小。因此,减少存储对我们来说是非常有意义的。改造二:数据分区我们在日志文件节点数较少(机器数小于5)时去掉了Kafka层。由于Fluentd可以支持数据和大文件存储,因此可以将数据持久化到磁盘。我们直接给每个应用对应一个tag,这样每个应用都可以对应自己的tag,按照自己的固定规则,最后写入ES,方便各自定位问题。此外,我们可以通过延迟计算和文件分割来快速找到问题的根源。因此,我们节省了Kafka和ES的各种计算资源。在实际操作中,由于HBase不需要做raid,它完全可以自己控制磁盘的写入,所以我们进行了数据压缩。就其效果而言,大大降低了ES的存储开销。后期我们也尝试了更极端的方案:让用户直接通过客户端的shell查询数据,并使用本地缓存保留机制。优化效果优化效果如下:有效利用服务器资源。实施新方案后,我们节省了大量的服务器,单台服务器的存储资源也节省了15%。单核处理原本每秒可以传输3000条,实施后提升到15000到18000条。而且,当服务器单独空运行时,即不进行任何计算时,单核每秒可以传输近3万条。很少触发ES保护机制。原因是我们已经卸载了数据。以前,历史数据只能保存7天,因为我们节省了服务器,现在可以保存更长的时间。而且,对于一些被别人查询过的日志,我们也会按照原来的策略有选择性的保留,以便追溯。日志系统优化总结关于日志平台优化,我总结了以下几点:由于日志是低频的,我们将历史数据存储在便宜的存储中。当普通用户需要的时候,我们导入到ES中,使用Kibana的前端接口可以快速查询。对于程序员来说,不用上ES,直接查询即可。数据存在的时间越长,它的意义就越小。我们根据实际情况制定了保留有意义数据的有效策略。顺序写入磁盘取代RAM。例如:不同于通常的随机写入磁盘,我们在读写流文件时,采用的是顺序写入数据的方式。当存储容量较大时,应考虑SSD。特别是当ES遇到限流的时候,使用SSD可以提升ES的性能。提前定制规范,有效解决后期分析工作。日志格式如上图所示。常用的日志格式类型包括:uuid、timestamp、host等,尤其是host,由于日志会涉及到上百个节点,有了host类型,我们就可以判断出哪台机器是标准的。图中的其他环境变量类型可以有效的追溯一些历史信息。日志方案如上图所示,我们可以通过Rsyslog直接将采集端的数据写入文件或数据库。当然,对于一些暂时不可用的日志,我们不一定要实现过滤传输的规则。如上图,Fluentd也有一些传输规则,包括:Fluentd可以直接连接Fluentd,也可以直接连接MongoDB、MySQL等。另外,我们还有一些组件可以快速连接插件和系统,比如允许Fluentd和Rsyslog直接连接到ES。这些是我亲自为您定制的一些最基本的基线。我认为日志从采集、缓存、传输、存储、最终可视化分为三组基线。收集到存储是最简单的,像Rsyslog到hdfs或其他文件系统,我们有这种情况。比较常见的情况是从采集、传输,到存储可视化,然后形成我们现在最复杂的系统。大家可以根据实际情况选择。最后,我考虑一个实际情况。如果在这种情况下,我们尽可能少占用服务器,然后传输需要进行过滤和转换,日志可以比较简单,符合这种Keyvalue(KV)格式。我们可以拿一个Rsyslog、一个Fluentd、一个Hbase、一个echars等来做一个计划。我觉得Rsyslog、Fluentd、heka都可以用来收藏。然后就是传输部分的Fluentd传输,因为Fluentd和Kafka对于插件来说非常灵活,可以直接对接我们很多的存储设备,也可以对应很多文件,甚至是ES。Kibana可以用来做可视化,主要是因为它和ES结合的很紧密,他们的结合需要一点学习成本。杨金平,大数据架构师,从业十余年,擅长Web架构和大数据架构。开源爱好者为大数据项目做出了开源贡献,如Hadoop、Hive、Shark等。目前在饭铺金科担任大数据架构师。【原创稿件,合作网站转载请注明原作者和出处为.com】
