数据模型

数据模型

Prometheus从根本上存储的所有数据都是时间序列:具有时间戳的数据流只属于单个度量指标和该度量指标下的多个标签维度。Prometheus采用了单值模型,数据模型的核心概念是metric,labelssamples;其格式为:<metric name>{<label name>=<label value>, …}。一条Prometheus数据由一个指标名称(metric)和N个标签(label,N >= 0)组成的,比如下面这个例子:

promhttp_metric_handler_requests_total{code="200",instance="192.168.0.107:9090",job="prometheus"} 106

metric的命名具有业务含义,比如http_request_total,指标的类型分为:Counter,Gauge,Historgram,Summary。labels用于表示维度,samples由时间戳和数值组成。Prometheus会自动生成targetinstances作为标签。这种数据模型和OpenTSDB的数据模型是比较类似的,详细的信息可以参考官网文档 Data model。另外,关于指标和标签的命名,官网有一些指导性的建议,可以参考 Metric and label naming

除了存储时间序列数据外,Prometheus也可以利用查询表达式存储5分钟的返回结果中的时间序列数据。

Metrics Name & Label

每条时间序列是由唯一的指标名称和一组标签(key=value)的形式组成。指标名称 一般是给监测对像起一名字,例如 http_requests_total 这样,它有一些命名规则,可以包字母数字之类的的。通常是以 应用名称开头_监测对像_数值类型_单位 这样。

  • push_total

  • userlogin_mysql_duration_seconds

  • app_memory_usage_bytes

标签 就是对一条时间序列不同维度的识别了,例如 一个http请求用的是POST还是GET,它的endpoint是什么,这时候就要用标签去标记了。最终形成的标识便是这样了

http_requests_total{method="POST",endpoint="/api/tracks"}

针对http_requests_total这个metrics name无论是增加标签还是删除标签都会形成一条新的时间序列。查询语句就可以跟据上面标签的组合来查询聚合结果了。如果以传统数据库的理解来看这条语句,则可以考虑http_requests_total是表名,标签是字段,而timestamp是主键,还有一个float64字段是值了。

Counter:只增不减的计数器

Counter类型代表一种样本数据单调递增的指标,即只增不减,并且您的应用程序在其metrics上公开的值是自启动以来的总和。可以使用counter类型的指标来表示服务的请求数、已完成的任务数、错误发生的次数等。譬如,如果我们要去计算每秒吞吐的网络流量:

rate(node_network_receive_bytes_total[5m])

[5m] 意为使用过去五分钟的数据进行计算,即会得到过去五分钟的平均值:

{device="lo",instance="localhost:9100",job="node"}  1859.389655172414
{device="wlan0",instance="localhost:9100",job="node"} 1314.5034482758622

rate函数的输出值即为Gauge,我们也就可以应用Gauge的聚合函数。如上的用法,我们也可以忽略网卡详情而获得总的容量:

sum without(device)(rate(node_network_receive_bytes_total[5m]))
# {instance="localhost:9100",job="node"} 3173.8931034482762

或者我们也可以指定获取某个网卡的数据:

sum without(instance)(rate(node_network_receive_bytes_total{device="eth0"}[5m]))
# {device="eth0",job="node"} 3173.8931034482762

Gauge:可增可减的仪表盘

Gauge是对于状态的快照,通常被用于那些需要求和、求平均以及最小值、最大值的场景。Gauge类型代表一种样本数据可以任意变化的指标,即可增可减。Gauge通常用于像温度或者内存使用率这种指标数据,也可以表示能随时增加或减少的“总数”。常见指标如:node_memory_MemFree(主机当前空闲的内容大小、node_memory_MemAvailable(可用内存大小)都是Gauge类型的监控指标。

譬如Node Exporter导出了node_filesystem_size_bytes这个度量,其反映了挂载的文件系统的体积,并且包含了device, fstype,以及mountpoint标签。我们能够通过如下的方式计算总的文件大小:

sum without(device, fstype, mountpoint)(node_filesystem_size_bytes)

这里的without函数会高速sum聚合器将有相同label的度量值相加,但是忽略device, fstype,以及mountpoint标签。我们可能会得到如下的结果:

node_filesystem_free_bytes{device="/dev/sda1",fstype="vfat",
    instance="localhost:9100",job="node",mountpoint="/boot/efi"} 70300672
node_filesystem_free_bytes{device="/dev/sda5",fstype="ext4",
    instance="localhost:9100",job="node",mountpoint="/"} 30791843840
node_filesystem_free_bytes{device="tmpfs",fstype="tmpfs",
    instance="localhost:9100",job="node",mountpoint="/run"} 817094656
node_filesystem_free_bytes{device="tmpfs",fstype="tmpfs",
    instance="localhost:9100",job="node",mountpoint="/run/lock"} 5238784
node_filesystem_free_bytes{device="tmpfs",fstype="tmpfs",
    instance="localhost:9100",job="node",mountpoint="/run/user/1000"} 826912768

# 聚合的结果如下
{instance="localhost:9100",job="node"} 32511390720

同样地,我们也可以忽略instance标签,即获得总的文件系统的大小:

sum without(device, fstype, mountpoint, instance)(node_filesystem_size_bytes)
{job="node"} 32511390720

您可以对其他聚合使用相同的方法。max会告诉您每台计算机上最大的已挂载文件系统的大小:

max without(device, fstype, mountpoint)(node_filesystem_size_bytes)
# {instance="localhost:9100",job="node"} 30792601600
avg without(instance, job)(process_open_fds)

对于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)

使用HistogramSummary分析数据分布情况

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

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

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

例如,指标prometheus_tsdb_wal_fsync_duration_seconds的指标类型为Summary。它记录了Prometheus Serverwal_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.0123524639分位数(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消耗的资源更少。在选择这两种方式时用户应该按照自己的实际场景进行选择。

Links

上一页