一、背景简介

Prometheus由于对云原生的优秀的支持,成为各企业上云监控选型的首选,本文主要介绍Prometheus监控指标采集后图表的配置。

Prometheus受启发于Google的Brogmon监控系统(相似的Kubernetes是从Google的Brog系统演变而来),从2012年开始由前Google工程师在Soundcloud以开源软件的形式进行研发,并且于2015年早期对外发布早期版本。2016年5月继Kubernetes之后成为第二个正式加入CNCF基金会的项目,同年6月正式发布1.0版本。2017年底发布了基于全新存储层的2.0版本,能更好地与容器平台、云平台配合。详见:Prometheus简介

二、监控指标数据

Prometheus使用Pull模式主动拉取监控数据,在应用内部我们实现监控指标数据采集并暴露一个数据采集端口,在Prometheus服务侧配置监控指标采集任务进行监控数据采集(Prometheus默认15s采集一次监控数据)。

指标数据格式

在spring-boot应用中,默认监控采集路径为: http://172.16.1.15:31744/actuator/prometheus

Prometheus采集到监控数据如下:

# HELP srcp_url_control_item_req_metric_seconds_max  
# TYPE srcp_url_control_item_req_metric_seconds_max gauge
srcp_url_control_item_req_metric_seconds_max{application="sedp-mos-biz-server",} 0.0
# HELP srcp_url_control_item_req_metric_seconds  
# TYPE srcp_url_control_item_req_metric_seconds summary
srcp_url_control_item_req_metric_seconds_count{application="sedp-mos-biz-server",} 1411.0
srcp_url_control_item_req_metric_seconds_sum{application="sedp-mos-biz-server",} 4.836

# HELP srcp_url_control_req_metric_seconds_max  
# TYPE srcp_url_control_req_metric_seconds_max gauge
srcp_url_control_req_metric_seconds_max{application="sedp-mos-biz-server",} 0.001
# HELP srcp_url_control_req_metric_seconds  
# TYPE srcp_url_control_req_metric_seconds summary
srcp_url_control_req_metric_seconds_count{application="sedp-mos-biz-server",} 1410.0
srcp_url_control_req_metric_seconds_sum{application="sedp-mos-biz-server",} 5.324

在上述Prometheus采集到的监控指标数据中,有几个关键词:

  • HELP:表示一个监控指标名称
  • TYPE:表示该监控指标的类型

三、监控指标类型

Prometheus定义了4种不同的指标类型(metric type):Counter(计数器)、Gauge(仪表盘)、Histogram(直方图)、Summary(摘要)。

Counter:只增不减的计数器

Counter类型的指标其工作方式和计数器一样,只增不减(除非系统发生重置)。常见的监控指标,如http_requests_total,node_cpu都是Counter类型的监控指标。 一般在定义Counter类型指标的名称时推荐使用_total作为后缀。

Counter是一个简单但有强大的工具,例如我们可以在应用程序中记录某些事件发生的次数,通过以时序的形式存储这些数据,我们可以轻松的了解该事件产生速率的变化。 PromQL内置的聚合操作和函数可以让用户对这些数据进行进一步的分析:

例如,通过rate()函数获取HTTP请求量的增长率:

rate(http_requests_total[5m])

查询当前系统中,访问量前10的HTTP地址:

topk(10, http_requests_total)

Gauge:可增可减的仪表盘

与Counter不同,Gauge类型的指标侧重于反应系统的当前状态。因此这类指标的样本数据可增可减。常见指标如:node_memory_MemFree(主机当前空闲的内容大小)、node_memory_MemAvailable(可用内存大小)都是Gauge类型的监控指标。

通过Gauge指标,用户可以直接查看系统的当前状态:

node_memory_MemFree

对于Gauge类型的监控指标,通过PromQL内置函数delta()可以获取样本在一段时间返回内的变化情况。例如,计算CPU温度在两个小时内的差异:

delta(cpu_temp_celsius{host="zeus"}[2h])

还可以使用deriv()计算样本的线性回归模型,甚至是直接使用predict_linear()对数据的变化趋势进行预测。例如,预测系统磁盘空间在4个小时之后的剩余情况:

predict_linear(node_filesystem_free{job="node"}[1h], 4 * 3600)

使用Histogram和Summary分析数据分布情况

官方文档: Histogram and Summary

除了Counter和Gauge类型的监控指标以外,Prometheus还定义了Histogram和Summary的指标类型。Histogram和Summary主用用于统计和分析样本的分布情况。

在大多数情况下人们都倾向于使用某些量化指标的平均值,例如CPU的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统API调用的平均响应时间为例:如果大多数API请求都维持在100ms的响应时间范围内,而个别请求的响应时间需要5s,那么就会导致某些WEB页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。

为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在010ms之间的请求数有多少而1020ms之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram和Summary都是为了能够解决这样问题的存在,通过Histogram和Summary类型的监控指标,我们可以快速了解监控样本的分布情况。

例如,指标prometheus_tsdb_wal_fsync_duration_seconds的指标类型为Summary。 它记录了Prometheus Server中wal_fsync处理的处理时间,通过访问Prometheus Server的/metrics地址,可以获取到以下监控样本数据:

# HELP prometheus_tsdb_wal_fsync_duration_seconds Duration of WAL fsync.
# TYPE prometheus_tsdb_wal_fsync_duration_seconds summary
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173
prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002
prometheus_tsdb_wal_fsync_duration_seconds_count 216

从上面的样本中可以得知当前Prometheus Server进行wal_fsync操作的总次数为216次,耗时2.888716127000002s。其中中位数(quantile=0.5)的耗时为0.012352463,9分位数(quantile=0.9)的耗时为0.014458005s。

在Prometheus Server自身返回的样本数据中,我们还能找到类型为Histogram的监控指标:prometheus_tsdb_compaction_chunk_range_bucket

# HELP prometheus_tsdb_compaction_chunk_range Final time range of chunks on their first compaction
# TYPE prometheus_tsdb_compaction_chunk_range histogram
prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="6400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="25600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="102400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260
prometheus_tsdb_compaction_chunk_range_bucket{le="6.5536e+06"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780
prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09
prometheus_tsdb_compaction_chunk_range_count 780

与Summary类型的指标相似之处在于Histogram类型的样本同样会反应当前指标的记录的总数(以_count作为后缀)以及其值的总量(以_sum作为后缀)。不同在于Histogram指标直接反应了在不同区间内样本的个数,区间通过标签len进行定义。

同时对于Histogram的指标,我们还可以通过histogram_quantile()函数计算出其值的分位数。不同在于Histogram通过histogram_quantile函数是在服务器端计算的分位数。 而Sumamry的分位数则是直接在客户端计算完成。因此对于分位数的计算而言,Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。反之对于客户端而言Histogram消耗的资源更少。在选择这两种方式时用户应该按照自己的实际场景进行选择。

四、监控图表配置

在Prometheus配件监控指标数据采集任务后,我们需要通过PromQL语言实现监控数据查询,将查询结果显示Grafana图表或进行告警,本文主要讲述对常见类型的监控指标的图表配置。

PromQL的学习可以参考下面文档:探索PromQL

Counter指标

由于counter类型的指标是一个只增不减的计数器,所以如果直接在Grafana的监控图表展示该指标非常不直观,所以对于counter类型的指标需要用到PromQL的几个常用内置函数:rateirate

increase(v range-vector)函数是PromQL中提供的众多内置函数之一。其中参数v是一个区间向量,increase函数获取区间向量中的第一个后最后一个样本并返回其增长量。因此,可以通过以下表达式Counter类型指标的增长率:

increase(node_cpu[2m]) / 120

这里通过node_cpu[2m]获取时间序列最近两分钟的所有样本,increase计算出最近两分钟的增长量,最后除以时间120秒得到node_cpu样本在最近两分钟的平均增长率。并且这个值也近似于主机节点最近两分钟内的平均CPU使用率。

除了使用increase函数以外,PromQL中还直接内置了rate(v range-vector)函数,rate函数可以直接计算区间向量v在时间窗口内平均增长速率。因此,通过以下表达式可以得到与increase函数相同的结果:

rate(node_cpu[2m])

需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(v range-vector)。irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。

irate(node_cpu[2m])

irate函数相比于rate函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate函数。

详细内容参见:PromQL内置函数

Gauge指标

在一般情况下,系统管理员为了确保业务的持续可用运行,会针对服务器的资源设置相应的告警阈值。例如,当磁盘空间只剩512MB时向相关人员发送告警通知。 这种基于阈值的告警模式对于当资源用量是平滑增长的情况下是能够有效的工作的。 但是如果资源不是平滑变化的呢? 比如有些某些业务增长,存储空间的增长速率提升了高几倍。这时,如果基于原有阈值去触发告警,当系统管理员接收到告警以后可能还没来得及去处理问题,系统就已经不可用了。 因此阈值通常来说不是固定的,需要定期进行调整才能保证该告警阈值能够发挥去作用。 那么还有没有更好的方法吗?

PromQL中内置的predict_linear(v range-vector, t scalar) 函数可以帮助系统管理员更好的处理此类情况,predict_linear函数可以预测时间序列v在t秒后的值。它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。例如,基于2小时的样本数据,来预测主机可用磁盘空间的是否在4个小时候被占满,可以使用如下表达式:

predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0

Histogram指标

Histogram和Summary都可以用于统计和分析数据的分布情况。区别在于Summary是直接在客户端计算了数据分布的分位数情况。而Histogram的分位数计算需要通过histogram_quantile(φ float, b instant-vector)函数进行计算。其中φ(0<φ<1)表示需要计算的分位数,如果需要计算中位数φ取值为0.5,以此类推即可。

以指标http_request_duration_seconds_bucket为例:

# HELP http_request_duration_seconds request duration histogram
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.5"} 0
http_request_duration_seconds_bucket{le="1"} 1
http_request_duration_seconds_bucket{le="2"} 2
http_request_duration_seconds_bucket{le="3"} 3
http_request_duration_seconds_bucket{le="5"} 3
http_request_duration_seconds_bucket{le="+Inf"} 3
http_request_duration_seconds_sum 6
http_request_duration_seconds_count 3

当计算5分位数时,使用如下表达式:

histogram_quantile(0.5, http_request_duration_seconds_bucket)

通过对Histogram类型的监控指标,用户可以轻松获取样本数据的分布情况。同时分位数的计算,也可以非常方便的用于评判当前监控指标的服务水平。

Summary指标

Histograms and summaries both sample observations, typically request durations or response sizes. They track the number of observations and the sum of the observed values, allowing you to calculate the average of the observed values. Note that the number of observations (showing up in Prometheus as a time series with a _count suffix) is inherently a counter (as described above, it only goes up). The sum of observations (showing up as a time series with a _sum suffix) behaves like a counter, too, as long as there are no negative observations. Obviously, request durations or response sizes are never negative. In principle, however, you can use summaries and histograms to observe negative values (e.g. temperatures in centigrade). In that case, the sum of observations can go down, so you cannot apply rate() to it anymore. In those rare cases where you need to apply rate() and cannot avoid negative observations, you can use two separate summaries, one for positive and one for negative observations (the latter with inverted sign), and combine the results later with suitable PromQL expressions.

根据上述描述,Summary类型的指标是一个复合类型指标,其中的count和sum本质上是Counter类型指标,如下:

# HELP srcp_url_control_req_metric_seconds_max  
# TYPE srcp_url_control_req_metric_seconds_max gauge
srcp_url_control_req_metric_seconds_max{application="sedp-mos-biz-server",} 0.001
# HELP srcp_url_control_req_metric_seconds  
# TYPE srcp_url_control_req_metric_seconds summary
srcp_url_control_req_metric_seconds_count{application="sedp-mos-biz-server",} 1410.0
srcp_url_control_req_metric_seconds_sum{application="sedp-mos-biz-server",} 5.324

上面srcp_url_control_req指标采集了链接管控请求的请求数量、请求响应时长总和、请求响应时长最大值。则计算请求的平均响应时长表达式如下:

irate(srcp_url_control_item_req_metric_seconds_sum{namespace="srcp-test"}[5m])/ irate(srcp_url_control_item_req_metric_seconds_count{namespace="srcp-test"}[5m])

rate vs irate

rate:计算的时候是选择指定时间范围下的第一和最后一个样本进行计算;
irate: 过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的长尾问题。

irate时间范围说明:irate使用最后两个点计算,那为什么还要指定类似于 [1m] 的时间范围呢?这个 [1m] 不是用来计算的,irate 在计算的时候会最多向前在 [1m] 范围内找点,如果超过 [1m] 没有找到数据点,这个点的计算就放弃了。

详见文档:PromQL查询之rate函数的使用

总结

本文简单介绍了Prometheus指标数据采集、指标类型及PromQL使用,读者可以根据监控指标类型编写PromQL在Grafana中进行查询配置可视化图表。

参考文档

PromQL查询之rate函数的使用
直方图 (Histogram)