本文根据吴燕军老师在〖deeplus直播:逆袭生产力担当,云原生时代的运维新归宿〗线上分享演讲内容整理而成。(文末有回放的方式,不要错过)
大家好,我是来自于去哪儿网的吴燕军。目前在公司主要负责监控系统的开发、容器化以及上云过程的相关工作。今天的分享主要包含以下几个方面的内容:
一、基本介绍
去哪儿网是一个发展得比较久的公司,我们有很多的业务线,而这些业务线反映到公司的架构层面就是多个部门。最初的时候,我们每个部门都有一套独立的监控系统,一般都是Cacti或Cacti+Nagios,Cacti主要用于画图和看图,而Nagios则用于配置报警策略。每个部门之间相互独立。但这样就带来了一些问题:
Cacti单机部署,不能横向扩展,性能差,非高可用;
各部门维护一套甚至多套,运维成本高;
报警配置有专人负责,不能由开发人员定制,效率低;
各个监控系统之间数据不通,不便于整体分析和使用。
1)基本特点
基于这些问题,我们提出了一个解决方案:搭建企业级统一监控报警平台,也就是我们的Watcher系统。这个Watcher系统它有些什么特点呢?
首先,Watcher系统是基于graphite深度定制开发。也就是说,它的整体核心都是graphite以及Carbon和Whisper这一套服务;
然后,它是多种开源产品的有机结合。包括Grafana等;
最后,这套系统完成以后,我们可以实现海量监控数据实时处理。
这是它的基本特点。
2)四大特性
Watcher系统支持主机的基础监控报警,比如CPU、Load和Memory、内存以及swap等,它的报警采集、采集记录以及画图都是自动添加的,用户不需要参与。我们会记录业务指标、分析类结果统计指标,比如Flink任务采集到的log指标以及异常日志的分析、统计等。Watcher系统还提供嵌入SDK记录的访问类指标等。由于我们的整个技术栈比较统一,基本都是Java类的服务,因此我们提供了Java的SDK,允许开发人员记录它在整个使用过程中所有的监控信息。这样一来,开发人员可以记录某个接口甚至某个函数的访问时间情况,操作会更加便利。
3)支持协议
Watcher系统支持多种协议。最初它是基于graphite定制开发的一套监控系统,而graphite是基于Python来开发的,所以它天然支持Python的Pickle协议,即TCP协议,同时它也支持UDP协议。也就是说,Watcher支持两种协议来采集数据。
TCP和UDP本身在特点和性能上存在一些差别,TCP可以支持大的TCP包之类的处理逻辑,但UDP可能并不支持这种特别大的数据和处理方式,它们各有利弊。
4)采集方式
Watcher系统支持推、拉两种数据采集方式:
我们更倾向于采用通过server端拉取用户所提供的, SDK上的数据的方式。因为这种方式更可控,如果SDK端有异常或其它情况,我们可以感知到并做出相应的动作,如拉黑等;但是像一些分析类的服务,比如Flink任务、一些日志分析的任务等。这些分析完的系统的如果没有暴露HTTP的接口等途径,那我们是没办法拉取的。所以在这里,我们允许它作为一个数据推送到我们服务。因此支持两种方式。
5)系统架构
上文有提到,Watcher系统是多种开源系统的有机结合。如图所示:
Watcher系统涉及到较多的开源系统,在这里我先给大家简要介绍一下它的定位。
首先是Graphite。Graphite可以说是整个系统的核心。我们最核心的存储也就是时序数据的存储、Whisper以及它的数据高可用、多副本的处理都是Graphite来做的,当然我们也做了很多二次开发来调整整体的架构;同时我们也使用了Grafana来做展示和看图的Dashboard;用PG来存储一些元数据信息。
这里需要说明的一点是,虽然Graphite最初的版本不需要源数据DB,而是直接根据文件目录来查找整个指标的内容;但后续,Graphite发展到1.x版本时,它是提供了一个tag指标类的查找方式。而使用了这个方式之后,它的指标会被哈希掉,也就是说,一个指标会变成一个模糊的哈希串。那这时候完全没办法把它反过来找到我们的指标名了,所以就需要一套监控系统或者说一套记录原信息和哈希串对应关系的系统,也就是我们现在使用的PG系统。
当然我们开始并没有使用Graphite提供的方式。因为我们使用了一种多集群使用Graphite的方式,这使得我们天然需要一个记录原信息和目标指标的对应关系的数据库,所以我们其实很早就使用了PG的数据库。
近期,因为业务发展以及一些用户的诉求,我们其实也在较为深入地使用ClickHouse,这也是比较新的一套开源的OLAP作为在线数据分析的存储数据库。它在我们的Watcher系统有比较重的使用。
另外一个就是Icinga。Icinga其实是一个报警的管理和通知的模块,类似于Nagios的升级版。当然,这些基本上都是开源的系统,我们也都在上面做了一些定制化的开发和处理。
以上是我对Watcher系统的基本介绍,有些内容可能比较偏概念,所以有些无聊。接下来我会给大家详细地说一下这个监控系统的整体架构情况,这里是一个整体简化过的架构图:
1)指标收集
上文提到,Watcher系统支持多种指标类型,像一些普通的基础监控,即CPU、 Load等。体现在整个架构图中,就是左上角这部分。主机的CPU、Load、内存等,都会通过基础指标直接上报到我们的Grelay,也就是一个数据分发和处理的模块,我们基于Graphite和collectd做了一个二次开发,增加了一些定制化的功能,这就是基础指标的数据收集。
然后是业务指标的数据收集。业务指标这一块对应到了一些像Flink任务、日志分析任务等数据处理逻辑,它们都会采用一种同样的方式把数据推送到Grelay服务,由Grelay服务做到多副本或哈希的存储,保证数据存储到它应该到的地方。当然,Grelay也会Mirror出一份元信息存储起来,记录元数据和真实存储位置的对应关系。
另外是我们比较推荐的,同时也是现在使用比较重的一个系统,就是Qmonitor系统。我们提供的很多定制化功能都是在SDK做的,SDK会自动记录一些JVM的监控信息,甚至一些接口类的其他信息。提供了SDK之后,它会对外暴露一个HTTP接口,这时候我们会通过Qmonitor的 server端抓取这些数据,做一定的聚合处理后,上报到我们的存储系统进行存储。
2)存储
这里比较有意思的点在于,Qmonitor是一个比较抓取存储的系统,而SDK又被放给了用户。随着它的使用发展,在用户或开发人员迭代等时候,可能会出现SDK使用泛滥的情况。很多信息都记录在SDK里,我们难以判断它能否清理。现在这个问题可以借助混沌工程、辅助代码、指标的清理逻辑之类的方式解决,但在过去,就会导致需要处理的数据量巨大。
我们原先的监控系统的整体技术栈是Python,在处理 SDK抓取以及聚合计算并发送到Grelay的时候,它的多进程和多线程的模型,现在延时已经到了分钟级,比较极端的甚至已经达到了4分钟。
对于监控这种需要及时发现问题的系统来说这是不能容忍的,所以我们在这一块做了一些优化。把监控系统从Python的抓取转变成Go的方式,因为Golang对于异步或并发的支持是天然比较友好的。另外,相对于Python,它是个强类型的语言,不需要面对Python的GIL锁以及变量类型解析等过程带来的损耗,整体能够带来8倍到10倍的性能提升。
单机树系统采用了支持多种数据存储的ClickHouse。一般使用Qmonitor,它是支持数据聚合逻辑的,而单机数这里相当于把数据聚合逻辑直接设置为不聚合,会带来数据量的大量膨胀。但ClickHouse有一个特点,它对批量写的支持非常好,能够满足我们的写入要求。Clickhouse在使用过程中能够达到每秒百万级以上的写;但它读请求支持的QPS不是特别多,因为ClickHouse在每次读请求时可能还需要处理大量数据。所以我们对于单机数的定位可能更多在于辅助和排查解决问题。
用户使用Qmonitor server抓取元信息,并存储到我们的监控系统,通过后续报警来使用这些数据。而一旦发现问题,就可以反过来使用ClickHouse查询发生问题的具体位置。比如整个服务有10台主机,一般出现问题的可能只是其中的某一台或是某几台。我们如何才能快速定位问题所在呢?这时候就会使用到单机数系统,我们会把所有Qmonitor抓取的原始信息全部记录下来,储存到ClickHouse。
3)优化
①资源匹配
需要注意的是,这个系统的读和写都非常极端,而且对于我们来说,它只是一个用来分析、发现问题的抓取分析的系统。因此,在对系统资源进行分配时必须进行优化。目前这套单机数系统大概使用的存储和及解析指标的机器,大概是聚合存储的1/10到1/5,使用比较少。我们是怎么做到这种优化的呢?
首先是ClickHouse支持批量写这一部分,这部分我们不需要改动太多。ClickHouse基本上没有做太多开发,只是在每台机器访问ClickHouse的时候做了一些高可用授权及健康检查。整个系统最大的优化点在于:如何让解析系统使用更少的资源支持更大的请求?
这一块我们采用了MQ的形式,对数据的抓取和处理做了解耦。在数据抓取时,一般抓完一次就直接把它塞到 MQ,所以它的业务逻辑并不重,不需要进行特殊处理。
②数据解析
所以我们最大的优化点是数据解析,每进行一次的抓取解析服务就需要起一个协程去处理,但整个系统在使用过程中支持的处理能力却并不高。经过对比分析候发现,当大量数据涌入时,解析代码的这部分会存在好几万的抓取逻辑,基本上都有并发的几万个协程处理。这样一来它带来的调度以及其他开销就会很高。为了解决这个问题,我们采用了Golang中的channel的方式,在内部又做了一个小的队列。channel后面会对接到一个类似协程池的形式,固定几个来处理协程。而我只需要重复从 channel里取出数据并进行处理这样一个过程。优化完成之后,单台32核左右的主机的系统性能基本上能达到每分钟处理1亿或是1.2亿的量级,也就是说,它每核基本上每秒可以处理10万+的指标。
③信息转换
当然这部分还有其他一些需要优化的点,比如原始信息的转换等。因为指标基本上都是一些文本信息,但我们需要做一些特殊的处理和检查,把它关联到某个具体的请求,记录它的原始信息或处理等。整个文本的处理可能会涉及到字符串和比特流的转换过程。而整套系统只是把数据做一些辅助信息的添加和处理,然后扔到存储的一个中间系统。能否抛弃比特流到字符串之类的转化过程,直接采用比特的流式处理呢?
基于这种想法,我们在拉取HTTP的文本到单机数系统之后,直接遍历循环整个文本的比特流,再基于比特流做数据的切片和合法性校验,把结果扔给批量写的服务,批量写入服务聚合成一个大的二进制文本,再批量写入ClickHouse。这里是采用单个TCP的发送方式,带宽转发数据基本在1G以上,性能比较高。这是我们在Qmonitor以及单机数系统处理的时候所面临的问题、它的特点以及我们的优化手段。
架构图中颜色较深的部分是存储系统,当指标数据抓取、存储到整个存储系统之后,我们提供了一个统一的API来访问这个服务,对外所有的数据或看图类的请求都通过 API来执行。这样一来,用户在使用过程中就不需要关心每一个系统单独的实现方式是怎样的,而只是作为一个解耦的方式更方便大家使用。用户可以访问API做一些定制化的使用,比如访问指标值、对它做一些定制化处理或者一些其他的预警方式,这些都是可以支持的。
④报警抑制
从用户的角度看,他的所有请求都会落到Dashboard系统上。而Dashboard系统提供了一些报警配置的功能,当它配置完成之后,报警就会落到Dashboard存储库。当然,他也可以查询我们的一些指标值或者是单机数值,直接通过API来拉取数据,这是一些用户的使用行为。除了用户使用行为,监控系统其实更多是在和报警系统打交道,或者说交互。我们不可能一直人肉盯着整个服务或者指标的趋势,因此报警系统就显得格外重要。什么系统、什么时候出现了问题?怎么让用户及时感知到?以及如何尽量避免打扰用户等。
当用户配置的报警信息落到了Dashboard存储库之后,我们的报警解析模块会读取整个报警信息以及它配置的规则,真正拉取整个指标值,并根据它的规则进行,判断是否满足报警条件,然后把它推送给Icinga服务,由它来做报警管理,通过Qtalk(内部IM)、电话等方式通知用户。这里包含了一种升级的规则,因为一般Qtalk这种工具大家下班或者其他的一些时间不太经常看。如果真的发生大问题,就需要更及时的一种通讯方式,也就是电话。我们的报警最初是发送 IM消息,如果大家看到并处理掉之后,系统就不会再打电话;但如果用户没有关注到,而报警一直在持续的话,我们就需要实行报警升级策略,进行电话通知。当然,如果用户认为整个报警并不是很重要并进行了关闭报警等,系统也不会打电话。
当用户发展到一定规模之后,大家在处理报警或者配置报警的过程中一定会面临一个很大的问题:如何避免轰炸到用户?比如我同时配置使用了特别多的报警,一旦出现问题,我的手机或者其他的联系方式就会一直接收到报警消息,但有些报警其实在一段时间后会自动恢复。为了避免出现“狼来了”效应,我们提供了一个报警抑制策略。如果一个服务基于报警解析,判断它满足报警条件,但通过报警抑制模块后发现这个报警一直在闪报,每次报警后过两分钟就恢复了。那这个时候系统就会倾向于认为这个报警并不重要,没必要一直打扰用户。所以系统会采取报警抑制策略,把它标注为被抑制状态,保证不骚扰用户。
这个算法并不是复杂,只是基于用户的使用过程、历史的报警情况以及报警的重要程度进行评判。为了保险起见,我们也设立了一个绝对值,如果某次报警的持续的时间比较长,就会自动退出报警抑制状态,通知用户。这是我们的一个兜底手段。
以上是我对于整个报警系统架构的介绍,在这个过程中也涉及到了我们面临的一些问题以及优化手段等。介绍完了监控系统的架构,接下来我给大家简单介绍一下它整体的使用情况。
二、使用情况
我们目前的业务指标已经达到了6000万的量级。而且,因为一些树结构的组成形式等,它的真实存储可能已经到了7000万;一些基础监控指标也已经达到了2.5千万左右,这里我们采用了一种分级的方式,按照点分级组成树结构的形式,结构形式比较复杂,在5000万到6000万之间。
大家可以看到,业务指标和基础指标之间存在很大的区别,为什么业务指标和基础指标会存在两倍以上的差距呢?基础指标基本上属于扁平类的指标,是对于指标应该怎么使用的规划,规划没那么多,那他们就会看到指标上的差异。
什么叫多环境指标?直观地说,因为我们线上的业务可能会分为测试或者线上环境等多种环境,也就是说,我们支持线上、测试、甚至是其他的一些灰度环境来实现指标抓取。当环境增多后,它的指标量会比较少,而且从存储策略上说,我们对于这种指标存储的时间周期会比较短,当然,这种多环境指标是排除了线上的指标的,这是我们指标量的使用情况。
业务类的报警大概在15万左右,每周的报警情况更是将近100万,这里大家也可以看到报警抑制的必要性。当然这一块也能看到我们的基础监控和业务监控在使用情况上的差别,业务指标较多,但它的报警量反而相对较少。
最后是单机数大概处理的情况。目前,单机数每分钟写入的点数在4000万左右,但它每分钟处理的点数却将近1.3亿,为什么会有这么大的差别?这是由于我们实施的一些优化手段。系统每分钟处理1.2亿之后,写入4500万,剩下2/3是什么呢?大家可能不太关注,一般在监控指标中存在很多0值指标,尤其是在指标量特别大的情况下。比如有些业务我访问过一次之后,这个记录一直被保留了下来,没有被清理掉,但他的值其实一直是0。
这一类的指标存在非常大的比例,当我们处理完每分钟的指标之后,会把所有的0值指标全部丢掉,这样一来写入量就是1/3,相当于做了一个定制项的优化。大家在其他系统的使用过程中可能也会面临这种情况,这时候可以考虑一下这种优化手段。当然,并不是所有的0值都没用,我们对于线上的0值会比较慎重,只是把单机数的0值去掉。这样就能让单机树系统使用更少的资源提供更多的服务。
三、可观测性
了解了监控系统的使用情况,接下来是对它的可观测性的介绍。
从这张图中,我们能够比较直观地看到可观测性结合的内容。之前有同学问到:监控和可观测性有什么区别?这个问题的答案我们可以从这张图中找到。监控系统主要集中在一些指标、指标的聚合以及聚合完后对应的报警等;而可观测性是一个更新的概念,它把指标以及整个访问过程的链路、日志的情况进行结合,结合完之后,它们之间的交集,或者说它结合的整个过程,就是可观测性的内容。
上文中,我们比较详细地介绍了 metric这一部分的内容,接下来我会给大家会介绍metric、Trace以及log的部分关联,也就是我们在可观测性上做的一些工作。
这是我们整体的架构方式。可观测性的诞生其实是从微服务开始的,而它带来的问题就是服务的混乱。比如我访问B服务并向它要了一个数据,而B服务可能掉了CDEF等N个服务而我并不知道。一旦发生问题,我又不是特别熟悉这套系统,那就很难排查到问题的真正位置。最多只能定位到 A跟B要数据时B服务提供的数据错误,但是为什么会这样呢,大家并不知道,这就是可观测性上做的一些事情。
那么整体的架构就是:我们中间件团队提供Agent的方式来采集一些可观测性的信息,包括记录它的Span等;当它记录完成之后,就会通过ELK的形式,将信息收集到Kafka系统;当收集Kafka系统之后,Flink任务会处理分析这些记录的Trace或Span信息,建立相应的索引存储到Hbase之类的数据库。整个过程也会有一些其他的索引建立来帮助快速定位,日志基本上也是以同样的方式收集到Clog服务,并通过Flink这一套数据处理方式进行索引建立和存储。
从图中可以看到,因为可观测性是后提出的概念,所以我们的日志和Tracing的服务与指标服务并不会混合在一起,Metrics服务或者说监控服务基本上是一个比较独立的过程。从监控报警中收完指标,同时Tracing收完它的SpanID、Log收完日志之后,它们要如何实现关联呢?这里我们是通过开发一套新的服务让它们实现关联。
因为有指标信息的存在,所以我们会基于关注的指标做一些定制化处理,将它推送到收集的Agent上,让它记录这些指标的Tracing,当它记录完成之后,Trace会带着这些指标信息以及它自己生成的SpanID类的信息解析到存储里,同时Flink处理时,也会再生成一份指标和ID的索引信息。这样一来就可以实现指标和Span的关联。日志也是同样,当问题发生之后,我只需要把SpanID输入到日志里,就能找到相对应的信息。通过指标我们可以关联到Span信息或者trace信息,而通过Span ID,我们也可以找到对应的日志, 这就是我们的整个架构的关联方式。
接下来给大家展示一下我们这一架构方式下的成果:
这是我们监控系统的界面,大家可以看到,当报警发生时,我可以通过查找Trace查看报警信息。这时候就会用到我们刚刚建立的TraceID和 Metrics信息的索引。当然我们也会做一些定制化的排序等,排完序之后,大家就可以通过TraceID直接跳转到Trace系统来查看它的整个调用过程。
如图所示,因为一些安全问题,我隐藏了某些节点的信息。图中每一个方块其实都代表了一些节点,这些节点我在图片的上部分做了大概的说明。从图中我们可以看到,单次调用的时候整个链路会很复杂,人力基本不可能了解到清楚整个过程情况。所以我们可以通过指标查找到Trace信息,然后通过Trace系统定位到真正出现问题的位置。如果只是单纯的时间高的话,我就能知道哪个时间更高一些。
因为这里存储着N个系统上传的Span信息,如果我们想了解更多详细的Span信息,我也可以通过“全部Span”来查看。假如这是一个Trace信息,而我的问题是:它为什么耗时这么高?这时候只要点开更详细的Span,就能看到Span在系统或者是这次调用中各部分花费的时间,然后去查耗时最高的系统,了解原因,从而实现问题的轻松定位。
除了通过TraceID查找到整个Trace的详细信息之外,通过TraceID我们也可以查找到与它相关的所有日志。在图中大家可以看到一些关键字,像合并、WARN等,这些都可以关联起来。这就是我们在可观测性方面做的一些事情。
四、新的挑战
上文在介绍架构的时候有提到它的挑战,比如说:当我们的业务发展过程中指标量特别高,而处理能力无法满足时要怎么解决?
可能大家比较早的时候,发展基本上都是使用Python或者PHP之类的脚本型语言,那么见效比较快的优化形式,就是从脚本化的Python转变为Golang。这里不是说Python语言不好,只是我认为,Python可能并不适合大并发以及数据量特别大的处理场景,而是更适合做一些其他脚本或者是科学计算等。所以技术栈的切换是一个很好的优化点。
以我们的经验来看,把Python语言优化到Golang语言带来的性能提升基本在8倍到10倍左右。
然后是语言信息的存储系统。在这方面我们一直发展得不错。从我进入公司时的2000万,到现在已经发展到了6000万~7000万的水平,业务量翻了好几倍。大家如果对数据库有一定的了解就会明白,当一个数据库中的数据量达到了这个量级,那它的处理就会是一个非常艰难的过程。不仅每次查找索引的耗时高,从索引中找到真实数据花费的时间更高。
面对这样的情况要如何进行优化?
我们最早的系统采用PG的Ltree方式来实现指标的查找,每次查找可能会查到Ltree的某一级,并进行模糊匹配,匹配完成之后再把数据返回给用户。但这里有一个很大的优化点:Ltree的匹配或者说字符串的匹配会涉及到很多算法相关的内容,性能都不是特别好。我认为比较好的方式是:将抽象出来的模糊匹配的数据或者常用关键字转为tag。
比如我们是做旅游业务的,工作中会经常涉及到各种地名,像上海、北京等。如果用Ltree的方式,当我要查看各地的查找情况,我可能会写一个shanghai.status_code.500,然后找到它各个系统的内容;又或者找到Ltree的这一级,将它全部展开。
转换成tag的方式之后,比如我在系统中增加了一个数据维度的信息,我一般称之为city信息。这个city信息可能代表北京或上海,如果存在一个北京或上海和ID的映射关系,我就可以直接通过ID查到整个指标。这带来的性能提升基本上是数量级的,int类型的比较要比字符串的比较快很多。
当遇到数据量特别大的情况时,大家可能会想到两种处理方式。比较常用的方式是分库分表。但除了分库分表之外,我还想给大家提供一种新思路:PG或MySQL有一些类似于分区表的新特性。这里简单介绍一下我们使用PG或MySQL的分区表之后发生的变化。
在数据的处理过程中,这些数据在物理上都是分成块的,但我在使用时其实并不需要关注它分成物理块的方式以及具体的查找过程。只需要它的关键信息就能进行查找,这就是我们对于分区表的使用方式。经过我们的实践,使用分区表可以会让业务的实现变得更加简单,自己也不再需要维护大量的事务信息。所以,大家在出现性能问题时,可以适当考虑分区表的形式。
这部分介绍了许多我们业务量发展到一定程度后带来的问题。除了业务量发展带来的问题之外,在整个容器化的过程中,我们从不变的KVM固定主机变成了会经常变动的pod,而这一变动也带来了一些挑战。原有系统在面对不同的数据时,可能只需要添加一个配置,在固定的主机名上请求业务;变成了pod之后,谁也不知道哪个pod代表了什么。
那这个时候就需要通过建立pod IP,通过pod以及IP等同我们服务之间的映射关系进行事件驱动,以这样的方式将数据进行汇总和定时更新。这是我们从不变到变的过程中的解法,需要建立映射关系,一般会有一个服务类的模块帮助我们实现统一参与,在应对变化时将它变成一种普遍的解答方法。
五、未来规划
接下来简单给大家介绍一下我们对未来的规划。
我们目前使用到了K8s以及Prometheus,但它与我们之前的业务的结合并不完美,所以我们下一步计划对Prometheus等做更多的兼容,甚至是对某些功能和模块做替换。
文中有提到,在从不变到变化的过程中,用户的访问会产生大量不可控的SDK的指标或大数的指标。那个时候我们可能会考虑在整个pod中添加一个sidecar的容器,用sidecar的小容器对部分数据做提前过滤。当数据处理完成之后,再由server来实现统一的收集抓取。
后续我们也会更多地去关注可观测性的发展,帮助它更快实现定位、提高Trace的关联比例等。
以上就是我分享的全部内容,大家如果有什么想法,欢迎在评论区提出~
Q&A
Q1:对于指标的数据聚合来说,在超大系统规模下,做可观测性如何优化资源占用和时效?
A1:这个问题比较大,我们可以将它拆分成两个部分进行回答,一个是数据聚合,另一个是可观测性下资源和时效的均衡问题。
首先是数据聚合。文章中有提到,我们的数据量十分庞大,已经达到了几千万的指标量级。而数据聚合我们可以想到好几个思路,最容易想到的方式是做服务的拆分,也就是微服务化,这样一来,当某个服务出现问题时我们可以针对它做一些动态的扩缩容,实现节约资源或提高系统处理能力的目的。
还有一种思路是将数据聚合的时间提前或者延后,避免大量数据涌入。举个例子,用户可能会有一个记录监控来记录系统的访问情况,而我每次访问时都会记录一个指标,如果每次都直接把访问的记录情况扔给监控系统的存储,那么存储面临的QPS压力就会特别大。这一部分我们会提供一些预聚合服务,像statsd等,允许用户做一些定制化的聚合逻辑。把大量需要聚合的请求提前挡住,降低存储压力。而statsd或其他预聚合服务可以实现定制化开发,所以它的成本也会低一些。除了聚合提前以外,我们也可以对它做一些延后的聚合,大家可能会好奇,为什么做了聚合的提前之后还要做聚合的延后?这是它们因为针对的场景不同。如果我们将同样一份代码部署到线上,且分别提供了web端的展示服务以及 API的服务,而我们在日常查看服务访问时往往都是分开看的,只是偶尔需要进行总和计算或对比。因为偶尔的使用所以平时没必要花费大量开销来进行提前聚合,所以只需要在查看端,也就是Dashboard之类的界面将两个指标都查到之后进行对比或聚合。
接下来讲可观测性如何优化资源占用和实效。可观测性方面确实会需要很多资源,包括Trace、log以及监控指标等等。那要如何做到资源分配均衡呢?首先要想清楚可观测性的定位。如果我们的可观测性是要把所有的内容记录得大而全的话,那么优化资源基本上是不太可能实现。但如果我们的可观测性的定位是:用来发现问题和解决问题的这种系统,这时候就可以做一些优化手段了。
如果我们有6000多万的指标,当它全部关联到Trace上时就会带来的一些问题,我们没有这么大的处理资源来处理这些Trace信息。因此我们会挑出一些重点指标关联到Trace上。那我们的重点是哪些呢?这需要我们自己定义。比如报警指标,当我们把所有的报警指标都关联到了Trace后,从量级上看,可能会直接从十几万飙升到七千万的量级,这种提升基本上是百倍的。而这一类报警指标关联到Trace,可能会带来一些延后的问题。有可能报警已经发生了,然后Trace才关联到系统上,这时候报警已经过去,所以Trace其实没有采集到信息或者采集到的信息有误。针对这一部分,一些像exception或者是error之类的异常类的指标,我们要做到全量的定义采集。对于资源的平衡,我的建议是:考虑清楚定位,只需要管理一些关键信息和Trace,而不必把所有的指标都关联。
多提一句,如果是访问耗时类的指标,也没必要把每一个耗时的数据请求都记录下来,可能只需要记录Top 10或者是两个极值就能发现问题。这是对于资源占用的优化或均衡。
Q2:去哪儿网现有监控系统,是如何与容器云平台监控做对接和融合的?
A2:我们现在还是用Prometheus实现容器云平台的监控、数据采集和存储。因为Prometheus是从k8s或者是云诞生时就结合在一起的,它们肯定天然适配。目前我们和它兼容的方式是把Prometheus作为另外一个数据源,让现有的监控系统用与Prometheus兼容的API来查询数据。收集到数据后再进行绘图和展示,所以Prometheus只是作为一个数据源辅助融合。另外,对于告警模块,我们没有使用Prometheus的alertmanger,因为我们已经有一套比较成熟的报警通知系统了。Prometheus检测完报警之后,我们会自行开发、封装一个Prometheus告警通知模块,再通过我们的报警通知系统将报警发出去。
Q3:请问你们是基于什么设计的性能监控指标?是自动化干预还是手动调优?
A3:这个问题偏业务,我们中间件团队其实提供了一些通用的模块,每一个接口或者QPS、时延等都是由监控自动添加的,不需要用户来参与。而添加完之后,因为每个业务线对于性能的定义各不相同。我们不会直接干预用户的性能调优,更多的是提供一些指标、周期的同比、环比等形式,配合一些触发报警的条件来通知用户。当然,我们也提供了一些报警回调接口等,用户可以基于报警以及他的报警回调来触发他自己设置的调整动作,自动处理,解决报警问题。所以基本上我们提供是能力,而不是手动干预。
Q4:请问一些服务的自定义指标,需要应用开发者来实现metrics,这部分运维如何推动?
A4:如果只是单纯的运维的话,要推动开发来来实现metrics是比较困难的。从我的角度来讲,我的解决方案是,提供更多通用方式。像我们中间件团队会提供一些自动加链路的QPS延时的监控方式。对用户的侵入越小,推动过程中可能会受到的阻力也会越小。如果真的需要推动,我可能会需要通过自上而下的方式,或者是建立一个美好的未来给用户,让他对实现后的效果产生期待,这时候推动起来会比较顺利。
Q5:请老师再讲一下,可观测性里面,监控数据怎么和trace span关联的?
A5:我们的监控系统发展得比可观测性要早很多,所以它相对比较独立,指标之类的也都比较成熟。在做关联的过程中,我们其实是单独实现了一个服务。这个服务会读取到监控系统中配置了哪些报警,以及这些报警关联了哪些指标信息。知道报警关联的指标之后,我可以通过指标反查到具体的业务。通过服务可以把指标信息以及它需要做一些采样频率等推送给trace、Span的 Agent,由它来进行采集关联。关联之后,报警每次经过Span时就会被记录下来,同时也会记录报警指标的一些信息。记录完成之后,它会通过Kafka移到Flink处理,这个过程会建立指标和SpanID、traceID的关联关系。
这样一来,在报警发生时,我可以直接通过指标查找到Span ID和traceID,通过traceID找到所有log。
Q6:请问老师有没有做告警收敛,如何做告警收敛?
A6:告警收敛我们有做,但做的不是特别好。文章中在提到报警模块时,有提到报警抑制,这是一个比较简单的算法,在某块指标多次发生闪报、恢复时进行抑制。而告警收敛是,在上层的网络服务出现问题时,大量报警涌入,这时候我们会有一个小的窗口期来对它们做聚合,也就是进行报警收敛。将所有的、相关的报警收敛成一个电话,告诉用户:在哪个区间发生了多少报警、可能涉及到多少系统等,同时通过IM把更详细的信息发给用户,而不再单独通知所有人。这是我们做的一些告警收敛的相关工作,但还没能达到能够通过指标进行自动关联的程度。
获取本期PPT,请添加群秘微信号:dbachen
↓点这里可回看本期直播
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721