Prometheus 与 Grafana:监控报警系统中的银弹
监控报警是服务稳定的基础,是性能优化的重要依据,是可以未雨绸缪的重大利器。现代系统赋予了监控报警重要地位,近年来随着微服务设计理念不断成熟与广泛使用,做为系统方案的设计者,监控的选择和使用将是搭建系统不可或缺的一个环节。
Prometheus和Grafana像一组黄金搭档一样出现在了历史的洪流中,就像当年PHP和MYSQL一样。这两个系统以其明确的分工以及简单易用的特性、高度可扩展性,在这个领域赢得了一席之地。
可谓如果对指标收集、监控、报警毫无头绪,那选择Prometheus和Grafana就一定没有错。
1. Prometheus(https://prometheus.io/)
Prometheus 是一个集监控(图表) + 报警 + 时序数据库于一体的开源项目。它采用定期拉取接口的方式来采集需要统计的指标。
1.1 结构和运作方式
关于Exporter
一般来说,指标数据的提供就需要改造现有业务组件做为Exporter。像go或者Java这种长驻进程类的语言,都提供了Promethues客户端包来暴露服务的常用指标。如与Spring Boot结合使用的Micrometer Registry Prometheus(https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus),它不仅提供了JVM的统计数据(如GC的各代的堆大小,线程数量等),还提供了接口请求的响应时间,QPS等等;当然自定义指标是肯定可以支持的。go语言也有对应的包Prometheus Go client library(https://github.com/prometheus/client_golang),虽不及Java的强大,但该有的功能也一个不少。
对于PHP这种并非常驻内存的编程语言来说,实现指标的暴露就比较麻烦了。有一种方案是使用如redis的本地存储,将指标记录下来,以便Prometheus Server来拉取的时候可以采集到完整的指标。但这个方案的缺点很明显,与业务耦合了。
Prometheus提供了Pushgateway的方案
(https://prometheus.io/docs/practices/pushing/),像PHP这样的服务往Pushgateway上打点,再由Pushgateway提供Exporter的功能暴露指标。
1.2 数据的存储
Prometheus将拉取到的指标数据保存于时间序列数据库中(TSDB),默认提供了本地文件式的TSDB,可以直接使用,一般可满足大部分监控场景。
但考虑到持久化、高可用、迁移相关事宜,Prometheus 通过支持配置 remote_read/remote_write接口的方式,可以很方便地使用外部的存储(https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage),如InfluxDB,甚至Elasticsearch等等。
1.3 统计实践
通过访问 exporter 暴露的http接口,可采集到如下格式的样本数据:
# HELP task_execute_count task执行计数
# TYPE task_execute_count counter
task_execute_count{task="test1",instance="host1.huajiao.com",} 10
task_execute_count{task="test1",instance="host2.huajiao.com",} 20
# HELP system_load_average_1m 1分钟内机器负载
# TYPE system_load_average_1m gauge
system_load_average_1m{application="system-java"} 0.06
# HELP task_consume_all 接口耗时分布
# TYPE task_consume_all histogram
task_consume_all_bucket{le="10"} 100
task_consume_all_bucket{le="20"} 200
task_consume_all_bucket{le="+Inf"} 100
task_consume_all_sum 10000
task_consume_all_count 400
# HELP go_gc_duration_seconds GC耗时统计
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.326e-05
go_gc_duration_seconds{quantile="0.25"} 3.9552e-05
go_gc_duration_seconds{quantile="0.5"} 4.9175e-05
go_gc_duration_seconds{quantile="0.75"} 6.5348e-05
go_gc_duration_seconds{quantile="1"} 0.000909402
go_gc_duration_seconds_sum 132.156493338
go_gc_duration_seconds_count 2.217437e+06
该样本数据中包含四种指标类型,以下一一介绍。
指标类型
指标类型由Exporter做标记。上面示例中以 # 开头的注释标识了采集到样本的信息:HELP是描述。TYPE标识了样本的指标类型。
指标的类型分为四大类:
Counter 计数器,可用于接口请求数,任务完成数等。可用来展示QPS类数据。
Gauge 实时值。可用于展示系统负载,内存使用
Histogram 直方图(柱状图)。用于将数据分组,如:耗时分布
Summary 概括分布。类似直方图,但是支持按百分比划分结果。可展示出:“95%接口耗时都在200毫秒以下”的效果。
前两中用于反应当前系统运行状态,后两种用于分析数据分布。
样本
统计样本一般由一个 metric(指标) 和N个label(标签)。如:
main_api{api="main/index",code="200"} 100
其中main_api是指标,并包含2个标签api code,最后的100是样本值。
标签相当于维度,相同指标、相同标签的数据构成一条时间序列。
查询 PromQL
(https://prometheus.io/docs/prometheus/latest/querying/basics/)
其全称是 Prometheus Query Language。是 Prometheus 提供的一种特殊表达式,来查询监控数据。
交互界面是Prometheus 提供的可视化的 Web操作界面——Graph 。
瞬时向量
简单的可以查询某项指标、某标签下的指标。如:
main_api #列出该指标所有数据
main_api{instance="host1.huajiao.com", code="200"} #过滤出指定标签下的数据
main_api{code=~"5.*"} #支持正则过滤规则 =~
以上查询只列出最新的一个样本,样本集被称为 瞬时向量 instant vector。如果要列出一批样本,需要指定时间范围,筛选出区间向量 range vector。
区间向量
区间向量用于指定时间范围的时间单位有:s-秒 m-分 h-小时 d-天 w-周 y-年
main_api{code="200"}[1m] #最近1分钟内http响应码为200的样本
如果想查询之前样本还可以指定偏移 offset
main_api{code="200"}[1m] offset 5m #5分钟前 1分钟内http响应码为200的样本
结合操作符的查询
Prometheus 内对于样本的操作符有以下几类:
数学操作符:加减乘除、取模等
比较操作符:==, >, < 等
三个逻辑操作符:and or unless
聚合操作符:sum, count, topk 等
通过这些操作符以及一些内置函数,可以实现一些直观的数据统计:
a.统计接口的QPS
rate(main_api[5m])
该指标为 counter 类型,所以要用 rate 获取其平均增长值。
rate是计算每秒速率。其中[5m]表示按取每5分钟内的值计算平均值,数值越大,曲线越平滑
b.按api标签分组 获取QPS
sum(rate(main_api{api=~"live/.*"}[1m])) by(api)
c.按api标签分组,获取QPS的Top30
topk(30, sum(rate(main_api[1m])) by(api))
d.统计各个接口的99%的响应时间
histogram_quantile(0.99, sum(rate(api_consume_all_bucket[1m])) by (le))
rate
对于counter这种只增不减的指标类型,展示时要将数值转换为单位时间内的增长值。这就要用到 rate 函数。
rate(counter[5s]) = (样本值 - 5s前的样本值) / 5s
2. 结合 Grafana
PromQL 的查询虽然强大,然而展示太简陋。Grafana 很好的弥补了这个短板。它可以和 Prometheus 完美结合,并支持多种展现方式。
Grafana 支持 Prometheus 作为数据源,Prometheus对外也提供了api用于进行查询。只需要在grafana操作界面做简单配置即可。
2.1 部分图表展示
QPS
Grafana 中配置图表使用就是在Prometheus后台的查询。如各服务接口的QPS:
sum(rate(kong_http_status{job="kong_system"}[1m])) by (service)
耗时分布
90%的耗时和50%的耗时分布
histogram_quantile(0.9, sum(rate(kong_latency_total_bucket[5m])) by (le))
histogram_quantile(0.5, sum(rate(kong_latency_total_bucket[5m])) by (le))
2.2 报警
Prometheus自带AlertManager组件用于监控报警,然而 Grafana 的报警功能操作更方便,学习成本更低。在特定图标上点击编辑,即可见到 alert 报警选项卡。
报警方式
grafana 支持多种报警方式,最为方便的就是 webhook。当触发报警条件时,以http请求的方式调用报警接口,继而实现自定义的报警方式。
报警条件
grafana 定义了一些函数用来检测触发条件。如阈值检测,变化检测。而且支持自定义时间区间。如:
上述报警条件为:查询语句B最近五分钟的最大值低于 0.0001 并且 查询语句C最近10s的值不在于3~7之间,就触发报警。
阈值判断除了max()函数外,还有 sum(), count(), median(), avg() 。此外还有可用于进行变化检测的 diff() 函数,用于检测QPS突增等情况。
3. 花椒中的应用
3.1 Gateway
花椒系统中有PHP的项目,考虑到PHP无法内置exporter,必须用到 PushGateWay。官网 Prometheus 的插件 声明了几个缺点:
容易成为单点
Prometheus无法对服务的监控
数据没有淘汰机制,旧数据不会自动清理
然而除外还有一个严重缺点:新数据覆盖旧数据,推送到gateway的值,即为最终值。这样就无法支持打点方式计数。
因此花椒同事自行研发了一套用于接入 Prometheus 的高性能方案,包括一个Gateway和配套SDK。基本解决了所有缺点:
多集群部署,避免单点故障,数据存储到 Redis
支持打点方式计数
打点请求使用UDP协议,最大程度减小对业务的性能影响
此外包括 Gateway和配套SDK。SDK与receiver的通信采用UDP协议,最大限度减小对业务的性能影响。
3.2 高可用方案
Prometheus的数据默认存储于本地,一旦故障服务不可用,甚至数据都会丢失,存在单点风险。
因此最好将数据远程保存,并做主从备份。此外还要搭建两个Prometheus实例,保证Prometheus本身的高可用性。
花椒选用了时序数据库 TimescaleDB。这是基于 PostGRESQL实现的。文档丰富,部署简单。
Prometheus 支持通过配置 remote_write 和 remote_read 的方式进行远程读写。本身其实是http请求。因此需要一个实现该操作的中间件Prometheus PostgreSQL Adapter.
中间件启动如下:
docker run -d -p 9201:9201 timescale/prometheus-postgresql-adapter:latest \
-pg-host=host.to.pgsql \
-pg-password=pwd2pg
配置如下:
remote_write:
- url: "http://127.0.0.1:9201/write"
remote_read:
- url: "http://127.0.0.1:9201/read"
3.3 优化查询
当PromeQL较为复杂或数据量很大时,查询可能超时。此类统计不能实时计算,必须提前制定规则,在后台定时计算。查询时只需要获取当前查询结果即可。
如下配置中,rule_files的每一个元素都是一个统计脚本配置。
rule_files:
- rule1.yml
- rule2.yml
rule1.yml中的配置可以是
groups:
- name: example
rules:
- record: job:http_inprogress_requests:sum
expr: sum(http_inprogress_requests) by (job)
其定时计算的频率设置字段为 global.evaluation_interval
4.结束语
Prometheus 最近风头正盛,明显感觉到使用的人越来越多。本文只是简单介绍了其部分功能。其他诸如与docker、K8s的配合,服务发现、集群等功能,因为没有涉及到,所以并未细述。
目前感觉 Prometheus比一些老牌监控系统如Nagios 更适用于如今的生产场景,而其开箱即用的便捷性,也使得切换到该系统变动简单可行。
5. 参考资料
官方参考文档:
https://prometheus.io/docs/prometheus/latest/
Prometheus Book:
https://yunlzheng.gitbook.io/prometheus-book/