InfoQ--博睿数据CTO孟曦东访谈实录:可观测技术是未来发展方向

2022-09-09

差不多在五年前,分布式系统已经成熟,微服务架构尚未普及,可观测问题就已经在桎梏技术团队的工作效率。一个To C的软件使用问题可能由客服发起,整条支撑链路的所有技术部门,都要逐一排查接口和日志,流程非常原始,也非常低效。如果业务到达一个量级,支撑系统变多,两名研发查上两三个星期也是常事。

微服务架构普及后,问题变得更加严峻。一个服务被拆分成数个黑盒的、虚拟的微服务,故障排除彻底成为一种折磨。这一切都使业务的可观测性成为2022年技术人必须关注的话题。

近日,博睿数据创始人兼CTO孟曦东做客InfoQ《极客有约》,与大家一起聊聊可观测技术究竟是什么?


以下为访谈实录:


InfoQ:微服务架构的普及对可观测带来了一些挑战,这些挑战又让运维领域发生了怎样的变化?

孟曦东:可观测不是一个新名词。2018 年,CNCF 将其正式引入 IT 世界,该理论的出现则可以追溯至 2014 年前后,主要来自于控制学,希望通过外部输出推断内部的状态变化。如今,技术栈发生了巨大变化,微服务可能构建在容器之上,容器又构建在虚拟机上,虚拟机则在物理机上,包括更复杂的网络支持,这让定位排障遇到了前所未有的困难。CNCF 之所以将可观测性带到微服务领域也是希望能有更好的能力控制系统的运行状态。

与传统的监控相比,可观测性的核心点还是有所区别的。监控可能更多在看现实状态的变化,很直接,但并没有表现出问题的核心点在哪。我们认为可观测性是对现今技术架构非常好的适应,可以用另外一种模型来判断风险所在位置,能更好地预防故障发生而不是简单地降级、限流。


InfoQ:如今,大部分企业还停留在粗暴的降级阶段,还是有意识做全局可监控?

孟曦东:可以分成两类,一类是发展靠前的企业,在业务体验或者用户感知能力上面要求较高,内部对此有很多 KPI,比如出现问题需要一分钟内发现,十分钟内解决等;另一类是农林牧副渔等领域的传统企业,目前手段还比较初级,只做到了单体的简单监控,整个上层的应用体系还没有完整建立起来。


InfoQ:具体到技术层面,可观测问题可以分为四类,分布式链路追踪、APM、NPM、RUM,方便介绍下这四者的核心思想吗?

孟曦东:从可观测性的建设体系来看,需要有三种类型的数据。RUM 可能更多关心的是用户侧,比如用户到底在使用浏览器、APP 还是小程序,使用体验如何或者整个运行过程中的数据能力是如何表现出来的;NPM 可能更多在描述链路层面,因为这是必备通道,是建立从前端到后台连接的必备过程,在描述整个数据流向的时候,流量数据又是什么样的表现;APM 把物理设备层面的能力提升到了以应用代码级为主,可以看最详细的代码状态,或者依赖的中间件以及 JVM 状态变化。整个链路追踪分段做数据采集,数据来源可能不同,但模型的核心是构建出一套完整的数据链条来帮助我们更好地判断业务受损到底是由哪个环节产生的问题。


InfoQ:APM 做到代码级别之后,还有进一步的改进空间吗?

孟曦东:改进空间肯定还是有的。第一,全链路可观测性需要了解代码的整体逻辑,这样才能更好地知道版本迭代时前后接口的变化;第二,我们也需要知道彼此之间的依赖项是什么,从技术内部来看,链路是非常多样化的,尤其是引用了容器云之后,随着 Pod 的增加和减少,链路变得错综复杂并且更加动态,我们需要有更完整的信息数据来支撑我们做故障定位。


InfoQ:国内外目前在可观测领域的技术发展现状大概是什么样的?

孟曦东:相对于国外来说,国内起步稍晚,我们可以看到国外有很多优秀的友商,在可观测能力的构建上已经非常成熟,他们还与 DevOps 做融合,加强安全方面的能力等。我认为国内在可观测性领域属于起步阶段,以博睿数据为例,我们今年才真正构建所谓的一体化全栈解决方案。


InfoQ:如何快速低成本地构建业务系统的可观测性?

孟曦东:构建一个所谓的可观测性系统有三个要素,一是要有数据;二是背后有一个强大的异构能力的数据引擎;三是需要有高效的查询。最直接经济的方案是看现在的情况是什么样的,哪些需要采购商业化的产品,哪些选择开源项目或者自研,最终对整体进行拼凑,这种方式会高效一些。


InfoQ:能否聊一下目前建设可观测体系通常的路径,比如说什么类型,或者什么规模的企业?

孟曦东:大体分为三类,第一类是自研的,比如头部的互联网公司,自己的研发实力或者研发资源非常多,在公司的发展过程中沉淀了很多有价值的东西;第二类是基于开源做二次构建,比如腰部的公司,打磨出一个可能适合自己或者组织规模的模型,或许 APM 就可以,不一定是可观测的解决方案;第三类是全部采买三方软件,通过这种方式构建可观测的能力平台。


InfoQ:目前市场上提供这种可观测的商用产品是不是也不多?

孟曦东:国外的产品不少,因为今年 Gartner 的 APM 领域调研报告也增加了可观测性象限,其中列出了一些新型公司。谈到可观测性需要解决的核心问题,也就是数据来源、对数据的理解以及分析利用,国内市场能完整覆盖的方案少之又少,国外在该领域的纯商业化公司更多一些。


InfoQ:大家比较熟知的项目 SkyWalking 是否适合微服务的架构?

孟曦东:SkyWalking 本身应该定义在 APM 领域更合适。如果是微服务,对探针端的能力是有要求的,据我们现在看到的,SkyWalking 还没有真正做到类似商业公司的探针技术,还做不到全智能的基于 K8s 的直接部署,动态探针以及自动命名。


InfoQ:可观测性技术在解决数据孤岛方面的作用是什么?

孟曦东:大多数用户的监控系统还是比较多的,可能有几套到十几套不等,因为监控系统也有可能是由于不同的组织内部不同的部门构建的,这样就势必会造成一个问题,因为没有从上层做统筹安排,把这些系统真正有机地组成在一起,供所有业务方去真正消费,孤岛问题就比较严重。我们希望能把数据从相互割裂的体系里面抽取出来,做一个统一的描述的模型,然后供不同的业务方去消费。不管是报警场景,还是运维场景,都可以落地到实际的业务场景里面,这样才能真正拉通。我们有一个很重要的特性就是三方数据的开放性或者兼容性,可以把现有的标准集成到一个平台里面,做统一的标准化,统一的模型建设,统一的落盘,然后再抛掉上层做不同场景的消费能力的支持。


InfoQ:AI 在监控领域的作用?

孟曦东:AI 赋能到监控领域分为几大方面的作用:第一也是最重要的是根因分析的能力,基础是建立一体化的数据平台;第二是希望可以做自动化的框架,不管是第三方的还是商业化的,通过我们的判断触发一些信息让业务做更有价值的动作,让人力可以得到释放。


InfoQ:如何看待国内可观测厂商 SaaS 发展的一个前景?

孟曦东:很多人都提出国内的 SaaS 发展与北美差异较大,我个人认为有几个要素:一是国内的市场环境或者技术栈还未到一定程度,北美也是从基础监控、做日志、做 APM 慢慢累积到现在这个程度的,美国云计算的发展领先中国五六年的时间,所以北美很多业务应用更习惯于放在几大云上;第二,国内存在一些行业政策的监管要求,比如金融领域可能有一些数据方面的安全要求,这也就限制了公有云标准化 SaaS 能力的交付;第三,产品能力,这个问题不该回避,国内的可观测能力确实还在起步阶段,在整个能力构建图谱上还有差距,如果产品没有打磨好或者没有特别好的能力价值输出,就会影响客户的买单意愿。


InfoQ:OpenTelemetry 项目目前在可观测领域比较受欢迎,这是为什么?

孟曦东:首先,OpenTelemetry 将原来部分定义的标准真正体系化了。我们很早就有了 OpenTracing ,但那只是定义了追踪数据的标准格式。任何企业或组织的技术人员,都希望能把某些能力标准化,这样不管是兼容第三方,还是自我迭代都会有一致性或者维护成本方面的好处。其次,该项目提供了非常丰富的 SDK 和 API 能力,可以让开发者和企业快速使用。最后,该项目基于 CNCF 基金会,其中有很多优秀的人物制订了标准。


InfoQ:在生产环境当中,如何选出靠谱的工具去解决可观测性的问题?

孟曦东:在生产中,环境是多样的,我们首先要找到能与当前业务发展情况较好匹配的工具,毕竟每一款工具或者平台都不是万能的,企业会有很多个性化的要求。对于企业级服务,是不是真的有一些标准或者制度可以约束出来,提供给 IT 人员做问题定位。在整个工作流里面,QA 测试完以后是否能覆盖到所有场景。

我们认为,对任何企业或者 IT 组织来讲,APM 工具都是必备的,因为可以把不同角色的人用同一种话术连接在一起。我们做运维、研发,或者业务 Owner,需要一个平台把这些标准融合在一起,避免大家产生不必要的纠纷。在 APM 之外,用户肯定还会再构建更完整的能力平台,因为不能只看到内部,还要看到除了数据中心以外的人的反应。因为这部分可能还会需要依托互联网,依托前端业务应用场景定位可能产生的问题,我认为这是一个有机的组合,根据不同的阶段以及人群使用场景构建出一套自己的体系。


InfoQ:博睿数据前段时间也在可观测这部分做了一些事情,发布了一体化智能可观测平台 ONE,我们怎么理解这里面的“一体化和智能可观测”?

孟曦东:一体化,我们认为就是要全面,数据能力要能覆盖到整个系统的云管边端的全数据链条。第一步是用三方能力接入或者博睿数据提供自己的数据采集能力把它构建起来。第二步体系化或者标准化的过程,真实构建一个立体的组织模型,否则会导致治理或者定义指标能力时出现混乱。第三步,我们认为一体化也是为 AI 提供一个底座,我们认为未来 AI 的价值不可或缺,在主动巡检、过程中的异常监测以及后面的根因分析,AI 技术在其中发挥了很大的价值。


InfoQ:国内目前可观测市场的未来发展技术方向是什么?博睿数据后续有什么规划?

孟曦东:如果我们认为 IT 运维是为了业务做服务或者做支撑,不是成本中心,IT 本身就会离业务越来越近,这肯定是一个必不可少的发展路径。反过来想,希望 IT 输出的价值可能也会发生改变,所以我们认为可观测性本身的核心定义就是 Google 谈的定位问题。如果业务是敏捷的,某个时间点的弹性或者高可靠无法代表全局。随着业务规模的逐渐膨胀,可观测性需要真正把冲突从根上解决,因为最终还是要定位问题,通过定位到的问题做好事前的风险防范、事中的问题排障以及事后的反思。我认为可观测性肯定是未来,不管是由于云计算还是其他技术的发展。


博睿数据今年希望先把一体化做扎实,再在其上构建其他的能力模块。现在因为测试左移越来越流行,我们准备将安全与 DevOps 结合在一起,同时在知识库和其他一些 ITSM 工具的整合上面下功夫,希望能帮助到客户做成一个有机的定位平台。

在未来发展中,博睿数据将从可观测性的广度和深度两个方面出发,不断丰富标准化的数据。并基于此深化数据相关性,加之博睿数据自研的Swift-AI中台赋能,从而给出更多更精准的信息判断,帮助客户快速落地高效可持续的观测--判断--优化闭环。


文章标签

可观测性平台 智能运维

相关文章

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