Prometheus 存储的是时序数据, 即按照相同时序(相同的名字和标签),以时间维度存储连续的数据的集合。
时序
时序(time series) 是由名字(Metric),以及一组 key/value 标签定义的。具有相同的名字以及相同的标签则属于相同时序。
时序的名字由 ASCII 字符,数字,下划线,以及冒号组成。它的名字应该具有语义化,一般表示一个可以度量的指标,例如: http_requests_total, 可以表示 http 请求的总数。
时序的标签可以使 Prometheus 的数据更加丰富,例如 http_requests_total{method="POST"} 可以表示所有 http 中的 POST 请求。
时序数据的4种类型
时序数据可以分为以下4种类型:
- Counter: Counter 表示收集的数据是按照某个趋势(增加/减少)一直变化的,我们往往用它记录服务请求总量、错误总数等。例如 Prometheus server 中 http_requests_total, 表示 Prometheus 处理的 http 请求总数,我们可以使用 delta, 很容易得到任意区间数据的增量。
- Gauge: Gauge 表示搜集的数据是一个瞬时的值,与时间没有关系,可以任意变高变低,往往可以用来记录内存使用率、磁盘使用率等。例如 Prometheus server 中 go_goroutines, 表示 Prometheus 当前 goroutines 的数量。
- Histogram: Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。
- Summary: Summary 和 Histogram 类似,由 <basename>{quantile="<φ>"},<basename>_sum,<basename>_count 组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。
关于Histogram和Summary:
Histogram和Summary主用用于统计和分析样本的分布情况。例如,统计延迟在010ms之间的请求数有多少而1020ms之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram和Summary都是为了能够解决这样问题的存在,通过Histogram和Summary类型的监控指标,我们可以快速了解监控样本的分布情况。
例如下面一个Summary类型的metric:
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。其中耗时小于0.012352463秒的样本占比50%,耗时小于0.014458005的样本占比90%,而耗时小于0.017316173的样本占比99%。因此,这个quanatile表示百分位数,代表了相应样本值在总样本中的分布比例。
下面是一个Histogram类型的mertric:
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
Histogram类型的样本同样会反应当前指标的记录的总数(以_count作为后缀)以及其值的总量(以_sum作为后缀)。不同在于Histogram指标直接反应了在不同区间内样本的个数。例如上面的指标表示 tsdb_compaction_chunk 的值小于 1.6384e+06 的个数为260个,小于 6.5536e+06 的个数为780,小于 2.62144e+07 的个数为780,小于最高值的个数为780。tsdb_compaction_chunk值得总量为 1.1540798e+09(_sum指标)。指标记录总数为 780(_count指标)。
注意后面的采样点是包含前面的采用点的,例如xxx_bucket的值为30,而xxx_bucket的值为120,那么意味着这120个采用点中,有30个是小于10ms的,其余90个采样点的响应时间是介于10ms和50ms之间的。而+Inf是最高bucket的上限值,所以xxx_bucket是所有采样点的数量,是Prometheus自动增加的一个bucket。
同时对于Histogram的指标,我们还可以通过histogram_quantile()函数计算出其值的百分位数(quantile)。不同在于Histogram通过histogram_quantile函数是在服务器端计算的百分位数。 而Sumamry的百分位数则是直接在客户端计算完成。因此对于百分位数的计算而言,Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。反之对于客户端而言Histogram消耗的资源更少。在选择这两种方式时用户应该按照自己的实际场景进行选择。
更多关于百分位数(quantile)的讨论,请参考:https://cloud.tencent.com/developer/news/319419
Job和instance
Prometheus 中,将任意一个独立的数据源(target)称之为实例(instance)。包含相同类型的实例的集合称之为作业(job)。 如下是一个含有四个重复实例的作业:
- job: api-server
- instance 1: 1.2.3.4:5670
- instance 2: 1.2.3.4:5671
- instance 3: 5.6.7.8:5670
- instance 4: 5.6.7.8:5671
这个job中包含了四个数据源,例如 1.2.3.4:5670 就是一个数据源(targe),Prometheus会定时从数据源中拉取数据存储为metric。
对于每一个实例,Prometheus都会为其存储以下metric:
up{job="<job-name>", instance="<instance-id>"}: 0 表示该实例故障,1 表示该实例正常工作
scrape_duration_seconds{job="<job-name>", instance="<instance-id>"} 表示拉取数据的时间间隔
scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"} 表示采用重定义标签(relabeling)操作后仍然剩余的样本数
scrape_samples_scraped{job="<job-name>", instance="<instance-id>"} 表示从该数据源获取的样本数
其中 up metric可以有效应用于监控该实例是否正常工作。
PromQL使用
PromQL (Prometheus Query Language) 是 Prometheus 自己开发的数据查询 DSL 语言,语言表现力非常丰富,内置函数很多,在日常数据可视化以及rule 告警中都会使用到它。
Prometheus 存储的是时序数据,而它的时序是由名字和一组标签构成的。一个简单的查询相当于是对各种标签的筛选,例如:
http_requests_total{code="200"} // 表示查询名字为 http_requests_total,code 为 "200" 的数据
http_requests_total{code!="200"} // 表示查询 code 不为 "200" 的数据
http_requests_total{code=~"2.."} // 表示查询 code 为 "2xx" 的数据
http_requests_total{code!~"2.."} // 表示查询 code 不为 "2xx" 的数据
http_requests_total[5m] 范围区间查询: 过去 5 分钟数据
配置文件
Prometheus 启动的时候,可以加载运行参数 -config.file 指定配置文件,默认为 prometheus.yml。下面是一个典型的配置文件样例:
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
rule_files:
- "rules/node.rules"
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 5s
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
scrape_interval: 8s
static_configs:
- targets: ['127.0.0.1:9100', '127.0.0.12:9100']
- job_name: 'mysqld'
static_configs:
- targets: ['127.0.0.1:9104']
- job_name: 'memcached'
static_configs:
- targets: ['127.0.0.1:9150']
Exporter
在 Prometheus 中负责数据汇报的程序统一叫做 Exporter, 而不同的 Exporter 负责不同的业务。 它们具有统一命名格式,即 xx_exporter, 例如负责主机信息收集的 node_exporter。
一个 Exporter 本质上就是将收集的数据,转化为对应的文本格式,并提供 http 请求。Exporter 收集的数据转化的文本内容以行 (\n) 为单位,空行将被忽略, 文本内容最后一行为空行。
下面是一个Exporter收集的样本数据的例子:
# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="idle"} 362812.7890625
# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 3.0703125
- 以 # HELP 开头表示 metric 帮助说明。
- 以 # TYPE 开头表示定义 metric 类型,包含 counter, gauge, histogram, summary, 和 untyped 类型。
- 其他表示一般注释,供阅读使用,将被 Prometheus 忽略。
告警
告警能力在Prometheus的架构中被划分成两个独立的部分。通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。
在 Prometheus 中告警分为两部分:
- Prometheus 服务根据所设置的告警规则将告警信息发送给 Alertmanager。
- Alertmanager 对收到的告警信息进行处理,包括去重,降噪,分组,策略路由告警通知。
使用告警服务主要的步骤如下:
- 下载配置 Alertmanager。
- 通过设置 -alertmanager.url 让 Prometheus 服务与 Alertmanager 进行通信。
- 在 Prometheus 服务中设置告警规则。
告警规则
在Prometheus中一条告警规则主要由以下几部分组成:
- 告警名称:用户需要为告警规则命名,当然对于命名而言,需要能够直接表达出该告警的主要内容。
- 告警规则:告警规则实际上主要由PromQL进行定义,其实际意义是当表达式(PromQL)查询结果持续多长时间(During)后出发告警。
一个典型的告警规则如下:
groups:
- name: example
rules:
- alert: HighErrorRate
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
labels:
severity: page
annotations:
summary: High request latency
description: description info
- alert:告警规则的名称。
- expr:基于PromQL表达式告警触发条件,用于计算是否有时间序列满足该条件。
- for:评估等待时间,可选参数。用于表示只有当触发条件持续一段时间后才发送告警。在等待期间新产生告警的状态为pending。
- labels:自定义标签,允许用户指定要附加到告警上的一组附加标签。
- annotations:用于指定一组附加信息,比如用于描述告警详细信息的文字等,annotations的内容在告警产生时会一同作为参数发送到Alertmanager。
AlertManager
Alertmanager拥有以下特性:
- 分组:分组机制可以将详细的告警信息合并成一个通知,避免一次性接受大量的告警通知,而无法对问题进行快速定位。
- 抑制:抑制是指当某一告警发出后,可以停止重复发送由此告警引发的其它告警的机制。
- 静默:提供了一个简单的机制可以快速根据标签对告警进行静默处理。如果接收到的告警符合静默的配置,Alertmanager则不会发送告警通知。
Alertmanager主要负责对Prometheus产生的告警进行统一处理,因此在Alertmanager配置中一般会包含以下几个主要部分:
- 全局配置(global):用于定义一些全局的公共参数,如全局的SMTP配置,Slack配置等内容;
- 模板(templates):用于定义告警通知时的模板,如HTML模板,邮件模板等;
- 告警路由(route):根据标签匹配,确定当前告警应该如何处理;
- 接收人(receivers):接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;
- 抑制规则(inhibit_rules):合理设置抑制规则可以减少垃圾告警的产生