Mercury:唯品会全链路应用监控系统解决方案详解(含 PPT)

唯品会   2016-08-09 16:51:12 发布
您的评价:
     
0.0
收藏     1收藏
文件夹
标签
(多个标签用逗号分隔)

姚捷,唯品会平台架构部高级架构师,加入唯品会前有超过 10 年的金融/保险互联网技术架构和团队管理经验,擅长以产品思维设计和构建系统。现专注于互联网基础架构,负责唯品会全链路监控/分析平台的开发,管理,推广和运维落地工作。对大数据体系,实时计算,微服务体系,消息系统有深入研究和实践。

我来自唯品会平台架构部,非常感谢高可用架构在上海组织了这么一场高逼格的活动。唯品会平台架构部是长期致力于基础应用架构建设与推广的部门,我们承担非常多的基础应用架构研发。

唯品会有三大特点,特卖 + 闪购 + 正品,在唯品会,峰值访问量非常大,这样的流量,使得唯品会平台架构部承担非常大的挑战,包括我今天分享的全链路监控系统。

上面是我们的新广告,周杰伦代言,傲娇品牌,只卖呆萌的价格。

今天给大家带来的是我们一个比较重要的产品 Mercury,它是一个大型互联网应用全链路监控系统解决方案。

大家可能了解,500 错误场景可能是大家在线上经常遇到的问题,等待很长时间,然后系统给你 500,让大家很抓狂。在部署全链路监控之前,大家可能需要使用比较古老的方式,跑到生产机上面去查问题,这个过程可能耗费大量的时间。

之前在唯品会,我们做了一个叫 Logview 的产品,它是一款基于 nginx access log 的监控平台。唯品会大量系统前置了一个代理服务器,Logview 的机制是通过解析 nginx access log,构建一个日志监控平台。它跑了蛮长时间,迄今还在线上稳定运行,它曾经是唯品会最重要的一个监控平台。

但随着业务规模的不断增加,基于 nginx access log 的监控会遇到不少问题。

  1. 没有办法从代码级别进行监控,通过它只能看到粗粒度的监控信息;

  2. 随着服务化的改造,大部分的流量,已经不经过 nginx 了;

  3. 它支持告警,但是没有办法通过告警跟踪到问题的 root cause。比如说 500 通常是某一个异常导致的,但是这里是看不到异常的,你还得到主机上、到 ELK 上查日志。它没有办法通过告警很快速的定位到我们线上的 root  cause,寻找问题的成本还是很高。

  4. 告警规则配置呆板,维护变更代价大。

  5. 没有办法跟踪业务之间的调用关系。唯品会在全网大概有上个业务域,主机现在接近几万台,这个体量是非常之大的,当一个系统一个域出了一个业务问题的时候,其实导致它产生问题的地方可能是很深层的。Logview 没有办法帮你跟踪到这些业务之间的关联关系,调用关系没有建立起来,出了问题你不知道最根本的原因在哪个域。

  6. 没有办法快速定位应用性能瓶颈。上面也提到,应用出了问题没有办法关联异常。

  7. 高峰值期间流量一上来就经常挂。

因为上面种种问题,我们痛定思痛,寻找新的方法。这个新的方向是什么?当时我们看了 Google Dapper ( http://research.google.com/pubs/pub36356.html )  的文章之后,认为基本上就是全链路监控系统。

Dapper 是帮大家构建一套比较完整的端到端的全链路日志采集的规范,是大家可以跟随的一个原则或者准则。它最关键的概念就是 span。

基于 Dapper 原理,有蛮多行业解决方案,比较有名的有淘宝 eagle eye,点评/携程的 CAT,微博的 Watchman,还有听云 Server 等。当时我们也对这些方案做了一些调研,但最终我们还是选择自建平台。

为什么呢?因为唯品会还算有钱,团队的能力也比较强,有蛮多资深的技术人员。除了有钱有之外,更多的是从业务上,从系统层面上去看我们为什么要自建平台。

第一, 系统复杂 。上千个业务域,同时又是异构系统,有很多系统还是 PHP。服务化体系内存在众多的异构系统的,我们要去监控这样的异构系统,显然不能直接用一些业界的方案。

第二, 海量数据 。唯品会峰值每分钟上亿日志量,一天达到上百亿的日志量,这是非常海量的数据。BAT 之外,对比其他公司,唯品会在这个数据量上面也是很高的。对这些海量日志,怎么样端到端对海量数据搜集、计算、存储、检索,这是一个很大的挑战。

第三,我们需要 自建服务化体系的监控 。我们有自建的服务化体系,这个服务化体系要怎么监控?我们的服务端其实需要一些定制的埋点和数据透传机制的,它没有办法说我直接打一个 log,这其实是每个公司有自己的服务化体系,它需要自己去埋点。

第四, 高度可治理 。这个非常重要,因为当你整个摊子铺大了之后,上千个域都接入后,怎么去管理?比如说大促的时候经常遇到我没数据了,为什么没有数据,整个体系是非常复杂的,我怎么样能够快速的知道这个业务为什么没有监控数据了,这就涉及到治理。

另外大促的时候我们要做一些动态的升降级,这些动态的升降级也是依赖比较强的治理能力,需要在生产环境去控制日志的产生。

第五, 快速接入,升级便捷 。因为我们的团队很大,开发人员很多,所以我们的产品要做到让业务用起来很方便,我们需要一些无侵入埋点,快速接入。

同时在我们很复杂的体系当中,需要考虑怎么快速的升级,我不知道大家有没有经历过在一个很庞大的体系当中升级客户端,不管是监控系统的客户端,还是其他的客户端都是很头疼的问题,特别是在业务线众多的大型公司。据说阿里的 dubbo/hsf 的升级其实是非常困难的,我们现在也遇到类似的问题,因此需要提前去考虑这些问题。

第六, 灵活的告警策略和高效告警 。我们需要一个多维度、多监督、多级别、多时效、同时支持告警收敛的告警引擎

第七,还有一个很重要的一点, 监控体系要与公司的很多体系无缝对接 ,我需要满足不同角色的需求,同时要与唯品会的发布、监控、问题跟踪平台无缝对接。

所以正是因为以上种种原因,我们决定自建平台。

先快速给大家介绍一下 Mercury。

  • Mercury 是 唯品会自主研发的应用性能监控和问题跟踪平台 

  • 基于客户端探针上报应用调用链相关日志

  • 基于流式计算和大数据存储和检索技术

  • 提供实时和准实时告警,并快速定位根源问题

  • 分布式应用调用链路跟踪

  • 提供快速有效的数据展现和分析平台

唯品会监控体系

我们的全链路应用监控体系在唯品会处于什么样的位置?

系统层监控我们主要还是 Zabbix,我们在上面做了一些封装,提供更好的交互。

业务层我们有一个产品叫 Telescope,所有的业务监控,包括 PV/UV、定单量、支付成功/失败率等重要业务数据,全部在这个产品上面可以看到。

在应用层,现在 ELK 用得比较多,另外一个产品 SKYHAWK,它其实是 APP 应用端的监控平台。

接下来是 Mercury,唯品会现在所有的应用,所有服务化的应用,所有 PHP/Web 应用,都是通过 Mercury 来做应用层的监控。

为唯品会的监控生态而设计

在说到一些技术之前,我还想强调一点,我们做产品并不是简单地做一个技术,简单地把很多东西搭在一起,我们更多的是要为整个技术生态去设计。我们的产品更多的是为满足整个唯品会的运营、技术运营的需要,为这个生态的需要而设计,所以会涉及到人、流程和技术。

人的话,无非就是开发、监控、运维、运营、管理人员,这些人都会通过我们平台,去察看他们想看的数据。我们的监控系统会发布流程,发布的时候我要监控整个发布的质量,监控流程、故障问题定位、故障问题修复、以及故障回顾,这些其实是整个监控系统需要控制的流程。

技术上,主要包括大数据采集、大数据计算、大数据的存储和分析,这些技术整个形成了我们的监控生态。

Mercury 核心价值

Mercury 的核心价值在于:

  1. 生产环境 端到端 的性能可视化;

  2. 展现一个应用的完整的拓扑关系,通过全链路监控系统,我们把整个链路的拓扑展现出来;

  3. 快速告警,并且定位根源的问题。

这三点我总结下来,是我们这个产品最核心的价值。

海量数据刚才我也提到了,大约如下:

  • 1万+ 应用服务器接入

  • 接入业务域 500+

  • 大促峰值每分钟处理日志 1亿+

  • 日均处理日志量 150亿+

  • 日均存储日志 5T+

  • 日均索引量 1T

这些数据都体现我们的产品,我们是在支撑一个非常海量的数据。

前面提到这么多的统计数据也好,监控流程也好,回头来看一下,Mercury 是怎么去支撑这样的海量数据的?我们的架构是怎么样的?我们可以深入到一些架构的细节,去看怎么去构建一个比较完整的全链路监控系统。

完整的全链路监控系统

一个比较完整的全链路监控系统,通常会包括几个部分。

第一, 数据埋点和采集 ,这个相当重要,其实说白了,数据是整个监控系统最核心的部分,必须有能力快速和正确和方便地采集日志,所以我们在数据埋点和采集上做了很多文章。

第二, 指标计算 。指标计算有好几种方式,一种我可以在客户端做一些计算,通过 agent 上报计算结果,然后到服务端再做加工。还有一种策略是完全在服务端的计算,客户端只做简单的数据采集,所有的指标计算全放在服务端。这二种架构其实都有不同的优缺点。

第三, 指标存储、查询、展现 ,这个也非常重要。算完后的指标放在哪里,这些指标怎么样快速的查询出来,指标怎么样很灵活的展现给我们的运营人员、开发人员。

第四, 调用链的存储、查询、展现 。因为我们是全链路监控系统,调用链怎么存、怎么查、怎么看,这个也是非常重要的。

第五, 告警、问题定位 ,因为你单纯只有查询远远不够,我们需要一个很快速很高效的告警,同时产生告警之后,我们需要很快速的去定位这个告警是什么原因导致的。

第六, 自监控 。我们的体系需要一个自监控的功能,假如我在大促的时候挂了,我需要很快的恢复我们的平台,自监控非常重要。

第七, 治理 。大促之前需要动态地调整日志采样率,或者对日志采集做一些功能降级,例如不采集缓存相关日志。

建设一个完整的全链路监控体系是需要考虑以上这几个过程。

全链路监控技术栈

技术栈,我们的体系有用了很多的技术,Spark 、 Open  TSDB  、HBase  、Elastic Search,Kafka,Flume,Python 等,整个技术栈相当之复杂。

系统架构

我们 Mercury 的架构是怎么样的呢,大家来看一下,大致来跟大家描述一下我们整个架构的走法。

(点击图片可以全屏缩放)

先说客户端,应用系统其实是依赖一个 client 这样的探针组件,它其实是一个非常非常高效的数据采集组件,对用户来说是完全透明的,你不需要去做任何的编程,基于字节码织入技术,只需要做一些简单的配置。那么客户端就能够很高效的采集相关日志数据。这些日志是基于一个标准格式的,因此,我们的接入是非常高效的,你不需要关心我采集日志的格式是什么。很多其它的监测系统,比如像 CAT,可能它需要手动埋点,这个时候你可能需要非常关注它的埋点格式是什么。我们是不需要关心埋点格式的。

我们的日志数据是不会直接传到后端的,它首先落地在客户端的磁盘上面。

接着我们有一个 flume agent,基于 Flume,我们做了一个插件,这个插件会非常高效地采集我们客户端的日志。

当我们搜集到了日志之后,它就很高效地把日志推到 Kafka 集群里面。我们要保证它的日志是非常精简的,因为唯品会有多个 IDC 机房,跨机房低带宽占用是非常重要的。这个上面我们做了一些基于 avro 的数据序列化的优化,序列化之后带宽基本上降了一倍左右。 我们没有用压缩,因为压缩会导致客户端的消耗比较大。

当这些数据来到 Kafka 集群,后面有几个集群,首先第一个是 Spark 集群,这是一个非常重要的数据流式计算的集群,我们通过 Spark 做指标计算,让所有的日志都通过 Spark 很快速的算下来的,基本上延时在两分钟左右。考虑到端到端数据上报的时效性,其中只有一到两分钟是计算的延时。

指标算完之后,一条线到 OpenTSDB,如果做过监控的同学对这个应该是蛮熟的,相对来说也是比较成熟,比较简单的基于 HBase 的一个时间序列的框架。

另外一条路,通过 flume 写到 HBase 里,因为大家看到其实我们有调用链,那我们要对调用链做很快速的检索,我们以前是直接把调用链日志写到 HBase 里,但是有一个头疼的问题是它的写入很快,通过 rowkey 一键查询也很快,但是如果说你需要做一些比较复杂的查询,HBase 是不支持二级索引查询的,你必须自己在它之上构建二级索引。

原来我们直接设计了一个索引表,但在海量数据下,查询起来非常之慢,所以我们后来用 ElasticSearch,首先日志数据送到 Kafka 后会被消费两次,一条路是直接送到 ElasticSearch 里建索引。另外一条路是调用链日志直接落地到 HBase。

查询的时候,首先到 ES 里根据查询条件定位到 trace id,接着拿着 trace ID 到HBase 里一键把所有的端到端的请求都拉出来,所以通过一个 Trace ID 就能一下子把作为的调用链全拉出来,因此这个设计是非常轻巧的。

整个体系还有很重要的是两个告警引擎,它分为实时秒级告警和一个指标告警。实时秒级告警更多的是对严重问题的告警,比如大量的服务超时,或者服务熔断。这个时候我们整个体系会上报一些事件,会有一些熔断事件,我们会预埋在我们的服务框架里面,一旦遇到这种问题的时会立刻上报。我们的上一级告警框架,会立刻检测到这个 alarm,然后做实时告警。

同时我们会对这些异常做一些收敛,假设瞬间好几百台或者上千台的主机都产生了异常,那整个异常都会一下子上报服务端,这个时候你会在服务端产生大量的报警,所以我们秒级告警会做一些收敛,会做一个特征值的设定,我会在三十秒或者十秒,这个时间窗口你可以定制,比如我十秒的窗口,我先对一些有特制的异常做一些签名,缓存到内存里面,如果在一段时间之内,上报的异常匹配了特征值,则不会做重复告警。

当这个时间窗口过去之后,这个特征值就会失效,下一次再进来的时候,我们会重新构建这个特征值。我们用这样的一个方法实现告警收敛。

下面这个是指标告警,它是一个周期性的告警任务,它会根据我的告警规则设定,定时扫描指标数据库做一个周期性的告警。比如连续三分钟,响应时间大于 30。毫秒就告警,类似这样的告警我们通过周期性的告警规则来设定。

在后面还有 Dashboard 和告警跟踪系统。上面也提到,我们是为整个唯品会的监控生态而设计,这个告警跟踪系统会把我们所有的告警聚合到一起。告警看板的作用是说我立刻能在大盘上看到所有的告警,但是这些问题是不是已经处理完了,我们是需要一个比较好的跟踪平台去跟踪的,所以我们设计了这样的跟踪系统。它是长时间运行的,开发人员每天会上去看。比如我昨天有些告警,这些告警有没有处理完,都会在这个系统当中呈现。

调用链

整个体系还是蛮庞大的,接下来跟大家说一下我们整个体系最核心的调用链。

什么是调用链,大家可能第一次接触全链路系统,可能对调用链的概念并不一定熟。你可以认为它是一个串联端到端请求的链路,在这个链路上面我们会看到非常丰富的信息。

调用参数

我们在调用链上面我们去关联说这个调用的参数是什么,我的出参入参可以在这个调用链上面。

业务关键字

这里我需要稍微解释一下什么叫业务关键字。很多时候想象一下这样一个场景,有一个用户下单支付失败了,这个时候我们怎么样知道,为什么这个支付失败了,到底是支付整个链路上面哪里出了问题,这个是需要我们还原现场。

如果有一个系统它能够帮你还原这次调用,到底哪一步出了问题,所以你需要去反查,你需要拿着一些业务上面预先埋的点,比如说定单号或者用户ID,通过这些信息我能够反查到当时用户下单操作的那次调用,再去看那次调用到底哪里出了问题。所以业务关键字是非常重要的。

我们要求核心的业务系统要通过我们的 API 去埋它的关键字,把它的用户信息、订单号,或者说我们一些很关键的业务字段,都预先的埋到我们的调用链上面,出了问题之后就可以反查。

调用耗时

这个应该很清楚。事件,因为有的时候我们要调用的时候,我们可以把一些这次调用产生的事件关联在一起,我们也可以通过事件去反查当时的调用。异常日志这个信息非常重要,因为当你出了 500 的时候,一定会有异常产生,这个时候我就要通过这次调用去关联这个异常,然后通过这个异常信息我就知道产生问题的是什么。

调用结果

调用结果,成功失败。

调用链总结

所以整个调用链的信息还是蛮丰富的,那么调用链最核心的是什么?核心的是 每个请求都会生成一个全局唯一的 TraceID 和 SpanID ,它都会端到端的透传到上下游所有的节点。每一个请求 TraceID 都要透传上去,从最上游的移动端一直到最下面的数据库, 通过这个 TraceID 我们将不同系统的孤立的调用日志和异常日志串联在一起 ,同时 通过 SpanID 可以表达节点的父子关系

下面我为大家来说一说我们每一个组件当中的一些非常重要的设计原则,

1. 客户端 - 举重若轻

首先从客户端讲起,客户端我把它形容为举重若轻,你需要有一个很轻量级的客户端,但它同时又能够承担很大的流量,怎么能够做到这一点?我觉得很关键有以下几点需要去注意。

高效率

高效率更多的是对开发人员来说,我要让他们很快能够用我们的东西。我们现在是支持 Java 和 PHP,高效率主要体现在:

  • Java 采用 AOP 埋点,不管是一个 Java 调用、其他的调用还是服务化调用,我们都会拦截所有的请求,植入我们的逻辑,包括透传。

  • PHP 也有自己的框架,所以在 PHP 里面也进行预埋,对 PHP 的应用逻辑而言也是透明的。

  • 日志格式是标准的,要不然日志框架没有办法很快的去监控我们的系统,用户是没有感知的。

高性能