唯品会Dragonfly日志系统的Elasticsearch实践

2018-12-04  本文已影响0人  一帅

转载自 https://mp.weixin.qq.com/s/qRCAmEu_PAbEvsHBdbF_HQ

开篇-唯品会日志系统初探

唯品会日志系统,承接了公司上千个应用的日志,提供了日志快速查询、统计、告警等基础服务,是保障公司技术体系正常运行必不可缺的重要系统之一。日均接入应用日志600亿条,压缩后大小约40TB,大促时日志峰值流量达到每分钟3亿条。

唯品会日志系统,取名Dragonfly,寓意像蜻蜓复眼一样,可以依据应用日志既准确又快速的观察到系统的运行细节、并发现系统的任何异动。最初,Dragonfly是围绕开源的ELK(Elasticsearch/Logstash/Kibana)技术栈打造的,后来架构不断演进,增加新的功能组件。目前的系统架构如下图所示。

架构

但无论怎么演进,快速、稳定的日志查询始终是日志系统的核心服务所在,因此Elasticsearch可谓是Dragonfly系统中的核心组件。本文将重点介绍唯品会日志系统有效使用Elasticsearch的各种实践经验。

Elasticsearch简介

朋友圈里这个月最为热议的技术新闻之一,就是Elastic公司2018.10.6在纳斯达克上市,当天开盘后股价就上涨了一倍。将一项技术通过开源不断做大做强,成为无可争议的垂直领域领导者,最终实现上市获得更大发展——Elastic公司实现了无数软件创业公司的梦想,并提供了一个完美的创业成功典范。而Elasticsearch正是这家公司的招牌产品。

Elasticsearch(下面简称ES)是基于Apache Lucene打造的分布式文档存储+文本查询引擎。它通过倒排索引(inverted-index)技术提供极快速的文本查询和聚合统计功能,通过合理的索引和分片设计,又可以支持海量的文本信息。因此非常适合用于搭建日志平台。

尽管如此,将这样一个开源软件,既要能贴合公司内部的实际使用场景,又要做到高吞吐、高容量、高可靠,还是需要做不少细致的工作,下面会一一道来。

Dragonfly系统从2015年开始搭建,最初使用Elasticsearch 1.x,后来升级到5.6版本。

硬件配置

Dragonfly系统在不同机房搭建了多个ES集群(配置了跨集群查询),共上百台机器,最大的2个集群均有接近50台服务器。这些服务器包含了两种硬件配置类型:

两种类型服务器的磁盘均使用RAID0阵列,内存大小为64GB或128GB。

通过冷热分离,可以保证ES集群的写入和实时查询性能,又能在成本较低的情况下提供较长的日志保存时间,下面还会详细介绍ES集群的冷热分离是怎么实现的。

日志索引管理

ES中的文档数据是保存在索引(index)之中的。索引可以划分多个分片(shard)分布在不同的节点上,并通过配置一定的备份数实现高可用。

对于日志数据,通常把每个应用、每天的日志保存到不同的索引中(这是以下很多讨论的前提)。要承接公司上千个应用的海量日志,又要应对每个应用每天不同的数据量,如何有效管理这些日志索引,成为Dragonfly能否提供稳定和快速服务的关键。下面将分多个方面为你介绍。你不会在官方文档中直接找到这些知识,它们来源于我们多年的实战经验。

1. 索引预创建

当有一个写入请求,而请求中指定的索引不存在时,ES会自动创建一个5分片的新索引。因为要完成寻找合适的节点、创建主分片、创建备份分片、广播新的集群信息、更新字段mapping等一系列的操作,创建一个新索引是耗时的。根据集群规模、新索引的分片数量而不同,在我们的集群中创建一个新索引需要几秒到几十秒的时间。如果要在新的一天开始的时刻,大量新一天的日志同时到来时触发创建上千个索引,必然会造成集群的长时间服务中断。因此,每天的索引必须提前创建。

此外,还有一个必须提前创建索引的关键原因,就是ES索引的分片数在创建时就必须给定,不能再动态增加(早期版本还不能减少,ES 5.x开始提供了shrink操作)。在两种情况下会造成问题:

出于以上考虑,必须提前创建索引,并为不同应用的索引指定合适的分片数量。分片的数量可以由以下公式计算:

n_shards=avg_index_size/ size_per_shard * magnified_factor

其中:

索引预创建的操作我们放在前一天的清早(后面会看到,开销很大的操作我们都放在夜间和凌晨)进行。即今天凌晨会根据之前索引大小的EMA及上述公式计算出分片数,创建明天的索引。

2. 替补索引

这是很有意思的一个概念,你不会在任何其他ES相关的文章找到它(如果有,请注意百分百是抄袭:)

它解决的是这样的问题:即使我们已经提前估算出一个应用的日志索引的分片数,但是仍然会有异常情况。有时你会发现某个应用某一天的日志忽然增加了很多,可能是开发打开了DEBUG级别开关查问题,也可能在做压测,或者可能是bug死循环的打印了无数stacktrace。有时是一个新接入的应用,由于没有历史索引,所以并不能估算出需要的分片数。以上情况都有可能引起分片数不足的问题。

记得上面提到过索引在创建后就不能再动态调整分片数了,怎么破?

让我们来新创建一个替补索引(substitude index)吧,这一天接下来的所有日志都会写到这里。替补索引的命名,我们会在原索引名的基础上加上-subst后缀,因此用户在查询今天的日志时,查询接口仍可以通过一定的规则指定同时使用今天的“正选”索引和替补索引。

我们定时扫描当天索引的平均分片大小,一旦发现某个索引的分片大小过大,就及时创建替补索引。注意,创建替补索引时仍需指定分片数,这是根据当天已过去的小时数比上还剩下的小时数,以及当时已经写入的数据量估算得出的。

替补索引还有一种使用场景。有时促销是在晚上进行的,一到促销开始,请求量和日志量会出现一个非常大的尖峰。而由于已经写入了大半天的日志,分片已经比较大,这时任何的写入都可能引起更大规模的segment merge(熟悉ES的同学会知道,每次refresh会生成新的segment,小的segment会不断merge变成更大的segment,越大的segment merge时对磁盘IO和CPU的开销占用越大。这相当于HBase的compaction行为,也同样会有写放大问题),会使写入速率受到限制。而如果这时切换到新的索引写入(场上球员请全部给我下来~),就可以减轻merge的影响,对保障流量尖峰时ES集群的写入速率非常有帮助。

这种促销日的替补索引是提前创建的。分片数的计算、以及还有可能使用到“替补的替补”,就不再赘述了。

3. Force Merge

当数据写入ES时先产生小的segment。segment会占用堆内存,数量过多对查询时间也有影响。因此ES会按一定的策略进行自动合并segment的动作,这前文已经提过。此外,ES还提供force merge的方法(在早期版本称为optimize),通过调用此方法可以进一步强化merge的效果。

具体的,我们在凌晨执行定时任务,对前一天的索引,按照分片大小以及每个segment 500MB的期望值,计算出需要合并到的segment数量,然后执行force merge。

通过这个操作,我们可以节省很多堆内存。具体每个节点segment的内存占用,可以在_nodes//stats api中查看到。当segment memory一项占用heap大小2/3以上,很容易造成各种gc问题。

另外由于force merge操作会大量读写磁盘,要保证只在SSD服务器上执行。

4. 冷热分离

最初搭建ES集群时,为了保证读写速率,我们使用的都是SSD类型的服务器。但由于SSD磁盘成本高,容量小,随着接入应用越来越多,不得不缩短日志保存天数,一度只能提供7天的日志,用户开始产生抱怨。

我们自然而然产生了冷热分离的想法。当时在网上并不能找到ES冷热分离部署的案例,但出于曾经翻阅过官方文档多遍后对ES各种api的熟悉,我们认为是可行的。

image

通过以上冷热分离的部署方法,我们只增加了少量大磁盘容量的服务器(成本比SSD类型的服务器还低很多),就将日志保存时长增加到了30天,部分应用可以按需保存更长时间。

5. 日志归档

从上面可以看出,通过冷热分离,热数据服务器只存放最近3天的日志索引,其余27天的索引都存放在冷数据服务器上。这样的话,冷数据服务器的segment memory将非常大,远超过官方文档建议的不超过32GB的heap配置。如果要增加heap到32GB以上,会对性能有一定影响。

考虑到越久远的日志,查询的可能性越低,我们将7天以前的日志进行了归档——也就是close索引的操作。close的索引是不占用内存的,也就解决了冷数据服务器的heap使用问题。

Dragonfly前端放开放了归档日志管理的页面,用户如需查询7天前的日志,可以自助打开已经归档的日志——也就是重新open已经close掉的索引。

日志写入降级策略

前面已经提到过,我们公司促销高峰时的日志量会达到平日的数十倍。我们不可能配备足够支撑促销高峰期日志量的服务器规模,因为那样的话在全年大部分时间内很多服务器资源是浪费的。而在服务器规模有限的情况下,ES集群的写入能力是有限的,如果不采取任何措施,在促销高峰期、以及万一集群有故障发生时,接入的日志将发生堆积,从而造成大面积的写入延迟,用户将完全无法查询最新的日志。此时如果有业务故障发生但无法通过日志来分析的话,问题会非常致命。

因此就需要有合理的降级措施和预案,以保证当日志流入速率大于日志写入ES集群的速率时,Dragonfly仍能提供最大限度的服务。我们首先需要做出取舍,制订降级服务的目标,最终我们采用的是:保证各个应用都有部分服务器的日志可以实时查询。

唯品会的日志,是从应用服务器先采集到Kafka做缓存的,因此可以在读取Kafka数据写入ES的过程中做一些处理。严格来说如何实现这一目标并不属于Elasticsearch的范畴。

要达到降级服务目标,我们需要设计一套组合拳:

上一篇下一篇

猜你喜欢

热点阅读