Bonree ONE 技术带动「新质」可观测

2024-05-16

2024年4月25日,博睿数据 Bonree ONE 2024 春季正式版焕新发布,以全息观测、体验至上、轻锐交付三项特性开启智能运维新篇章。点此立即申请免费试用。

作者|博睿数据数智能力中心负责人-太道

随着数字化转型的深入,企业对于IT系统的可观测性提出了更高的要求。可观测性不仅关乎系统的稳定性和安全性,更是企业决策和运营效率的重要保障。Bonree ONE作为国内领先的一体化智能可观测平台,本次春季版的发布,在新一代技术变革中为生产力升级保驾护航,后台三板斧(高效能架构、高技术引擎、高质量数据)让可观测更“新质”。

1

高效能架构

架构是IT系统的骨架,决定了系统的稳定性和扩展性。高效能架构需要具备轻量化、高可用和易维护性,以适应不断变化的业务需求和技术环境。Bonree ONE在本次春季版中从以下几个方面做了技术上的提升

1.多地多中心

Bonree ONE平台支持异地多活的高可用架构。一般在金融或其他重要业务领域中,客户业务数据分布在多城市多数据中心(DC),同时对可观测性平台也是一样的能力要求。具体需要可观测平台具备数据本地存储(避免过多占用跨地带宽),多数据中心资源可以全局统一管理但权限又可以灵活,并且调用链要包含完整的跨数据中心的调用过程和详情。这对万亿级以上数据平台的技术挑战极大。Bonree ONE技术底座实现了数据本地化存储,在ClickHouse集群存储层实现分布式表,本地先计算一次后再聚合数据分析结果(当然,里面有很多技术细节的考虑),并通过OneService的统一联邦数据服务建立全局数据地图和动态路由,做到了跨地跨中心计算,既做到了数据分开存,也做到了可以全局分析,还做到了任一节点的服务高可用和数据高可用。例如我们的四地八中心底层架构:

2

2.存储瘦身

早期时候,调用链、会话、日志的数据存到了Elasticsearch。我们历史架构如下图,大数据团队需要维护多种存储。比如,告警业务A说要做AI训练,就得自己加工一份时序数据到HDFS,指标中心业务B说加工指标,就自己加工一条链路到ClickHouse,DEM业务C想做会话分析,APM业务D又想做调用链分析,日志业务E还想做日志分析,那么业务C、D、E就分别加工各自数据到ES,这就导致完全各自业务各自加工存储。这样烟囱式的发展到一定程度后,数据不好管理,资源浪费严重,并且各业务之间数据很难做关联式的统计和分析(比如用户的网络请求和后端调用链关联的多端打通场景),产品迭代越来越重。

3.jpg

在IT架构中,只要组件多、存储重,很多问题就会暴露。为了彻底解决数据底层割裂、资源成本、性能、稳定性等问题,本次Bonree ONE春季版将所有信号数据全部迁移到了ClickHouse中,彻底下线了ES。

4.jpg

经过本次 Bonree ONE架构瘦身治理和全面技术优化,博睿数据公有云千亿数据量的集群,下线了所有ES机器24台(16C32G配置),扩容了10台CK,节省了58%机器成本。如果从资源占用比例看,收益更大:

5.jpg

3.ZooKeeper瘦身

众所周知,大数据很多开源引擎都用ZooKeeper做分布式协调和数据同步。作为ClickHouse大数据底座,我们原先架构也是用默认的用ZooKeeper去做数据管理。但随着数据量和数据种类不断扩充的情况下,ClickHouse集群对于ZooKeeper的压力越来越大。例如在基于实时性要求高的告警数据入库查询时,数据查询的实时性要求秒级返回,ZooKeeper会出现性能瓶颈(比如一套ZK集群最多支持5个shard),资源占用和维护成本非常高,还经常出现fullgc和zxid溢出的故障发生。显然,我们把调用链数据和日志数据迁移到ClickHouse后,原有ZK方案明显支持不住,如下图,看层次就非常复杂,20个shard需要5套ZK集群,每套ZK又有三个实例,在扩容和安装升级过程中,一步都不能错,否则就容易出问题。

6.jpg

本次 Bonree ONE 春季版中做了架构升级,我们把ClickHouse的ZK替换成了固定一套ClickHouse-Keeper的方案,同时从技术解决了多套转一套、ZK加密鉴权的问题。如下图,从层次看就很精简了:

7.jpg

4.安装包瘦身

在私有化安装包瘦身方面,我们技术也一直追求极致,所谓“加能力不加体积!小u盘就代表实力!”。经过一年的努力,我们安装包在依赖包精简、dockerfile镜像合并压缩、程序包精简、基础镜像瘦身等方面都做了相关治理和改造,从早期的11GB降到了本次发布的5GB左右,里面已经装进了全部可观测性的技能包(数字体验、指标分析、调用链、日志、全局拓扑、仪表盘、AI、告警,等等)。

8.jpg

高技术引擎

本次Bonree ONE春季版发布,技术亮剑出三大核心引擎(AI、OneService、ClickHouse)。就比如汽车,AI更像方向盘,能感知全域信号上下文,通过关系挖掘、文本挖掘、因果挖掘辅助动态的决策;OneService作为联邦引擎屏蔽了底层存储,并且从查询统一了标准SQL,提供核心腰部力量,更像传动轴;ClickHouse作为信号一体化湖仓底座,让Bonree ONE真正成为融合式平台,这跟传统的拼凑型产品有着质的区别。

9.jpg

1.AI引擎 - SwiftAI

SwiftAI作为 Bonree ONE 的AI核心引擎,本次从算法层面优化了自适应异常检测,自动按日和周级别聚类并满足不同场景分而治之,f1-score提高到了0.88,并和自研的混沌工程平台打通做可解释性验证和分析。根因分析方面,发布了新一代的融合式AI能力,跟数据集成自动化打通,做到了集成即AI,不用再额外定制化配置流程或开发,能自动和zabbix/promethus/skywalking/pinpoint等多源数据融合自动做根因,为用户带来了“1+1>2”的卓越体验;另一方面,根因算法做了进一步优化,从空间和时间算法上都做了调整,提高了收敛和根因的准确性。

10.jpg

2.联邦引擎 - OneService

OneService作为Bonree ONE的联邦引擎,实现了多源异构数据联邦计算的能力,统一了标准SQL查询,比如同时联查MySQL和ClickHouse。同时底层能力先支持了实体属性和标签分析的能力,这个相比业内比较领先。有了OneService引擎后,业务团队不再担忧各种信创适配的排期成本,主要工作由一个团队轻量级完成即可,这样可以更好更快的支持业务新创需求。

11.jpg

3.存储底座 - ClickHouse

在ClickHouse引擎优化方面,从读写优化到内核二开,从高可用到运维管理自动化工具都做了大量工作。我们早期开始用社区版ClickHouse时,平台承载力大概在85探针/台,如今已经可以支持到200+探针/台了,得益于不畏挑战的技术精神。

12.jpg

高质量数据

Bonree ONE是可观测平台,能力不断迭代,功能不断增多,要想做到加码不加价,同时还需要持续做数据治理。我们会针对各种数据疑难问题做单独的技术优化和治理。比如在调用链业务场景,千亿级数据量做维度排序,我们用分位特征算法将数据量扫描缩小1000倍,查询性能从分钟级降到ms级。

13.jpg

再比如,网络请求首页优化,通过projection技术,优化后页面从5s访问耗时降到了ms级。

14.jpg

写在最后

技术经过不断迭代,目前Bonree ONE平台页面的访问耗时,历史上首次做到了P99在3s以内。从Bonree ONE经历了几个大版本的正式发布来看,组件和安装包瘦身不断降到一半,但性能提升数倍。Bonree ONE在技术方面的深耕,也得到了ClickHouse社区的认可,已收到邀请发表相关文章和meetup演讲。静水深流,技术永无止境为业务保驾护航。

15.jpg

文章标签

可观测性平台

相关文章

FreeMarker template error (DEBUG mode; use RETHROW in production!): The string doesn't match the expected date/time/date-time format. The string to parse was: "2024年3月5日". The expected format was: "MMM d, y". The nested reason given follows: Unparseable date: "2024年3月5日" ---- FTL stack trace ("~" means nesting-related): - Failed at: #if "2024年3月5日"?date lt item.createTi... [in template "themes/halo_quickstarter/post_newsDetail.ftl" at line 106, column 25] ---- Java stack trace (for programmers): ---- freemarker.core._TemplateModelException: [... Exception message was already printed; see it above ...] at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.parse(BuiltInsForMultipleTypes.java:220) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.getAsDateModel(BuiltInsForMultipleTypes.java:190) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.getAsDate(BuiltInsForMultipleTypes.java:197) at freemarker.core.EvalUtil.modelToDate(EvalUtil.java:86) at freemarker.core.EvalUtil.compare(EvalUtil.java:270) at freemarker.core.EvalUtil.compare(EvalUtil.java:115) at freemarker.core.ComparisonExpression.evalToBoolean(ComparisonExpression.java:78) at freemarker.core.IfBlock.accept(IfBlock.java:49) at freemarker.core.Environment.visit(Environment.java:370) at freemarker.core.IteratorBlock$IterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:321) at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:271) at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:244) at freemarker.core.Environment.visitIteratorBlock(Environment.java:644) at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:108) at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94) at freemarker.core.Environment.visit(Environment.java:334) at freemarker.core.Environment.visit(Environment.java:340) at freemarker.core.Environment.visit(Environment.java:340) at freemarker.core.Environment.process(Environment.java:313) at freemarker.template.Template.process(Template.java:383) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:391) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:304) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:255) at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:179) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1401) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:497) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:584) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1631) at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:230) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.UserInfoFilter.doFilter(UserInfoFilter.java:37) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.jasig.cas.client.util.AssertionThreadLocalFilter.doFilter(AssertionThreadLocalFilter.java:54) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.jasig.cas.client.util.HttpServletRequestWrapperFilter.doFilter(HttpServletRequestWrapperFilter.java:75) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.MyAbstractTicketValidationFilter.doFilter(MyAbstractTicketValidationFilter.java:215) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.MyAuthenticationNoLoginFilter.doFilter(MyAuthenticationNoLoginFilter.java:57) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.security.filter.ContentFilter.doAuthenticate(ContentFilter.java:69) at run.halo.app.security.filter.AbstractAuthenticationFilter.doFilterInternal(AbstractAuthenticationFilter.java:229) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.filter.CorsFilter.doFilter(CorsFilter.java:53) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.filter.LogFilter.doFilterInternal(LogFilter.java:40) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:548) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:516) at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: freemarker.core.UnparsableValueException: Unparseable date: "2024年3月5日" at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:51) at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:33) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.parse(BuiltInsForMultipleTypes.java:213) ... 118 more Caused by: java.text.ParseException: Unparseable date: "2024年3月5日" at java.base/java.text.DateFormat.parse(DateFormat.java:395) at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:49) ... 120 more