降本30%、提效200%!解密星巴克日志平台的极致升级优化之道

来源: InfoQ - 架构

原文

星巴克中国技术部日志平台团队经过近1年的日志集群迁移和升级,将维护支持星巴克中国的日志平台多个日志集群,数PB的数据,从ES版本从 7.8.X、7.9.X跨大版本无缝升级至8.X,从虚拟机架构部署模式迁移至云原生祼金属k8s平台部署模式。完成日志平台组件升级的过程中,也完成了日志平台的架构升级。从开始计划到执行完成,中间也遇到了很多的阻力和问题,经过团队技术攻坚,跨团队配合,新资源支持等方式解决了相关困难,完成之后我们回顾一路走来的付出和成就,确实有很多值得思考的地方,分享出来供大家参考。

2024年9月开始计划,在不改变用户查询和提升用户体验的前提下,到2025年6月完成所有日志平台组件架构升级和版本迁移,在这中间的过程中,经历了mapping不兼容,字段类型冲突,查询上下文失效,重复消费误告警等诸多业内普遍存在的难题,最终实现了单机查询性能提升80%,整体cpu下降30%,写入tps提升200%,用户也明显感觉到了查询性能提升带来的快感,本次分享从以下几个方面进行展开。

一、背景

在数字化业务高速发展的当下,日志作为系统运行状态的 “全景镜像”,承载着业务监控、故障排查、安全审计、数据分析等核心场景需求,星巴克日志平台从2017年开始建设以来,存在一些架构设计不合理,日志查询缓慢,超时,丢日志等情况,用户体验不好。传统架构下的日志平台逐渐暴露出性能瓶颈:查询延迟超 10 秒、存储成本年增长率达 30%、跨集群数据协同效率低,难以满足业务对 “实时性”“低成本”“高可用”的诉求。为此,技术团队启动全方位升级优化,通过架构重构、引擎升级、智能调度等手段,实现平台从 “能用”到 “好用”的跨越,为万亿级数据场景下的日志服务树立新标杆。

自2024年以来,日志平台团队开始逐步接手相关的平台建设工作,从日志采集组件(filebeat),日志接收(logstash producer), kafka ,日志消费组件(logstash consumer), ES集群,数据存储模式,日志查询组件(kibana),ES多集群查询组件(CCS)等层面进行了逐个优化,下图为日志集群没有优化之前的架构模式图:

此架构存在以下问题:

ES集群依赖远程网络存储,写入能力受到远程存储io资源限制,当时的数据量场景下,存储io带宽已经处于满载运行的状态,且扩容麻烦周期较长;   

logstash-producer和logstash-consumer单节点处理能力有限,两者占用了大量的计算资源,需进行优化或者技术演进,以提高效率和节约计算资源   

远程存储容量即将达到安全线内使用上限,如果不进行优化,无法承接更多的业务日志接入,用户体验也经常收到业务反馈查询超时,日志延迟等投诉建议   

各组件都是虚拟机部署,运维成本高,集群的可观测性,稳定性得不到保证。

在稳定性方面,因历史原因日志数据的数据清洗和存储方面没有最佳实践,没有合理的索引的模版结构和生命周期管理,索引分片和大小配置也没经过合理的优化,这样造成了数据查询超时,甚至部分节点内存溢出,资源使用倾斜情况等,用户体验非常不好。其次,存储使用的是远程网络存储,受远程存储性能的限制,造成ES集群写入和查询的资源受限,经常出现数据堆积,ES查询队列居高不下,查询缓慢的情况。

基于以上原因,日志平台技术伙伴从技术层面和成本管理层面来考虑,经过团队对现有硬件资源情况,日志每天增量情况,早晚高峰期流量情况等方面的评估,在同等硬件资源的情况下,进行日志平台技术架构升级,能够让日志的查询效率,日志高峰流量写入得到显著提升,可以缓解高峰时日志写入延迟,提高ES的写入能力方面等到优化,因此日志平台伙伴对日志平台进行了升级改造。

二、计划目标及面临的挑战

目标

日志平台伙伴根据实际的技术调研,跨团队配合情况,设定了详细的日志平台升级计划,保障每个关键节点实现相关里程碑成就,主要包括以下几点:

所有日志涉及的环境组件统一都迁移至云原生祼金属k8s平台引擎之上,即:日志平台涉及到的所有组件都需要容器化部署。

统一日志平台各组件的运行版本和交付标准,实现日志数据采集、日志规则解析,数据入库等流程化体系建设。

资源分配合理化使用,对不同索引根据流量大小,高峰流量情况,业务活动情况,从采集,接入,解析,存储进行资源的精细化控制。

提升日志平台的接入能力,保证业务高峰期及活动保障期间不会有数据堆积和丢失情况。

提升用户数据查询服务体验,目标是p99查询 < 5s,改善用户对日志平台一些不好的看法。

日志平台改造架构图如下:

与此前架构设计相比,本架构所具有的优势为:

所有组件都是容器化部署,运维相对简单,由之前在虚拟机上部署组件变为operator快速制备pod,组件交付效率提升90%以上;   

producer和consumer的组件由之前的logstash替换为vector,同等硬件资源下数据处理能力提升2倍以上;   

ES集群采用冷热角色部署,热节点使用本地磁盘,提高查询和存储速率,冷盘使用远程网络存储,可以最大程度使用已有资源,节约存储采购成本;    

计算资源可以集中化管理和调度,提升资源利用率的同步,也使用资源可以快速的支持需要重保的组件。

挑战

1. 如何保障用户体验?

在实际的集群升级过程中是不允许有数据丢失情况的,包括历史数据及新接入数据,用户查询界面也不能更换,即要在一个界面上进行新旧集群的日志查询,不能出现新集群数据在新界面查询,旧集群数据在旧界面查询的情况,另外,当时日志查询经常超时,用户经常反馈日志平台查询不到数据,查询缓慢,平台难用的情况,如何优化用户体验成为了日志平台伙伴面临的一个非常棘手的挑战。

如何解决数据积压问题?

远程网络存储io带宽已经达到写入瓶颈,io带宽资源支持最高4Gb/s,但是高峰期已经超过这个流量,直接制约了ES集群的写入能力,这也是造成日志经常堆积,日志数据延迟的主要原因。

如何提升单个消费节点的写入能力?

消费组件logstash-consumer的规则解析比较多,单个节点的平均写入大约1w/s左右,而且单个节点占用资源为32c 64g的配置,涉及到大量的规则解析,所以性能受到严重影响,流量高峰期间需要启动大量消费节点进行数据写入,是日志集群除ES节点外最大的资源消耗组件,如何提升单个节点的写入能力,降低资源消耗成为技术伙伴攻关的主要挑战。

如何提升集群的存储能力?

集群硬件存储资源总共数PB左右,但是当时日志增量为每天百TB左右,日志保留时长一般为30天,有些业务日志由于业务原因需要保留60天或者90天,所以存储资源当时已经处于饱和的状态,总共数据量达到万亿级别,所以如何在不增加存储容量的情况下进行存储资源的优化,也是技术伙伴面临的重大挑战。

如何解决业务日志接入流程长的问题?

之前业务日志接入是按照业务组件名称,要从采集侧开始配置,再手动配置生产者,kafka的topic,消费者及写入的索引名称,分片数量,查询别名及索引生命周期管理等,这一套流程手动操作下来要2小时左右,这也是在伙伴对流程比较熟练的情况下,但是我们接收到的新的日志接入每天大概3-4个,消耗在日志接入方面的人力成本非常多,如何优化日志接入流程成为了平台伙伴需要面临的挑战。

以上为主要面临的挑战,还有一些比如:业务组件如何对应索引名称和管理?人力资源有限的情况下,如何进行多集群的高效运维和管理?面对组件容器化之后,针对ES节点,kafka节点等有状态节点出现故障之后如何快速恢复?集群各组件的可观测能力,告警能力,故障自动恢复能力等等,都是在集群升级过程中要面临的问题和挑战。

三、执行步骤

在开始实施前,我们按照以上问题的优先级进行了执行步骤的制定,对每个步骤的实施方案,期望达到的效果及预计的完成时间进行了详细的技术讨论,拆分如下:

迁移过程中如何保障用户体验实施方案: 

a、通过ES集群中的ccs(Cross-Cluster-Search)的角色,可以实现kibana跨ES集群日志查询,由于资源的限制,新的中转集群开始只有9台物理节点,需要通过腾挪的方式,把旧集群的物理机逐步释放,再添加到新集群的方式完成硬件资源的迁移,由于初始的集群资源较少,所以迁移只能按照索引级别逐个迁移,涉及200多个索引,所以迁移周期比较长, ccs支持多ES集群查询流程如下图:

b、但是新集群仍然面临写入远程带宽受限的问题,所以团队经过技术评估,决定新的ES集群使用本地盘+远程存储的方式,即在新集群中hot节点数据使用本地盘,数据保留时间为7天,cold节点使用远程存储的方式,数据为7天之后hot节点迁移过来的数量,继续保存23天,以解决远程存储带宽受限及ES写入性能不足的问题,新集群的所有组件均在k8s平台基于容器化部署的方式交付,本地盘通过local storage的方式挂载给到ES的pod使用。       

预期效果:   

a、用户在原有kibana平台可以通过索引别名的方式查询新旧集群的数据,新集群的hot节点数据查询明显变快,因为新集群的hot节点使用的是本地nvme存储,写入和查询的速率较快。   

b、新集群的ES单个节点的写入性能明显提升,7天内的数据查询p99 < 5 s,新集群解决了数据写入和用户查询体验的问题。

使用本地磁盘代替远程网络存储的收益如下:

在单节点的性能优化中,我们主要优化了以下几个方面:

· 磁盘调度器根据硬盘硬件能力使用noop调度器或者mq- deadline调度器;

· 磁盘队列深度扩大数倍,以很好利用超大内存JVM;

· 磁盘脏页比例和回写比例优化,减少频繁IO次数,提升吞吐量;

下图是单个物理机在20w/eps时的iostat性能图

从上图可以看出,使用本地磁盘存储代替远程存储,磁盘io性能得到了指数级提升。

如何解决数据积压问题实施方案: 

经过团队内部仔细梳理,发现在业务高峰期导致日志积压的因素有多种,具体归纳如下:

个别组件日志量瞬间太大,最高单个topic可达15w/s,导致其它的索引写入和查询变慢       

生产者发送kafka单条业务日志太大,占用较多的带宽资源       

kafka对应topic的partition的数量设置不合理,没有根据实际的数据量评估单个partition可以承载的流量       

logstash-consumer消费线程数量设置不合理,造成有些线程空轮询,有些线程繁忙的情况,表现总体消费能力持续的忽高忽低,消费能力很不稳定       

索引的shards的数量设置不合理,建议单个shards的存储容量最高在20-40GB之间,但实际的情况是单个shards的数量达到TB级别,并且部分索引没有滚动策略,造成shards存储越来越大,直接影响了数据的查询和故障时索引的恢复时间       

ES写入线程数据及资源分配也不合理,对于部分集群冷热节点的资源没有合理规划,造成热节点在写入高峰期cpu资源不足,经常出现cpu打满,ES写入队列居高不下的情况,影响ES的写入性能。

针对以上发现的问题,团队在进行集群迁移升级的过程,对集群的吞吐能力从不同组件的纬度进行了优化,具体实施步骤如下:

a、对采集侧发送到logstash-procuder的大流量业务日志数据进行采样策略,即按照百分比丢弃,这个可以logstash-producer逻辑里面实现,和业务沟通高峰流量日志会有单条日志被放大7-10倍的情况,即一条数据在同一个组件内部或者跨组件过程中,由于不同的逻辑处理被多次打印的情况,所以部分日志进行采样收集不影响业务排查问题。   

b、对于单条日志过大的情况(单条业务日志大于10MB),在logstash-producer中进行过滤拦截,并通知业务整改,这种单条数据量过大的日志收集没有实际的意义,因为kibana也查询不出来,浪费集群的计算和存储资源。   

c、对于kafka中相关topic的partiton数量设置不合理,logstash-consumer的线程数设置不合理,ES的写入线程设置不合理,索引的shards数量设置不合理的情况,在实际的操作过程中通过压测找到合适的参数匹配关系,合理的利用集群的计算资源,比如针对大流量业务组件,如果topic的partition数量设置为80,如果单个consumer的worker线程数量设置为10,那么最多只需要启动8个消费者节点就可以,因为单个partiton同一时间只能有一个消费者线程消费,诸如此类的细节的优化措施在实施过程中对集群的吞吐能力提升很大,相关组件的参数调整及说明如下:

kafka集群设置:

    topic分区设置规则:针对业务应用日志量大的topic分区:设置45个(9个kakfa节点,保证是9的倍数),日志topic默认分区设置:9个

    kafka参数主要参考批次大小,参数过大,导致cpu,内存异常告警,设置过小导致filebeat事件活跃数高,日志写入延迟。

fetch.max.bytes: 30000000 

max.request.size: 31457280  

message.max.bytes: 41943040

vector写入ES参数设置:

主要针对日志量大的业务,减少日志写入提交频次,降低ES写入队列:  

batch:

  max_events: 2000

  max_bytes: 20971520  

timeout_secs: 5

compression: "zstd"

ES索引模版参数优化设置:

"refresh_interval": "73s",

"mode": "logsdb",

"codec": "best_compression",

"routing": { "total_shards_per_node": "1" }

d、对于索引shards中存储数据比较大的情况,给索引设置合理的shards数量,shards数量过多会占用更多的ES内存资源,过少会导致存储过大和写入性能问题,所以在实施过程中对每个索引进行了shards数量的优化,段合并优化,合理规划shards数量,制定索引的生产周期管理策略,在shards存储到达一定的水位之后自动滚动生成新索引,保证数据写入速率的同时,控制索引的存储容量。       

预期效果:   

a、通过限流的手段保障即使在流量高峰,也不会因为单个业务的日志量大拖垮集群的情况,从而缓解了流量高峰期日志查询缓慢,用户体验差的情况,通过对单条日志过大的治理,释放了集群的带宽和计算资源,为集群可以承载更多的流量提供了资源条件   

b、通过对kafka, logstash-consumer,es等相关参数的优化,提升了ES的集群的数据吞吐能力,降低了日志在流量高峰期间出现频繁积压和延迟的情况,提升了集群的稳定性   

c、通过对索引shards数量和存储容量的优化,提升了ES集群的写入能力,缩短了集群出现故障或者节点维护需要重启ES节点时的索引分片恢复的时间,进而保障了集群的可用性能力

如何提升单个消费节点的写入能力实施方案: 

在ELK的架构体系中,logstash组件扮演着数据管道的角色,把数据从filebeat写入kafka,再把数据从kafka经过解析或者清洗写入ES,这种组件集群的方式在业界使用非常广泛,但是logstash是java实现,在进行大数据量处理时经常需要占用更多的cpu和内存资源,日志平台总资源只有数十台物理机,平台提供给logstash的计算资源达到10台,一半以上的计算资源被锁在logstash-producer及logstash-consumer上,其实也没有存在资源浪费的情况,只是数据处理的性能低下,1个32c64g的生产者可以每秒发送3-4w条数据到kafka,而消费者的处理和写入能力也只有1w/s,因为要处理大量的日志数据字段解析,所以性能比不上生产者结合以上情况,团队伙伴决定做技术演进,在技术调研的过程中,新兴组件vector进入了我们的视野,据官方压测数据,其性能是logstash的数倍以上,所以我们决定引入vector取代logstash组件作为集群中的管道组件,具体实施步骤如下:

a、把logstash-consumer先替换为vector,此过程要解决的问题就是解析规则的迁移和兼容性测试,消费者需要处理的解析规则大概有3000多条,都是根据实际的业务需求作的日志数据字段解析,方便业务伙伴检索从而快速排查和定位生产问题,由于规则解析的多样性,迁移过程中还要保障解析规则的正确性,团队伙伴在规则验证方面投入了很多精力,也沉淀了一些规则迁移和验证的工具,保证了规则迁移的完整性和数据正确性。   

b、根据业务重要性及日志数据的大小对对所有消费的topic进行了分组消费,示意图如下:   

从上图可以看出,在每一组内如果消费堆积,不会影响其它组的消费,只需要解决堆积的那个组的问题就可以,也可以对重点保障的业务所在的组投入更多的消费资源,做到了消费资源的精细化控制,也可以在活动期间对活动涉及到的业务组进行重点保障工作。    c、logstash-producer替换为vector,因为生产者不涉及规则解析,只承担了数据转发和限流的功能,所以此部分的替换工作相对简单,但是vector本身的一些参数设置会影响其投递到kafka的速率,实际的替换过程中团队伙伴也是沉淀一些参数配置的最佳实践。

预期效果:   

a、通过vector组件的引入及日志数据解析规则的迁移,在同等cpu和内存资源配置下,单个生产者的写入速率最高可达10w/s,比logstash-producer提升3-4倍,资源节约近50%,同时生产者的限流能力也得到了平滑的迁移,单个消费者消费和写入ES能力提升到3-5w/s,比logstash-consumer提升2-3倍,资源节约60%左右,为集群支持日志数量增长提供了资源支持   

b、分组消费的优化也把堆积影响范围进一步缩小,使问题影响面变得更加可控和可快速定位及处理,方便了活动日的集群稳定性保障,由于每个组内的消费者节点数量都可以单独控制,极大方便了计算资源的动态调度,保障了集群的稳定。

如何提升集群的存储能力实施方案   

日志集群的总存储资源在团队接手管理时存储容量已经使用85%以上,而且每天不断有新增的业务组件需要接入日志集群,在存储硬件资源受限的情况,在业务日志保存时长不受影响的情况下,如何对现有的存储逻辑进行优化,释放更多的存储空间,进而承接更多的业务日志接入也成为了团队需要亟待解决的问题,经过团队伙伴的共同努力,发现可以进行存储优化的点有两处:     

 一、kafka存储优化,kafka的存储数据没有进行压缩设置,单个集群内有9个kafka节点,同时 kafka对数据有副本机制保障高可用,所以在保证磁盘使用率控制在80%以下的情况下,kafka可以保存的时长只有4个小时,这个时间要求团队伙伴如果在集群出现问题时,处理和恢复时间不能超过4个小时,否则数据丢失风险很大。       

二、ES集群对索引存储没有开启压缩算法,据了解,这里主要原因是开启压缩会占用更多的cpu资源,集群本来资源就非常有限,处理数据写入已经占用了大量的资源,经常会有写入堆积,还开启压缩功能就会适得其反,影响集群吞吐,ES7.9.x版本开启gzip压缩可以压缩30%左右,但是新版本的ES8.x支持zstd压缩,压缩比可达40%左右,但是,想要开启ES数据压缩能力,就必需提升ES的吞吐保证不会积压的同步,提供更多的计算资源给到ES集群,针对以上两点,团队伙伴进行了如下的实施方案:

a、对kafka进行存储优化,首先,对不再使用的topic进行清理,对流量小的topic进行合并。其次,对kafka数据落盘进行压缩,减少磁盘空间的占用,对日志的存储时长进行延长,保证在集群出现问题时有足够和时间处理,不会出现因集群故障,kafka日志过期导致数据丢失的情况。

b、对ES集群进行压缩算法开启,在上面的logstash -> vector的组件演进过程中已经提升了ES集群的吞吐量,释放了足够的硬件资源,可以支持ES集群开启压缩能力,至此,集群资源利用出现了正向循环,首先对旧的7.9.x的版本直接开启gzip压缩,对新集群开启zstd压缩。

预期效果:   

a、通过对kafka集群的治理和优化,kafka集群在存储容量不变的情况下,数据保留时间由之前的4小时延长到了8小时,数据压缩效率明显,数据存储容量下降50%,为集群的故障处理争取到了更多的处理时间,但实际情况是故障恢复也没有出现处理过长时间的情况,但是承接了更多的业务日志接入,在没有增加硬件资源的情况下,kafka层面解决了日志增长对存储压力的问题。   

b、通过对新旧ES集群开启压缩算法,旧集群存储容量减少了30%,新集群的容量减少了近50%,整体存储容量下降了40%,在不断的新增业务日志接入的情况下,取得了预期存储治理的目的,为后面可以承接更多的新境日志接入腾出了足够的存储空间,节约了公司存储资源采购的成本。

下表是使用不同ES版本索引同一份数据的存储空间测试对比:

从表中数据可以看出,开启压缩前后在不同的ES版本中存储容量均有明显下降。

如何解决业务日志接入流程长的问题

实施方案:

日志接入从应用到ES需要经过多个组件,涉及到业务应用跟topic映射配置,topic跟日志索引映射配置,针对不同的应用日志需要设置单独的索引生命周期,分片数,索引模版以及查询模版。整个日志接入流程下来,需要大概花费耗时2小时。原来整体日志接入流程如下:

在经过日志索引规范治理后,团队对日志接入流程进行了分析以及流程优化,基本能实现日志流程接入自动化。实施步骤如下:     

业务应用上线提交工单申请,在工单提供集群,应用分区,应用名称,日志保存时长等信息。工单审批通过后订阅ELK系统接口,将参数传到ELK平台。       

ELK平台接收到应用上线信息,通过应用分区,应用名称自动生成topic名称,topic生成规则:prod-container_应用分区。将生成的配置更新业务应用topic映射表,vector生产端定时更新配置写入topic。

vector消费端,脚本读取业务应用topic映射表,判断是否有新生成的topic,有的话将生成的topic跟索引配置更新到vector消费配置里面,且调用ES接口创建新索引模版,初始化新索引。vector消费端定时更新配置写入ES集群。

调用ES接口,判断是否有新初始化的索引,如果有,调用kibana接口创建查询索引。

预期效果:

人工操作时间:单个应用服务从2小时→ 5分钟(无人值守)

接入应用量:单批次支持多个应用同时接入,减少人工一个个应用接入。       3.配置一致性:避免人工错误。

四、成果与体会

成果总结:

经过 3 个月的升级与验证,日志平台的性能、成本、可用性均实现显著突破,核心指标如下:

用户查询P99 < 5s,极大的改善了业务团队对日志平台之前体验不好的印象,查询超时问题,日志延迟问题,日志丢失问题等用户体验相关问题得到了很好的解决

集群吞吐能力得到的增强,由之前的45w/s提升到90w/s,吞吐能力提升近3倍,满足当前日志平台高峰期流量写入,很少再出现日志积压和消费延迟的情况; 

存储压缩能力提升了50%,容量降低了50%左右,较大的节省了硬件资源成本。

集群稳定性得到极大提升,之前只要是早晚业务高峰,集群就会有问题,积压问题,存储问题等等,由于可观测和告警能力的建设完成,稳定性的问题也很少出现,给业务伙伴排障提供了很好的支持。

体会与感悟:   

日志系统升级初期面临诸多问题,业务所有告警策略全部都是通过查询日志索引,稍有考虑不到地方就可能会引起大量误告警,给日志升级方案设计带来了很大的挑战,同时还要兼顾业务诉求,平台使用无感,数据不能丢失等,需要考虑诸多因素,团队伙伴在保障日志平台平滑升级过程中付出了诸多努力。 

因为星巴克的业务场景原因,日志平台所有的升级动作都必须要在晚上10:00以后进行,不能影响正常白天业务日志的查询,为此,团队伙伴经常作业到凌晨以后,现在回头再看,正是团队伙伴兢兢业业的奉献,才能使日志平台升级工作顺利完成,正是团队伙伴熬过的每一个夜晚,才换来了业务伙伴对日志平台体验提升的一次次肯定。 

日志平台是一个多组件构成的复杂系统,升级优化需要考虑每个组件在流程中的贡献和作用,以及可能的优化空间,各组件的参数使用需要上下游连动,全流程优化也需要考虑整个系统的木板效应,过程复杂且具有挑战性,只有坚实的做好每一项的优化,才能得到整个系统的性能提升。

五、未来发展规划

架构优化降本增效:依托全自动化日志接入流程的技术,推动采集层架构升级,实现 filebeat采集器结合CMDB自动路由写入 Kafka消息队列的闭环链路,精简现有架构中 logstash与 vector生产者的中转环节,显著降低计算资源占用率,进一步优化平台整体运营成本。

生态化整合:打通日志平台与 APM、监控平台的数据壁垒,构建 “日志 - 监控 - 告警”一体化运维体系,实现从 “被动排查”到 “主动预防”的转型。

智能搜索深化:引入大语言模型(LLM),支持自然语言查询日志(如 “查询今日dpfm接口错误日志”),降低业务人员使用门槛。