链路监控探索实践一 现状分析与分布式追踪系统调研篇

    原创文章,转载请注明出处。由于本人水平有限,文中错漏之处在所难免,希望大家多多批评指正。本文的内容过多,我们分成四篇聊,此为第一篇。

    最近一段时间一直在忙着接分布式追踪系统,每天噼里啪啦敲着代码玩得还是很开心的。这里要感谢敏哥、宇飞、祥哥、金俊、媛媛姐对我的支持和帮助,尤其要感谢金俊同学对系统运维问题的专业解答,感谢媛媛姐超级有耐心的帮助调试接入过程中遇到的各种问题。目前系统已经上线,通过一段时间的观察,没有出现丢失数据、崩溃等异常情况,运行稳定。趁着告一段落,记录下自己学到的知识点,聊一聊这期间遇到的一些问题、思考和自己的一些感受,也为其他考虑使用追踪系统的团队提供一些参考。

先说说背景
  • 业务:我们是做旅游出行产品的,主要涉及的业务有机票、火车票、汽车票、用车。
  • 架构:前后端分离,我们的后端整体采用服务分层架构,服务间使用自研的RPC和HTTP框架通信。
  • 技术栈:后端主要是Java和PHP。
  • 部署:分布式部署,使用Docker部署在私有云上。
  • 运维:运维同学自研了一些工具,有CI/CD工具,Docker日志的界面化查询工具,线程堆栈dump工具等等。

    每一个团队都会希望研发的产品能够带给用户更多的便利,被更多的用户所喜爱,我们也是一样,用户的肯定会带给我们极大的动力。而用户的增长,使用率的上升自然也会导致访问量越来越大,带来的问题自然也就多了起来。很多的用户又会希望产品的功能更加的丰富,能够不断的满足他们新的需求,这样涉及到的服务也就会越来越多,问题的复杂度自然也就高了。数量和复杂度两个维度的增长给排查定位工作带来了很大的难度。在预防和解决线上问题,保障用户使用体验的的过程中,我们的团队也不可避免的遇到了很多麻烦,先来看看有哪些问题,带着问题再去寻找答案。

技术支持的痛点
  • 我们处于整个架构的中间层级,如图1所示,上接前端请求,下连各个业务方的服务,自身需要整合不同业务方的接口,为前端提供一个完整的功能入口。处于这个层级,基本就是排查所有用户问题的最前线了,实际上大部分的线上问题都会先分配给我们。而我们就很尴尬,尴尬的点在于这个接口既有我们自己的业务代码,也有处理依赖服务方接口的代码,虽然从以往的排查经历中看,大部分的问题都是出在服务方的,但是还是没啥底气支撑我们直接把问题分配给服务方。导致每一个问题都需要我们先排查,定位它是是哪一方的问题。排查问题又是一个很耗时的事情,尤其在业务复杂、涉及到多个部门的时候,很有可能需要两三天的时间才能定位到问题的原因,我们的人力又很有限,这就让我们有点头疼。那有没有什么方法可以缩短定位问题的时间?至少能快速的判断出问题出现在哪一个部门、哪一个服务方,这样可以直接将问题精准的分配到对应的责任方,既能加快处理线上问题的速度,也能大大减少我们的工作量。
图1 我们团队所处的位置,涉及到与多个业务方的交互
研发的痛点
  • 如图2所示,用户发起的一次前端请求在服务端往往需要调用多个服务来获取数据,调用的服务越多,调用关系就越复杂,很难理清接口间的依赖关系,调用顺序。尤其是经过多次交接,历史悠久的系统,几乎没人能够理清调用关系,导致很多时候不敢改动系统,怕造成其他接口甚至其他系统的异常。而如果真的有改动,往往需要经过全流程、多版本的回归测试,甚至很可能要几周的时间,严重拖慢了新功能或者bug修复上线的进度。假如我们能够快速、直观、清晰的绘制出接口调用关系的拓扑图,这个问题就好解决了。
图2 用户的一次请求会涉及到多个服务的相互调用
  • 我们的系统开发过一个功能,为每一个前端请求生成并附着一个唯一id,在之后的处理过程中需要记日志的时候,也将该id一起打到日志中。这样根据这个id就可以将一个接口的本地调用日志串联起来,大大方便了我们定位问题。不过这个id只能拿到一个接口的日志,在需要获取多个关联接口的数据时就没办法了。比如:用户反馈他在预定时看到的飞机起飞时间是15点,而出票打印的起飞时间是22点。这个时候就需要根据用户下单时的数据去关联出当时航班详情接口吐出的起飞时间是多少了。

  • 为了更好的对日志进行查询和统计,线上的日志数据统一接入了ELK(一套开源的日志采集、存储、查询系统)。随着访问量的不断增加,日志量激增,给ELK带来了很大的存储和查询压力。而公司的服务器资源又很紧缺、扩容困难,导致ES存储容量达到上限的时候,会拒绝写入请求,丢失数据。无奈下运维同学写了一个定期删除数据的脚本,每天会定时删除超过一段时间的数据。目前从Kibana可以发现ELK最多支持查询最近5天的日志,甚至可能更少只有3天,如图3所示,如果需要排查五天前的问题就没有办法了。

图3 Kibana最多只能查询到最近5天的数据,甚至可能更少只有3天
  • 有一些业务为了能查到更早的日志数据,在ES无法扩容的情况下,就只能采取人为减少日志打印这一种方法了。一般会采用只记入参,不记出参的方式减少日志,这样也会造成日志不全,无法还原当时接口数据的情况。

  • 有一些接口因为访问速度慢,崩溃频次高常常会影响用户的体验,降低服务的吞吐量,甚至会导致雪崩效应的发生。这些接口往往是潜在的问题接口,甚至本身就是问题接口,是研发比较关注的,需要优化的接口。而发现这些接口,往往需要依赖一些接口统计与查询的功能,比如接口的错误统计,响应时间查询等等,这些功能可以辅助研发做问题预防和服务优化,我们目前暂时没有这样的工具。

运维的痛点
  • 运维同学在开源ELK基础上加入了Kafka队列,如图4所示,将Filebeat采集到的日志数据直接写入队列,利用Kafka的持久化能力提高了ELK的可用性。在遇到ES磁盘空间不足,消费者消费缓慢或者其他异常情况导致消息积压时,如果积压超过两个小时,Kafka会直接丢弃数据,导致有时日志不全。
图4 运维ELK架构
  • ELK不是很稳定,直观感受就是Kibana经常报错、查不到数据或者挂掉。尤其是在低版本的ELK上发生的频率较高。
测试的痛点
  • 我们的测试环境是不允许接ELK的,猜测是运维同学考虑到已经有一套工具支持Docker日志的界面化查询,同时测试环境限制很少,可以随时需改发布和断点调试。这样日志也就不需要长期存储,只需要保证容器存在时可查,而在容器销毁时是可以一起销毁的,不但可以减轻ELK的压力,同时节省了资源。在平时使用中感觉这套日志查询工具还是很好用的,不过在查询结果的展示上有个大问题,它只支持展示搜索关键字命中的那一行文本,像异常堆栈这种需要同时显示多行的数据就没有办法看到了,如图5所示。看不到异常堆栈,测试同学就没有办法定位问题是出在调用方还是服务方了。
图5 红框部分,无法显示完整的异常堆栈
  • ELK实际还是以日志文本的方式展示,对于较复杂的调用,单从日志上比较难理清调用关系和调用顺序,也对无编程经历的使用者不够友好。

    公司现有的运维工具和研发工具还是有很多的短板的,在日常的使用过程中很多人会觉得有一些情况下没有真正的解决他们的痛点问题,工具不给力只能人力来凑,工作效率也会有所下降。基于这样的情况,我们就在考虑有什么办法可以解决这个问题。其实业内已经有成熟的解决方案,就是分布式追踪系统。

    早在2010年,Google发表了一篇名为《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》的论文,在文中介绍了Google生产环境中大规模分布式系统的跟踪系统Dapper的设计和使用经验。基于这篇文章,有很多公司实现了自己的分布式追踪系统,比如阿里的鹰眼,京东的Hydra等等。更有很多公司开源了他们的追踪系统,使更多的人受益。

    考虑到这次接追踪系统真的是时间紧任务重啊,5678月份是我们的业务旺季,迭代的需求很多,没有多余的人力来让我做追踪系统的事情。所以从调研到编码,从搭建环境到SIT测试都只能使用我的休息时间,在时间很有限的情况下,我考虑的是如果有成熟稳定的开源追踪系统,我们就尽量不要自己造轮子了,享受开源带来的便利。那让我们先看下主要有哪些经常提起的开源追踪系统,能否满足我们的需求。

调研
  • Twitter:Zipkin
    Zipkin
  • Naver:Pinpoint
    Pinpoint
  • Uber:Jaeger
    Jaeger
  • 大众点评:CAT
    CAT
  • 华为:SkyWalking
    SkyWalking

    这几个系统在github的star都有几千以上,社区活跃度很高,也被一些知名的大公司和很多小团队使用过,网上的使用文档也比较全面,可用性和稳定性都是有保障的。我们拉个表格,直观的对比下这几个系统的异同,先互相伤害吧。
互相伤害

    选项有点多,也各有优缺点,选择起来很难。感觉像我们考试的时候遇到不会的选择题一样,这个时候我一般会从反方向思考,不是选择哪个,而是排除哪个。使用排除法来排除很明显不对的,减少选项,最后再蒙。。。

    让我们先从反方向,参考我们的需求,使用排除法缩减下选择范围:

  • 我们需要快速的接入,所以追踪系统对接入方代码的侵入越少越好,这样我们排除了对代码侵入较多的CAT,而对代码也有侵入,只不过侵入较少的Zipkin和Jaeger我们先划到待定中。
  • 考虑到时间紧的情况下应该尽量使用自己熟悉的技术,而公司的研发中较少有人熟悉Go语言,加上Jaeger具有代码侵入性,所以我们排除了使用Go编写的Jeager。尤其后期可能需要二次开发,需要对自研中间件提供支持,也需要做到对接入过程中的风险可控,使用熟悉的技术更有利。
  • 考虑到需要支持TraceId查询,需要支持OpenTracing。数据库选择上,最好支持Mysql或者ES,这样我们排除Pinpoint。
  • 考虑到需要尽可能丰富、功能强大的Web UI,尽可能减少性能损耗,尽可能更细的追踪粒度,尽可能低的代码侵入性,我们排除了Zipkin。
  • 最后只剩下了SkyWalking,以选择题来说这个一定就是正确答案了,没想到so easy。。。

    再让我们从正向看一看SkyWalking(下文简称sw)能否满足我们的需求:

  • 从系统接入来看,需要尽量减少接入方系统的变动,尤其是代码的变动,因为代码变动往往都需要经过Review,功能测试,回归测试等等,会很大程度的延长上线的周期。所以追踪的agent最好对代码无任何侵入,无侵入可以做到只需要把agent放入到业务方共用的dock镜像,业务方在日常的打包发布中就可以做到无感知接入了,极大的减少了工作量。而sw的agent使用的Java字节码增强技术,符合要求。
  • 从需要支持的插件来看,我们需要支持Tomcat,SpringMVC、SpringBoot等Spring全家桶的部分框架,Kafka,ActiveMQ,Apache HTTP Client,log4j,log4j2,Logback,Redis,Memcached,Mysql等等组件。这一点看sw符合绝大部分要求。
  • 从二次开发来看,公司后端研发日常使用频率最高的编程语言是Java和PHP,而Java和PHP的研发人力也很充足。考虑到后期可能对使用的追踪系统系统做二次开发,以满足公司业务需要,我们选用的追踪系统最好是使用Java或者PHP开发的,这样可以减少使用一门新语言的学习成本和出问题的风险,即使出问题也可以做到阅读源码来排除,做到风险可控。而sw是使用Java编写的,符合要求。
  • 从扩展性来看,目前大部分的开源追踪系统系统都提供了对开源框架、中间件的追踪支持,不过仅限常用的、而且仅限部分版本。如果我们需要使用这些以外的组件或者使用该追踪系统放弃支持的较早版本,或者还来不及支持的较新版本,我们就只能等,等官方,等社区,等公司,最坏的情况就是自己开发了。尤其是有很多的公司出于各种原因会选择自己研发中间件,要想对这部分自研组件进行追踪支持,就一定要自己开发了。我们公司就是这种情况,有很多自研中间件在使用中,所以我们选择的追踪系统系统一定要有很好的扩展性,能够通过简单的开发,快速的扩展。而sw的扩展是很方便的,符合要求。
  • 从可替换性来看,每一个系统都可能因为各种原因停止更新,所以我们要尽可能的减少替换一个追踪系统的工作量,最好可以做到无缝切换。这样要求我们选择的追踪系统需要支持OpenTracing规范。而sw是支持OpenTracing的,符合要求。
  • 从运维角度来看,公司系统普遍使用的都是Mysql数据库,对Mysql熟悉的研发和运维有很多。我们常用的ELK使用的是ES做数据存储,运维同学对ES也很熟悉。所以追踪系统最好是支持其中一种数据库的,这样可以很快的搭建新的存储环境和接入数据。同时以后的日常运维和异常处理也都无需业务方关心,减少了使用的成本。而sw既支持Mysql,也支持ES,符合要求。
  • 从功能上看,需要至少能够满足我们当下的需求,最好也可以满足未来一段时间内的需求,尽量减少我们的开发量。而sw的web页面提供了TraceId查询、耗时查询、调用统计、系统拓扑、告警等等功能,符合要求。
总结

    基于以上原因,我们最终选择了SkyWalking作为我们的分布式追踪系统,帮助我们更好的研发产品和服务用户。这里要感谢所有共享优秀程序,无私开源系统的人们,尤其感谢SkyWalking的作者吴晟老师,感谢吴老师团队研发的系统对我们的帮助。最后希望我们也可以研发并开源一些对他人有用的作品,可以帮助到他人,让IT的世界更好。

参考资料

Dapper,大规模分布式系统的跟踪系统

CAT

Zipkin

Pinpoint

Jaeger

SkyWalking

你可能感兴趣的:(链路监控探索实践一 现状分析与分布式追踪系统调研篇)