作者:山猎,阿里云解决方案架构师
前言
随着分布式技术的发展与演进,微服务技术成为了大型分布式IT架构的必然选择。从本质上来讲,微服务是一种架构风格,将一个大型的系统拆分为多个拥有独立生命周期的应用,应用之间采用轻量级的通信机制进行通信。这些应用都是围绕具体业务进行构建,可以独立部署、独立迭代,也可能根据业务负载独立的水平扩展。微服务思想以及相关的技术为IT架构的发展带来了一系列深刻的变革。微服务技术让IT系统变得更敏捷、更健壮、更高性能的同时,也给带来了架构复杂度的提升,给应用监控带来了前所未有的挑战。在微服务时代,由于服务的拆分,单个用户请求会经过多个微服务应用,形成复杂的调用链路,使传统的依赖于单机业务日志的监控手段无从下手,这就需要建立全新的监控机制,帮助开发者全面洞察系统运行状态,并在系统遇到异常的时候快速的定位和解决问题。
什么是全链路监控?
在分布式微服务架构中,系统为了接收并处理一个前端用户请求,需要让多个微服务应用协同工作,其中的每一个微服务应用都可以用不同的编程语言构建,由不同的团队开发,并可以通过多个对等的应用实例实现水平扩展,甚至分布在横跨多个数据中心的数千台服务器上。单个用户请求会引发不同应用之间产生一串顺序性的调用关系,链路的概念就此诞生。
随着业务规模的增长,不但来自于前端用户的请求频度会增加,链路也变得更长,这也代表着应用之间的调用关系变得越来越复杂。为了提升微服务系统在复杂链路下的健壮性和稳定性,有3个关键诉求需要我们去解决:**1.如何梳理整套系统的调用关系,并评判应用上下游依赖的合理性?2.如何了解每一个应用的性能指标,并对系统容量进行合理的规划?3.当系统出现故障或异常的时候,如何第一时间发现问题、定位问题、解决问题?**这3个关键诉求的核心挑战,都来源于应用之间复杂的链路。如果有一套成熟易用的机制,对每一条链路的行为进行记录,并进行深入的分析,提取出有价值的参考数据,就能让这些难题迎刃而解,这个重要的机制就是全链路监控。
标准与规范
十年前,Google成为了分布式系统链路追踪服务的先行者,并通过《Dapper,aLarge-ScaleDistributedSystemsTracingInfrastructure》这篇著名论文阐述了如何在超大规模系统上建设低损耗(lowoverhead)、应用级透明(application-leveltransparency)、大范围部署(ubiquitousdeployment)的链路追踪服务。
Dapper阐述了对分布式系统进行链路追踪的技术细节,包括数据表示、埋点、传递、收集、存储与展示等方面,并提出了跟踪树、Span、Trace、Annotation等重要概念,为全链路监控提供了理论指导。
在Dapper的启发下,业界诞生了很多用于分布式链路追踪的开源组件,为了保持对链路中每一个环节的记录与匹配,不仅需要在应用内部对跟踪信息进行传递,还需要让跟踪信息跨越不同的应用以及不同的分布式组件。这需要制定一套统一的标准,让微服务体系中的所有应用遵循这套标准来实现跟踪信息的描述和传递,这套标准就是OpenTracing。OpenTracing抽象出一套与编程语言以及业务逻辑无关的接口,对链路追踪领域各类元素的统一管理,从而实现完整的全链路监控。本文不会深入介绍Dapper和OpenTracing的原理以及技术细节。我们只需要知道,优秀的全链路监控组件会尽可能的遵循OpenTracing标准,以获得更好的通用性以及扩展性。
可选方案
全链路监控组件如何获得链路相关的信息呢?最简单的方式是让开发者在业务代码中手工埋点,生成符合OpenTracing标准的链路信息,并汇入全链路监控组件。但手工埋点的方式要求开发者主动配合,并在业务代码中嵌入大量非业务逻辑。这样的方式是极为脆弱的,开发者稍有疏忽就会导致链路信息丢失,甚至影响到正常的业务逻辑。所以非手工埋点的自动链路信息采集,成为了业界的主流,其中包括两种实现方式:**1.SDK方式:通过引入链路追踪SDK自动生成链路数据,并自动上报。对于底层框架没有公开API的情况,监控逻辑的注入会比较复杂,有可能需要开发者针对具体的底层框架预先做好适配工作。2.探针方式:探针方式不需要在代码编译前引入SDK,而是在应用运行的过程中,通过一个Agent动态的拦截底层框架的行为,从而自动注入监控逻辑**。像Java这样的编程语言可以通过字节码增强技术实现探针方式的链路信息采集。这是一种最开发者最友好的方式,不需要任何代码层面的改动,但并不是每一种编程语言都能提供探针机制,因此SDK方式也被很多全链路监控组件采用。
不管是SDK方式还是探针方式,非手工埋点形式的链路信息采集都依赖于链路追踪组件对于底层框架的识别。这些底层框架包含的领域非常广,其中包含应用对外提供服务所需要的框架,应用进程内部的通讯框架,应用之间相互访问所需要的框架,应用访问外部系统所需要的框架等等。比如在Java体系中,用于提供HTTP服务的Tomcat、Jetty,用于进程内部通讯的RxJava,用于微服务应用之间相互调用的Feign,用于访问外部系统的MyBatis、MySQLJDBC、HTTPClient,都属于这个范畴。对于多种编程语言以及种类繁多的底层框架的适配,是一项浩大的工程,一个全链路监控方案能够适配的底层框架越多,它的能力就越强大。
基础链路信息收集上来之后,需要进行统一存储,并对数据进行清洗与聚合,根据应用响应时间、请求数、错误率等指标进行下钻分析,并按应用、接口、链路、事务等多个维度进行展示,这也是一项非常复杂的工作。因此,全链路监控方案的技术门槛是非常高的,在开源的全链路监控产品中,真正遵循OpenTracing标准,又够被广泛认可和使用的产品非常少,其中通过SDK方式接入的产品以Jaeger为代表,通过探针方式接入的产品以Skywalking为代表。
最轻松的方案
开源的全链路监控方案能帮助开发者更深入的理解全链路监控的思想、原理以技术细节,但在在生产环境大规模使用开源方案,还是会给开发者带来很大的挑战:1.维护工作复杂:除了客户端的SDK和探针外,一套全链路监控方案在服务端有计算组件、存储组件、展示组件,都需要单独进行维护。以Jaeger为例,仅在数据存储方面需要维护一套独立的Elasticsearch集群,需要投入很大的工作量。2.功能简单:对主流底层框架的全面支持,丰富的UI界面,完善的诊断机制和报警机制,这些都是一套优秀的全链路监控方案所必备的特质。开源方案在这些方面很难做到尽善尽美。3.缺少高可用保障:开源全链路监控方案并没有完整的高可用机制,当某个组件出现故障,比如服务器宕机的时候,无法自动恢复,需要人工介入进行解决,在这个过程中正常的监控会受到影响。4.无法支撑大规模场景:当接入的应用数量达到上千个之后,开源全链路监控方案会暴露出各种性能问题,需要开发者修改源代码进行针对性的优化。5.影响正常业务:如果SDK/探针存在设计上的缺陷,有可能导致应用出现不可预知的故障。这种情况极为罕见,但一旦发生,后果会非常严重,这种情况下一般也只能等待开源社区将问题修复后才能恢复使用。之所以在生产环境使用开源全链路监控方案存在这么大挑战,是因为这些方案本身缺乏大规模实际业务场景的验证。对于技术实力非常强的互联网企业,会有专门的技术团队负责全链路监控项目,在使用开源产品的过程中如果遇到任何问题,都可以通过自身的技术实力进行修复和弥补。但对于绝大多数使用者而言,这些挑战所带来的都是漫长而痛苦的体验。有没有一套经历过大规模实际业务场景验证,又简单易用的全链路监控产品呢?在云计算时代这个问题的答案是肯定的,阿里云ARMS就能满足这个要求,代表着业界在全链路监控以及应用性能管理领域的最高水平。
应用实时监控服务ARMS(ApplicationReal-TimeMonitoringService)起源于阿里巴巴内部的EagleEye(鹰眼)系统,已经经历了近10年的沉淀和演进。鹰眼系统同时将基础设施层、分布式应用层、业务逻辑层与客户端层进行了全链路跟踪,每天对万亿级别的分布式调用进行分析,对底层的流计算、多维时序指标与事件存储体系等进行了大量优化,同时引入了时序检测、根因分析、业务链路特征等技术,将问题发现与定位由被动转为主动。在ARMS的理念中,对全链路监控的理解已经超出了一般意义上APM(应用性能管理的范畴),而是把“可观测性”作为产品的最重要使命。可观测性是一切自动化决策的基础,求最终目的是为一个复杂分布式系统所发生的一切给出合理解释。正是因为经历了大规模生产环境的长期验证,才使ARMS能够在易用性、功能性、稳定性方面做到极致,以开源领域最活跃的全链路监控项目Skywalking为例,我们可以从多个维度对两个产品进行对比:
接入来,我们就通过阿里云ARMS,开启轻松玩转全链路监控之旅。
应用接入
ARMS采用无代码侵入探针接入方式,对于应用接入只需要非常简单的几个步骤,以Java应用为例:1.开通ARMS服务:登录阿里云控制台后,打开ARMS产品主页,按照提示开通“应用实时监控服务试用版”,开通服务后会获得15天的免费产品试用。2.下载探针(Agent):在公网环境以及VPC内,都提供了探针的下载链接,可以直接参考ARMS台的提示进行操作。3.修改应用的启动命令:通过-javaagent挂载探针,并配置对应的licenseKey以及应用名。比如我们启动SpringBoot应用为例,启动命令为java-javaagent:/{user.workspace}/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar-Darms.licenseKey={LicenseKey}-Darms.appName={AppName}-jardemoApp.jar其中,-javaagent后面的路径是探针文件所在的路径,arms.licenseKey可以从ARMS的控制台获得,appName代表应用的名字。在微服务应用中,一个应用可以拥有多个对等的应用实例,这些对等的应用实例接入ARMS的时候,使用同样的应用名,这样ARMS可以把这个应用的多个实例放到一个分组中进行统一管理。修改完应用启动命令后,对应用进行重启,就能够成功接入ARMS。如果在1分钟后,ARMS控制台的应用列表能够看到新的应用,就代表接入成功。当然,对于Tomcat等通过操作系统脚本启动的应用,不能直接修改应用启动命令来挂载ARMS探针,这个时候只要对启动脚本进行修改即可,以Tomcat为例,我们在setenv.sh中加入如下配置:JAVA_OPTS=$JAVA_OPTS-javaagent:/{user.workspace}/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar-Darms.licenseKey={LicenseKey}-Darms.appName={AppName}更多的Jetty等更多通过Web容器启动的应用可以参考ARMS的帮助文档。对于部署在阿里云EDAS或者容器服务Kubernetes的应用,接入工作会更加的简单,ARMS已经和这些产品进行了集成,使用者都不需要下载ARMS的探针到应用所在的节点,可以直接在控制台进行一键式的批量操作。特别重要的一点是,ARMS支持混合云模式,所以并不要求接入的应用一定要部署在阿里云,不管应用部署在线下IDC还是其他的云,都可以统一接入ARMS。当然,需要确保在混合云模式下,应用与ARMS服务端之间的网络是畅通的。
核心实践一:了解你的系统
应用接入后,可以通过ARMS获得哪些重要的信息,从而帮助使用者更好的了解整个系统呢?我们可以从这几个方面入手:
应用列表和全局拓扑
在应用列表视图,我们能看到每一个应用的健康度以及最近10分钟对外服务的响应情况。如果应用的状态列亮红灯,代表此应用运行不健康,我们可以继续点击红灯查看ARMS此应用生成的诊断报告,以进一步分析应用不健康的原因。
点击应用列表右上角的全局拓扑按钮,能够通过可视化界面观察所有接入ARMS应用的拓扑结构,这个界面清晰的展示了所有应用的上下游组件以及相应的调用关系,能够帮助使用者从全局角度深入理解整个微服务系统。
理想的微服务应用只有不同层级之间的单向依赖,这种依赖的原则是高层应用依赖于低层应用。同层应用之间的相互依赖,或者低层应用依赖于高层应用都是违背这个原则的。假设我们在全局拓扑视图里面,找到了环状依赖关系,说明微服务应用之间的依赖关系不清晰,可以考虑对应用的层次结构进行重构。
应用总览
从应用列表进入应用总览页,首先呈现给使用者的是概览分析视图,在这个视图中,我们能够查询应用在指定时间的关键指标。右上角的时间段是一个非常重要的选项,使用者可以根据需要来修改此应用关键指标的采集时间范围(默认15分钟)。在ARMS的很多视图里面,都提供了这个选项,来帮助使用者查看指定时间范围的监控信息。
应用在选定时间内的总请求量、平均响应时间、错误数、实时实例数、FullGC次数、慢SQL次数、异常次数和慢调用次数,以及这些指标和上一天的环比、上周的同比升降幅度等信息,都能够在这个视图体现。我们可以重点