前言
微服务架构是目前各大互联网公司普遍采用的软件架构方式。在微服务架构中,系统被拆分为多个小的、相互独立的服务,这些服务运行在自己的进程中,可以独立的开发和部署。在业务快速变化时,微服务单一职责、自治的特点,使系统的边界更加清晰,提升了系统的可维护性;同时,简化了系统部署的复杂度,可以针对某个微服务单独升级和发布;在业务增长时,也可以方便的进行独立扩展。
微服务架构虽然带来了很多好处,但也带来了新的问题。在以往的单体应用中,排查问题往往通过查看日志定位错误信息和异常堆栈;但是在微服务架构中服务繁多,出现问题时的问题定位变得非常困难。另外,微服务往往通过组合已有的服务来创建新服务,一个服务的故障很可能会产生雪崩效应,导致整个系统的不可用。因此,如何监控微服务的运行状况、当出现异常时能快速给出报警,这给开发人员带来很大挑战。
本文将介绍我们基于Prometheus搭建微服务监控系统的一些实践经验,及爱奇艺号在微服务监控方面的一些探索和实践,从爱奇艺号的业务特点出发,结合现有的开发运维技术栈确定监控的对象和指标,并有针对性地自研了一些关键组件和服务,实现服务的全面监控和统一报警。
监控系统简介
监控的几种主要方式
在微服务架构中,不同维度有不同的监控方式。
- 健康检查。健康检查是对应用本身健康状况的监控,检查服务是否还正常存活。
- 日志。日志是排查问题的主要方式,日志可以提供丰富的信息用于定位和解决问题。
- 调用链监控。调用链监控可以完整的呈现出一次请求的全部信息,包括服务调用链路、所耗时间等。
- 指标监控。指标是一些基于时间序列的离散数据点,通过聚合和计算后能反映出一些重要指标的趋势。
在上述4中监控方式中,健康检查是云平台等基础设施提供的能力,日志则一般有单独的日志中心进行日志的采集、存储、计算和查询,调用链监控一般也有独立的解决方案进行服务调用的埋点、采集、计算和查询,本文主要讨论第4种监控方式。
微服务监控的技术选型
由于微服务架构自身的特点,使得传统的一些监控方案不再适用。在传统应用监控中,Zabbix是最常用的监控方案。Zabbix的优点在于成熟可靠、强大的社区支持、多年积累的经验和方案。但Zabbix的缺点也很明显,首先是使用难度高、学习曲线陡峭;其次,Zabbix的监控维度是主机,无法适用于微服务的云原生环境。
经过调研,我们最终采用了Prometheus。选择Prometheus的主要原因:
- 成熟的社区支撑。Prometheus是一个开源的监控软件,拥有活跃的社区,能够很好地与云原生环境搭配。
- 易于部署和运维。Prometheus核心只有一个二进制文件,没有其他的第三方依赖,部署运维均十分方便。
- 采用Pull模型,通过HTTP的Pull方式从各个监控目标拉取监控数据。Push模型一般通过Agent方式去采集信息并推送到收集器中,每个服务的Agent都需要配置监控数据项与监控服务端的信息,在大量服务时会加大运维难度;另外,采用Push模型,在流量高峰期间监控服务端会同时接收到大量请求和数据,会给监控服务端造成很大压力,严重时甚至服务不可用。
- 强大的数据模型。Prometheus采集到的监控数据均以指标的形式存在于内置的时序数据库中,除了基本的指标名称外,还支持自定义的标签。通过标签可以定义出丰富的维度,方便进行监控数据的聚合和计算。
- 强大的查询语言PromQL。通过PromQL可以实现对监控数据的查询、聚合、可视化、告警。
- 完善的生态。常见的操作系统、数据库、中间件、类库、编程语言,Prometheus都提供了接入方案,并且提供了Java/Golang/Ruby/Python等语言的客户端SDK,能够快速实现自定义的监控逻辑。
- 高性能。Prometheus单一实例即可处理数以百计的监控指标,每秒处理数十万的数据,在数据采集和查询方面有着优异的性能表现。
由于采集的数据有可能丢失,Prometheus并不适合对采集数据要求100%准确的场景。实际上,对于监控系统的场景,偶尔的数据丢失完全可以接受。
基于Prometheus的微服务监控方案
爱奇艺号业务特点
爱奇艺号是爱奇艺专注视频内容的创作、分发和变现的平台,承载了自媒体、网大、网剧、儿童、动漫、知识、网络综艺、纪录片、文学、轻小说、漫画等内容,是爱奇艺内容生态的重要一环。
爱奇艺号整体采用微服务架构,内部依据功能、领域等角度划分为不同的微服务,外部流量先经DNS、QLB、前置机、网关等层完成统一鉴权、负载均衡、限流等操作后路由到系统内部不同的微服务实例。系统内部微服务除专有的MySQL、Redis、MQ等资源外,共享服务注册/发现、配置中心等服务治理能力。
系统整体架构,如下图所示:
爱奇艺号服务于内容创作者,服务质量直接决定了创作者的使用体验,影响内容创作的热情,进而影响内容生态的健康,因此对服务质量有很高要求。同时,爱奇艺号作为前台业务,依赖公司许多的内部服务和中台服务,服务的稳定性直接影响了自身服务的质量。
基于爱奇艺号的业务特点,在搭建微服务监控系统时,重点关注自身服务接口和第三方服务接口的监控。
微服务监控系统概述
我们基于Prometheus搭建了适合自身业务特点的微服务监控系统。Prometheus已提供非常丰富的组件,同时我们也开发了部分组件,满足我们的监控需求。
微服务监控系统的整体结构,如下图所示:
- 使用Spring Boot Actuator和Micrometer采集服务的监控数据,并暴露给Prometheus拉取。
- 开发了第三方服务接口的监控数据采集工具。
- 开发了qae-monitor组件,采集服务运行时容器的监控数据。
- 开发了基于文件的动态服务发现,给Prometheus提供拉取目标。
- 开发了Alert proxy服务,实现了报警内容投递到统一报警平台。
- 使用Prometheus联邦集群模式部署,并使用Grafana用于监控数据展示。
服务的全面监控
监控系统一般采用分层的方式划分监控对象。在我们的监控系统中,主要关注以下几种类型的监控对象:
- 容器环境监控,主要指服务所处运行环境的一些监控数据。
- 应用服务监控,主要指服务本身的基础数据指标,提现服务自身的运行状况。
- 第三方接口监控,主要指调用其他外部服务接口的情况。
对于应用服务和第三方接口监控,我们常用的指标包括:响应时间、请求量QPS、成功率。
容器环境监控
微服务应用部署在爱奇艺内部的应用云平台(QAE)上。在云平台中,一台主机上会同时存在多个容器实例,采用主机监控的方式采集到的资源使用和性能特征实际上是主机的指标数据,而非运行的容器。Prometheus虽然支持使用cAdvisor进行容器监控,但cAdvisor需要安装在主机上,而QAE是一个公共平台,自行安装部署其他软件并不现实。好在QAE提供了开放的API,很好的解决了这一问题。
QAE平台自身内建了监控功能,包括容器级和应用级两个层级,除了可以在QAE平台通过页面查看,也支持通过HTTP接口暴露出监控数据,这就为我们进行统一的监控数据采集提供了可能。
我们开发了一个QAE容器监控数据采集的服务,qae-monitor。qae-monitor服务通过自定义Prometheus Collector的方式,实现对QAE监控数据的收集。服务定时调用QAE平台的HTTP接口抓取容器监控数据,并整理成Prometheus的数据格式。
qae-monitor本身也通过Micrometer向外暴露监控数据采集端点,Prometheus通过该端点抓取采集到的监控数据。
应用服务监控
基础监控数据主要是指应用服务实例的运行时状态、资源使用情况等度量指标。Micrometer默认提供了非常丰富的应用度量指标,只要接入了Micrometer就可以直接采集到这些数据,主要包括:
- 系统信息,包括运行时间、CPU使用率、系统负载等;
- 内存使用情况,包括堆内存使用情况和非堆内存使用情况;
- 线程使用情况,包括线程数、守护线程数、线程峰值等;
- 类加载信息;
- GC信息,包括GC次数、GC消耗时间等;
- HTTP请求情况,描述HTTP请求的性能指标,是非常重要的监控指标,在统计HTTP服务的QPS、响应时间和成功率时必不可少。
第三方接口监控
微服务架构中,可以通过调用和组合已有服务的方式创建新服务,第三方接口会直接影响到自身服务,因此第三方服务接口的调用情况同样值得关注。在如何采集第三方服务接口的监控数据上,主要有两种方案:
- 显式主动采集。在发生第三方接口调用的地方,主动进行监控数据的采集。或者直接硬编码,或者用注解、切面的形式,优点是方案简单,缺点是会对已有的业务代码造成侵入。
- 隐式组件采集。在使用的HTTP/RPC组件中增加埋点采集的逻辑,优点是业务代码不需修改,缺点是HTTP/RPC组件需要扩展和升级。
我们最终选择了第二种方案,主要原因是:首先我们的技术方案比较统一,都采用HTTP协议进行服务调用,且使用的HTTP客户端组件(fluent-hc)也是基于Okhttp3进行的二次封装,方便统一修改。其次,Micrometer支持通过SDK的方式自定义监控指标数据的采集,也提供了众多常用的组件埋点方案,Okhttp3即是其中之一,进一步简化了第三方接口的监控数据采集难度。
具体而言,Micrometer提供了OkHttpMetricsEventListener组件,用于收集Okhttp的监控数据。我们只需要在构建Okhttp实例的时候传入OkHttpMetricsEventListener实例即可;也可以传入一个EventListener.Factory实例,在工厂的创建方法中返回OkHttpMetricsEventListener实例。Okhttp在3.11.0的版本中才正式加入了EventListener功能,使用时需要注意Okhttp的版本。
通过第三方接口监控的维度,我们可以方便地将自身服务与所使用到的第三方服务关联起来,以统一的视图展示服务用到了哪些第三方服务接口、这些第三方服务接口的响应时间和成功率是多少。当服务出现异常时,对于定位问题有很大帮助;同时,一些内部的服务可能监控报警并不全面,第三方监控也能帮助他们提升服务质量。
基于文件的服务发现
Prometheus采用的Pull模型需要知道哪些是被监控的目标对象。在Prometheus中配置监控目标分为静态和动态两种,常用的包括静态文件配置、文件服务发现、Consul服务发现等。此外,Prometheus还支持DNS、微软Azure、亚马逊EC2、谷歌GCE、Kubernetes等多种服务发现方式。
静态配置的方式最为简单,但在实际生产环境中并不可取。容器每时每刻都可能进行着创建和销毁,不可能通过静态配置方式设置监控目标。我们最开始选择了Consul的服务发现,它引入了集中的注册中心,微服务启动时向注册中心注册服务实例,Prometheus便可以从注册中心查询到服务实例作为监控目标。不过,我们最终并没有采用Consul,主要原因有两点,一是微服务接入Consul需要涉及代码改动,虽然改动不大,但大量服务的接入仍有不小的成本;二是还需要再单独部署和维护一套Consul环境,带来新的维护成本。
Prometheus服务发现的原理很简单,通过第三方提供的接口,Prometheus查询到需要监控的目标列表,然后轮训监控目标获取监控数据。由于QAE是私有云平台,Prometheus无法直接提供支持,但基于以上原理,我们可以实现类似的服务发现机制。
我们开发了基于文件的服务发现prom-sd-qae。prom-sd-qae是一个独立程序,部署在Prometheus服务所在的机器。它定时通过QAE平台的HTTP接口抓取容器服务列表,按Prometheus要求的格式在本地磁盘生成JSON或YAML文件,其中定义了所有的监控目标列表。Prometheus定时从文件中读取最新的监控目标,并从中拉取监控数据。
这一方案在两次刷新监控目标之间,可能会发生监控目标的销毁和创建,此时存在短暂的过期监控目标;不过,该方案兼顾了服务发现的动态性与简便性,依然不失为一种简单有效的选择。
统一报警
Prometheus允许基于PromQL定义报警的触发条件,Prometheus周期性的对PromQL进行计算,当满足条件时就会向Alertmanager发送报警信息。
在配置报警规则时,我们将每个服务的报警规则定义在一个group下,每个group定义了多条报警规则,包括:响应时间报警、接口成功率报警、QPS报警、第三方接口报警等。这样的好处是以服务为维度将报警规则聚合在一起,查看和配置时更方便;另外,同一个报警规则下不同服务的报警阈值可能不同,这样也可以独立配置。下图是一个报警规则示例:
Alertmanager在接收到报警后,可以对报警进行分组、抑制、静默等额外处理,然后路由到不同的接收器。Alertmanager支持多种报警通知方式,除常用的邮件通知外,还支持钉钉、企业微信等方式,也支持通过webhook自定义通知方式。
爱奇艺的统一报警平台实现了报警话题、报警内容、报警渠道、报警订阅的统一处理,我们充分利用了统一报警平台,开发了Alert-proxy报警代理服务。Alertmanager通过webhook方式将报警发送到Alert-proxy,Alert-proxy再投递到统一报警平台,最终发送到最终热聊、邮件、短信等接收端。Alert-proxy会将报警投递到统一报警平台一个默认的报警话题Topic,也支持投递到其他的Topic上。可以为不同服务、不同报警级别单独设置Topic,实现更精确的通知触达和聚焦。
报警涵盖了服务HTTP接口、第三方HTTP接口,也包括了JVM和容器的状态,目前已基本满足需求。
写在最后
监控是一个老生常谈但又常谈常新的话题,与业务特点、技术栈、方案选型有很大关联,看待问题的角度不同最终的方案也不尽相同,到底什么样的方式是最合适的,这都是仁者见仁、智者见智,只有适合自己的才是最好的。
本文是现阶段微服务监控的一些实践总结,随着业务和技术的不断发展,未来还有许多方面需要不断地探索和改进,例如报警规则优化、自动化报表、系统智能化监控等,使监控更加全面、强大和智能,进一步提升服务质量和稳定性,助力业务快速发展。
原文链接:
https://mp.weixin.qq.com/s/u9DGTaHPORuUO5AaN5UgLw