Prometheus 监控方案简述


最近两个月,在业务上做了很多监控相关的工作,最终选用了以 Prometheus 为中心的监控解决方案。其实真正的工作也没做多少,因为腾讯云最近灰度上线了 Prometheus 监控服务,我们基本是在使用腾讯云的工作成果。而我主要的工作内容,一是广泛调研监控工具,判断是否适合当下业务场景,二是负责实际落地。

本文写得比较零散,总体上说明了三件事情:

  1. 市面上可选用的监控方案有什么
  2. 以 Prometheus 为主的监控方案是怎样的
  3. 如何使用腾讯云 Prometheus 服务进行监控

概念 & 名词

APM

APM (Application Performance Management)应用性能管理,简单理解就是监控。

APM 是项系统工程,包括监控数据的收集存储处理告警,通常也包含可视化展示。监控的内容同样包含方方面面,从整体来讲包含基础设施监控(比如云容器、机房硬件整体表现)和业务监控(比如接口调用次数、JVM 表现)。

举一个切实的例子:比如我想知道,最近一周哪个接口调用的次数最多。我们可以拆分成以下问题:

  1. 数据收集:所有的接口每被调用一次,就要各自计数一次
  2. 数据存储:接口调用次数保存在哪里
  3. 数据处理:截取一周的时间,对接口调用次数进行倒序排序
  4. 可视化展示:以图表的形式,展示在前端
  5. (也许)告警:某个接口在一周内调用了 100 亿次,需要向管理员发送邮件/短信警告

市面上的 APM 工具(解决方案)主要有以下几种:

  • ELK

    Elasticsearch + Logstash + Kibana,这三个开源项目的首字母缩写。这三个项目各自的作用是带搜索功能的数据库采集数据,转换数据可视化,加起来是一整套 APM 解决方案。具体可以参考官网《什么是 ELK Stack?》。

    这一整套功能很全,但是也比较重,它基于日志的方式存储和处理数据,存储量很大,性能消耗也大(但同时功能也是最强的)

  • Skywalking

    这是一个国人做的项目,是 Apache 的顶级毕业项目。

    它的核心功能是分布式服务追踪,可以查出一个请求,从哪里被调用,中转了哪些微服务,使用了哪里的 Redis、MySql、MongoDB,以及最重要的:分别耗费了多长时间。它在分布式架构中表现非常卓越,通过它可以知道到底是哪里性能瓶颈大(比如某条 sql 数据库查询时间很长),但是在单体应用中作用不大。它支持 WebFlux,但是支持得比较一般。

    SkyWalking 不负责数据的存储,因此需要单独找一套数据库,比如 H2、MySql 等,比较推荐的是 Elasticsearch。

    SkyWalking 自己做了可视化,在分布式追踪上的效果展示还可以。

    SkyWalking 可以不用编码,引入一个 jar 包,通过字节码增强的方式实现每个接口的监控,但是会有一定的性能消耗(官方人员描述是 5%,网上描述是 10%,此外在高并发场景下会更多)

  • Pinpoint、CAT、Zipkin

    这三个跟 SkyWalking 的核心功能都差不多,都是做分布式追踪监控。Pinpoint 是韩国团队做的,小团队,开源,仍在更新。CAT 是美团系做的,功能更综合一些,研发挺慢的。Zipkin 是 Twitter 做的,开源,轻量。

    这三个工具我都没有实操过,具体表现不太清楚,应该跟 Skywalking 差距不大。

  • Prometheus + Grafana

    Prometheus 是 CNCF(云原生计算基金会)毕业的第二个项目(第一个是 Kubernetes)。它对云原生的支持度是最好的,是 Kubernetes 的官方推荐工具,两者做了很多适配,并且有大量的文章。

    在云容器监控上,应该是唯一的推荐选择,在业务代码监控上,本来表现一般,但是由于开源社区比较给力,因此适配得还不错。

    我们选用这一套来进行监控,一方面是腾讯云做了集成(因此我们的开发工作量是最小的),另一方面是它的性能表现理论上是最好的。具体内容下面会说。

    有关 APM 选型,还推荐阅读这篇文章《Prometheus监控告警——总结与思考》,很有启发性。

Prometheus

Prometheus 是云原生环境中最好的 APM 监控工具,有各种天时地利的因素,使它成为云原生和 Kubernetes 监控的近乎唯一选择。

Prometheus 包括数据采集、存储、处理、告警、可视化等全部要素,但是具体实现跟其他 APM 工具几乎完全不同,下面简单介绍一下 Prometheus 的工作逻辑。(以下的介绍都是很粗浅的,只是描述工作原理,跟实际架构还差很远)


数据结构

简单地理解 Prometheus,它是一个时序数据库,按照时间收集了很多 metric(指标)。

metric 可以理解为带标签的 key-value 键值对,它形如 key{label1="...", label2="..."} => 100,也就是说 Prometheus 按时间按类型收集了很多值,排列起来最终就是监控数据。具体的格式可参照下图(图片来源:《剖析Prometheus的内部存储机制》):

Prometheus数据结构

这种 metric 数据结构,占存储空间很小,性能很高,兼容适配也很简单,相比其他 APM 工具根据日志进行解析要优越很多。


数据采集

Prometheus 收集数据基于 pull 模式,监控数据并不是主动发送到 Prometheus 服务器中,而是给 Prometheus 提供一个接口,Prometheus 会自己去调接口收集数据,由它自己控制数据采样速度(最快应该是 1s 采样一次)。

这样做有特别多的好处,第一是开发者很容易在自己的程序中适配,只要提供一个符合约定格式的接口,就可以供 Prometheus 去进行监控,跨语言、跨平台、开发还迅速。第二是由于数据结构简单,采样可以很快,其他 APM 可能受限于性能,一分钟收集一次,而 Prometheus 最快可以 1s 收集一次,精度可以很高。


数据处理

Prometheus 有自己的语言,叫做 PromQL,用于查询监控数据。

PromQL 最简单的使用,就是形如上述数据结构的样子,输入 key 查找这一类数据,或者输入 key{label1=”…”} 查找某一类数据中的某类标签。下图就是在查找 JVM 内存的使用情况,只输入了 jvm_memory_used_bytes。

Prometheus自带的可视化

PromQL 可以非常复杂,因为内部的数据结构实际上就是 key-value 的键值对,而 value 是 64 位的浮点数,本质上只是数字而已,因此可以进行所有数值运算。PromQL 支持所有数值运算(包括微分、积分、矩阵等),同时内置很多函数。


数据可视化

Prometheus 自己做了可视化界面,上图查询 jvm_memory_used_bytes 的图片,就是 Prometheus 的前端界面。

但是这个界面需要用户自己输入 PromQL,不能保存语句,视图比较单一,而且不符合恶俗的 RGB 审美,大家一般选用另一种可视化工具 Grafana 来配合使用。

Grafana

Grafana 是一个可视化工具,它不负责任何数据的来源、存储、收集等等,它只是一个可视化的前端界面。Grafana 支持很多数据源,除了 Prometheus 之外,还支持 Elasticsearch、Zipkin、Loki 等等,甚至是 MySQL。

Grafana 的前身是 Kibana,这段历史还挺有趣的,推荐阅读《Grafana 与 Kibana 有什么区别,Kibana 能做的事情 Grafana 是否也都能做?》。

大家选用 Prometheus 基本都使用 Grafana 作为前端可视化工具。使用 Grafana 的好处,第一是符合 RGB 赛博朋克审美,第二是它能存储 PromQL 语句,也就是说配置一次之后,以后就不需要再写语句了。Grafana 的另一个优势在于,配置语句是可以拷贝出来的,去网上找一找,就能找到相关的模板,自己一句 PromQL 都不用写。

Grafana 有两个基本的概念,分别是 dashboard 和 panel,dashboard 是由 panel 组成的。以 Prometheus 为例,一条 PromQL 对应一个 panel,也就是一个图表,一堆图表加在一起就是一个完整的 dashboard。例如下图就是一个 dashboard,里面的每一块就是一个 panel。

Grafana_dashboard

Micrometer

上文写 Prometheus 时说到,Prometheus 基于 pull 模式,服务只需要提供一个接口,让 Prometheus 去调用,那么 Prometheus 就可以对这个服务进行监控。Micrometer 就是在 Spring Boot 框架下,给 Prometheus 提供接口的工具。

Micrometer 是一个专门用来进行 指标收集 的工具,实际上 Spring Boot 2 之后的 actuator 就是使用 Micrometer 来进行统计数据和发布数据到监控系统的。但是默认的 micrometer 并不是 Prometheus 的格式的,我们需要再主动引一个 maven 包:

1
2
3
4
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

大致的逻辑是这样的:Micrometer 收集指标数据,并通过 actuator 主动暴露一个接口,让 Prometheus 去调用,获取那些收集的指标数据。

Micrometer 默认会收集 JVM 相关的一些数据,业务数据需要自己写代码,比如我想统计 A 接口被调用了多少次,那么每次进入 A 接口的时候,就使用 Micrometer 计数一次。通常使用 Micrometer 是配合 Spring AOP 来使用的,做一个全局切面,统计所有接口的情况。

这是调用接口,获取到 Micrometer 收集的部分真实数据:

1
2
3
4
5
6
7
8
9
10
jvm_buffer_count_buffers{application="live-server",id="direct",} 3.0
jvm_buffer_count_buffers{application="live-server",id="mapped",} 0.0
jvm_memory_committed_bytes{application="live-server",area="nonheap",id="Compressed Class Space",} 1.0616832E7
jvm_memory_committed_bytes{application="live-server",area="nonheap",id="Code Cache",} 1.9857408E7
jvm_memory_committed_bytes{application="live-server",area="nonheap",id="Metaspace",} 7.794688E7
jvm_memory_committed_bytes{application="live-server",area="heap",id="PS Eden Space",} 3.3292288E8
jvm_memory_committed_bytes{application="live-server",area="heap",id="PS Old Gen",} 2.4379392E8
jvm_memory_committed_bytes{application="live-server",area="heap",id="PS Survivor Space",} 2.359296E7
http_server_requests_seconds_count{application="live-server",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/v3/api-docs/swagger-config",} 2.0
http_server_requests_seconds_sum{application="live-server",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/v3/api-docs/swagger-config",} 0.0825684

使用 Micrometer 是需要理解一些概念的,这里不具体写了,推荐两篇写的很好的文章:《SpringBoot2.0整合Prometheus Grafana》《给你的 Spring Boot 做埋点监控》,前者是总体使用思路,后者是 Micrometer 的使用细节。


监控解决方案

我们目前的监控主要分两个方面,一方面是对云容器的监控,另一方面是对业务代码的监控,下面分别叙述。

基础设施监控

我们使用腾讯云作为服务器,腾讯云内部正在开发集成 Prometheus,对 Kubernetes 容器进行监控,目前处于灰度上线阶段。

我们得知之后,直接使用腾讯云集成的 Prometheus 服务,不需要进行配置就直接使用,还是比较省心的。

腾讯云集成的 Prometheus 服务基本配置了全部内容,包括但不限于

  • Kubernetes 容器的数据埋点和数据收集
  • Prometheus 服务的部署、启动、数据采集、存储等
  • Grafana 的前端可视化配置(其实就是写 PromQL,很复杂)

腾讯云原来也有监控服务,使用新集成的 Prometheus 的优势有以下几点:

  • 理论上,Prometheus 的收集精度会更高,因为采样数据间隔更短
  • Prometheus 收集的数据更全,我们能够获得更多信息
  • 可以配置我们自己的 Prometheus 访问接口,如果需要做业务监控,不需要额外配置

目前该项目还处于灰度上线阶段(2020.09.18),目前仍遇到的问题有:

  • Prometheus 和 Grafana 都是有学习成本的,尤其是 Prometheus,此外还要有云原生的基本运维知识,门槛很高。如果我们想自己做一些改变,或者理解监控的内容,需要积累非常多的知识
  • 目前 Grafana 的前端展示都是英文的(当然,这更加准确),但是参数非常多,有时并不能理解,腾讯云缺少本地化翻译工作,也缺少操作指引教程
  • 不支持告警

业务监控

除了 Kubernetes 的宏观监控之外,我们还需要对业务代码进行监控,对每个服务内部做微观监控。

比如我想要知道,在进行压力测试时,哪个接口调用地次数最多,调用的时间最长,服务启动之后 JVM 是否表现合理,由此去判断如果想要优化代码,需要优化哪里。在这种场景下,单纯监控 Kubernetes 容器层面是远远不够的,还需要对业务服务进行监控。

我们总体的 APM 技术栈是:

  • actuator 和 Micrometer 做数据埋点
  • Prometheus 做数据收集(配置过程由腾讯云完成)
  • Grafana 做数据可视化

监控数据埋点(采集)

我们使用 actuator 配合 Micrometer 做数据收集。

Micrometer 默认会收集 JVM 等整体性的指标,我们除此之外,还收集了两项指标:所有接口的调用次数、所有接口的调用时间。

这些指标都通过 actuator 暴露接口,供 Prometheus 查询。默认暴露的接口是 /actuator/prometheus,比如 http://localhost:8080/actuator/prometheus

下面附一下代码,分为三部分,分别是 pom.xmlapplication.yml、埋点代码:

pom.xml 也就是 Maven 中需要引入的包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- micrometer 的 Prometheus 实现 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- AOP 切面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

application.yml 是 Spring 对 actuator 的配置内容:

1
2
3
4
5
6
7
8
9
10
management:
# actuator 允许访问的地址
endpoints:
web:
exposure:
include: prometheus
# 采样时,每个指标都会带的 label,方便可视化查找
metrics:
tags:
application: ${spring.application.name}

埋点业务代码,通过 AOP 切面,对所有接口做拦截,统计数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Aspect
@Component
public class PrometheusAopConfig {

private final MeterRegistry meterRegistry;

public PrometheusAopConfig(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

/**
* 拦截所有controller方法
*/
@Pointcut("execution(* 包.controller.*.*(..))")
public void controllerAop() {
}

@Around("controllerAop()")
public Object controllerAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取类名
String className = joinPoint.getTarget().getClass().getName();
// 方法计数
meterRegistry.counter("method.count", "method", methodName, "className", className).increment();

// 获取执行方法前的时间
long beforeTime = System.currentTimeMillis();

// 执行方法
Object proceed = joinPoint.proceed();

// 记录方法执行时间
long processTime = System.currentTimeMillis() - beforeTime;
Timer timer = meterRegistry.timer("method.time", "method", methodName, "className", className);
timer.record(processTime, TimeUnit.MILLISECONDS);
return proceed;
}
}

监控数据处理

由腾讯云的 Prometheus 监控进行数据的采集,通过简单的配置,比如设置 Kubernetes 的实例名、服务名、端口号、采集数据接口等,就可以完成 Prometheus 的配置。

腾讯云Prometheus配置

如果个人搭建的话,需要去 Prometheus 官方下载程序,配置 application.yml 文件,启动服务。


监控数据展示

使用 Grafana 进行可视化展示。

使用 Grafana 时,因为有很多 dashboard,因此要自己选择。

首先选择右边栏,找到 Manage 选项,会看到 Dashboards 目录。默认会有一个文件夹,是腾讯云配置的 Kubernetes 文件夹,里面是对云容器监控的 dashboard(有很多很多)。

腾讯云Grafana默认

个人配置的话,需要先创建一个 dashboard,再在里面创建一个个 panel,每一个 panel 就是一条 PromQL(写起来非常痛苦)。

还有一条捷径,就是去找网上已经配好的模板,比如 Micrometer 采集的 JVM 数据,网上就有已经配置好的模板《JVM (Micrometer)》。但是业务逻辑的话,还是乖乖自己写吧。