监控告警Prometheus

2,024 阅读15分钟

Prometheus

什么是Prometheus

Prometheus的特点

易于管理

  1. Prometheus核心部分只有一个单独的二进制文件,不存在任何的第三方依赖(数据库、缓存等等),唯一的需要就是本地磁盘,因此不会有潜在级联故障的风险
  2. Prometheus基于Pull模型的架构方式,可以在任何地方(本地电脑、开发环境、测试环境)搭建我们的监控系统
  3. 对于一些复杂的情况,还可以使用Prometheus服务发现(Service Discovery)的能力动态管理监控目标

监控服务的内部运行状态

  1. Prometheus鼓励用户监控服务的内部状态,基于Prometheus丰富的Client库,用户可以轻松的在应用程序中添加对Prometheus的支持,从而让用户可以获取服务和应用内部真正的运行状态

强大的数据模型

  1. 所有采集的监控数据均以指标(metric)的形式保存在内置的时间序列数据库当中(TSDB),所有的样本除了基本的指标名称以外,还包含一组用于描述该样本特征的标签

强大的查询语言PromQL

  1. Prometheus内置了一个强大的数据库查询语言PromQL,通过PromQL可以实现对监控数据的查询、聚合;同时PromQL也被应用于数据可视化(如Grafana)以及告警当中

    通过PromQL可以轻松回答类似于以下的问题:

    1. 过去一段时间中95%应用延迟时间的分布范围
    2. 预测4小时后,磁盘空间占用大致会是个什么情况
    3. CPU占用率前5的服务有哪些(过滤)

高效

  1. 对于监控系统而言,大量的监控任务必然导致有大量的数据产生,而Prometheus可以高效的处理这些数据,对于单一的Prometheus Server实例而言它可以处理:

    1. 数以百万的监控指标
    2. 每秒处理数十万的数据点

可扩展

  1. 可以在每个数据中心,每个团队运行独立的Prometheus Server,Prometheus对于联邦集群的支持,可以让多个Prometheus实例产生一个逻辑集群,当单实例Prometheus Server处理的任务量过大时,通过使用功能分区(sharding)+联邦集群(federation)可以对其进行扩展

易于集成

  1. 使用Prometheus可以快速搭建监控服务,并且可以非常方便地在应用程序中进行集成,目前支持:Java、JMX、Python、Go、Ruby、.Net、Node.js等等语言的客户端SDK,基于这些SDK可以快速让应用程序纳入到Prometheus的监控当中,或者开发自己的监控数据收集程序
  2. 同时这些客户端收集的监控数据,不仅仅支持Prometheus,还能支持Graphite这些其他的监控工具
  3. 同时Prometheus还支持与其他的监控系统进行集成:Graphite、Statsd、Collected、Scollector、muini、Nagios等,Prometheus社区还提供了大量第三方实现的监控数据采集支持:JMX、CloudWatch、EC2、MySQL、PostgresSQL、Haskell、Bash、SNMP、Consul、Haproxy、Mesos、Bind、CouchDB、Django、Memcached、RabbitMQ、Redis等等

核心组件

  • Prometheus Server, 主要用于抓取数据和存储时序数据,另外还提供查询和 Alert Rule 配置管理
  • client libraries,用于对接 Prometheus Server, 可以查询和上报数据
  • push gateway ,用于批量,短期的监控数据的汇总节点,主要用于业务数据汇报等
  • 各种汇报数据的 exporters ,例如汇报机器数据的 node_exporter, 汇报 MongoDB 信息的 MongoDB exporter 等等
  • 用于告警通知管理的 alertmanager

基础架构

image.png

  • 从这个架构图,也可以看出 Prometheus 的主要模块包含, Server, Exporters, Pushgateway, PromQL, Alertmanager, WebUI 等

    它大致使用逻辑是这样:

    1. Prometheus server 定期从静态配置的 targets 或者服务发现的 targets 拉取数据
    2. 当新拉取的数据大于配置内存缓存区的时候,Prometheus 会将数据持久化到磁盘(如果使用 remote storage 将持久化到云端)
    3. Prometheus 可以配置 rules,然后定时查询数据,当条件触发的时候,会将 alert 推送到配置的 Alertmanage
    4. Alertmanager 收到警告的时候,可以根据配置,聚合,去重,降噪,最后发送警告
    5. 可以使用 API, Prometheus Console 或者 Grafana 查询和聚合数据

存储计算层

  1. Prometheus Server,里面包含了存储引擎和计算引擎
  2. Retrieval组件为取数组件,它会主动从Pushgateway或者Exporter拉取指标数据
  3. Service discovery可以动态发现要监控的目标
  4. TSDB,数据核心存储与查询
  5. HTTP server,对外提供HTTP服务

采集层

采集层分为两类,一类是生命周期较短的作业,还有一类是生命周期较长的作业

  1. 短作业:直接通过API,在退出时间指标推送给Pushgateway
  2. 长作业:Retrieval组件直接从Job或者Exporter拉取数据

应用层

应用层主要分为两种,一种是AlertManager,另一种是数据可视化

  1. AlertManager:可对接Pageduty,是一套付费的监控报警系统,可实现短信报警、5分钟无人ack,打电话通知、仍然无人ack,通知值班人员Manager...

    Email发送邮件

  2. 数据可视化:Prometheus build-in WebUI、Grafana、其他基于API开发的客户端

Prometheus配置

配置说明

安装完成进入Prometheus目录,查看或者修改Proemtheus.yaml

  1. global配置块:控制Prometheus服务器的全局配置

    1. scrape_interval:配置拉取数据的时间间隔,默认为一分钟
    2. evaluation_interval:规则验证(生成alert)的时间间隔,默认为一分钟
  2. rule_files配置块:规则配置文件

  3. scrape_configs配置块:配置采集目标相关,prometheus监视的目标,Prometheus自身的运行信息可以通过HTTP访问,所以Prometheus可以监控自己的运行数据

    1. job_name:监控作业的名称
    2. static_config:表示静态目标配置,就是固定从某个target拉取数据
    3. targets:指定监控的目标,其实就是从哪儿拉取数据,Prometheus会从http://hadoop202:9090/metrics上拉取数据
  4. Prometheus是可以在运行时自动加载配置的,启动时需要添加:--web.enable-lifecyucle

    配置示例:

    image.png

    配置多种采集方式

    image.png

Pushgateway

  • Prometheus在正常情况下是采用拉模式从产生metric的作业或者exporter(不如专门监控主机的NodeExporter)拉取监控数据,但是我们要监控的是Flink on YARN作业,想要让Prometheus自动发现作业的提交、结束以及自动拉取数据显然是比较困难的

    Pushgateway就是一个中转组件,通过配置Flink on YARN作业将metric推到Pushgateway,Prometheus再从Pushgateway拉取

AlertManager

  • 选择性安装

  • 配置告警规则示例,alertmanager.yaml

    image.png

Node Exporter

  • 在Prometheus的架构设计中,Prometheus Server主要负责数据的收集,存储并且对外提供数据查询支持,而实际的监控样本数据的收集则是由Exporter完成,因此为了能够监控到某些东西,如主机的CPU使用率,我们需要使用到Exporter,Prometheus周期性的从Exporter暴露的HTTP服务地址(通常是/metrics)拉取监控样本数据
  • Exporter可以是一个相对开发的概念,其可以是一个独立运行的程序独立于监控目标以外,也可以是直接内置在监控目标中。只要能够向Prometheus提供标准格式的监控样本数据即可
  • 为了能够采集到主机的运行指标如CPU、内存、磁盘等信息,我们可以使用Node Exporter,Node Exporter同样采用Golang编写,并且不存在任何的第三方依赖,只需要下载,解压即可运行,可以从prometheus.io/downlaod/获取最新的node exporter版本的二进制包

PromQL

  • Prometheus通过指标名称(metric name)以及对应的一组标签(labelset)唯一定义一条时间序列。指标名称反映了监控样本的基本标识,而label则在这个基本特征上未采集到的数据提供了多种特征维度,用户可以基于这些特征维度过滤、聚合、统计从而产生新的计算后的一条时间序列,PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询。聚合以及逻辑运算能力的支持。并且被广泛应用在Prometheus的日常应用当中,聚合以及逻辑运算能力的支持。并且被广泛应用在Prometheus的日常应用当中,包括对数据查询、可视化、告警处理当中。可以这么说,PromQL是Prometheus所有应用场景的基础,理解和掌握PromQL是Prometheus入门的第一课

数据模型

  • Prometheus所有采集的监控数据均以时间序列(具有相同指标名称、相同标签集合的、有时间戳标记的数据流)的形式保存在内置的时间序列数据库(TSDB)中。除了存储的时间序列,Prometheus还可以根据查询请求产生的临时的、衍生的时间序列作为返回结果
指标名称(Metric Name)和标签(label)
  • 每一条时间序列由指标名称(Metic Name)以及一组标签(键值对)唯一标识。其中指标的名称可以反映被监控样本的含义(例如,http_requets_total表示当前系统接收到HTTP请求总量)

    通过使用标签,Prometheus开启了强大的多维数据模型:对于相同的指标名称,通过不同的标签列表集合,会形成特定的度量维度实例(例如:所有包含度量名称为 /api/tracks 的http请求,打上method=POST的标签,就会形成具体的http请求)。改变任何度量指标上的任何标签值(包括添加或删除指标),都会创建新的时间序列

样本Sample
  • Sample构成了真正的时间序列值(sample),样本由以下部分组成:

    • 时间戳(timestamp):一个精确到毫秒的时间戳
    • 样本值(value):一个float64的浮点型数据表示当前的样本值

基本用法

查询时间序列
  • 当Prometheus通过Exporter采集到相应的监控指标样本数据后,我们就可以通过PromQL对监控样本数据进行查询

    当我们直接使用监控指标名称查询时,可以查询到该指标下的所有时间序列

    prometheus_http_requests_total
    

    image.png

  • PromQL还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:完全匹配和正确匹配

    1. PromQL支持使用 = 和 != 两种完全匹配模式

      1. 通过使用 label=value 可以选择哪些标签满足表达式定义的时间序列
      2. 反之使用 label!=value 则可以根据标签匹配排除时间序列

      条件过滤

      prometheus_http_requests_total{code="200"}
      prometheus_http_requests_total{code!="200"}
      

      image.png

    2. PromQL还可以支持使用正则表达式作为匹配条件,多个表达式直接使用 | 进行分离

      1. 使用 label=~regx 表示选择那些标签符合正则表达式定义的时间序列
      2. 反之使用 label!~regx 进行排除
范围查询
  • 直接通过类似于PromQL表达式httprequesttotal查询序列是,返回值中只会包含改时间序列中的最新的一个样本值,这样的返回结果我们称之为瞬时向量,而响应的这样的表达式称之为瞬时向量表达式

    而如果我们想获取过去一段时间范围内的样本数据时,我们则需要使用区间向量表达式。区间向量表达式和瞬时向量表达式之间的差异在于区间向量表达式中我们需要定义时间选择的范围,时间范围通过时间范围选择器[]进行定义

    例如,通过以下表达式可以选择最近5分钟内的所有样本数据

    prometheus_http_requests_total{}[5m]
    

    image.png

    通过区间向量表达式查询到的结果我们称之为区间向量,除了使用m表示分钟以外,PromQL的时间范围选择器还支持其它的时间单位:

    • s:秒
    • m:分钟
    • h:小时
    • d:天
时间位移操作
  • 在瞬时向量表达式或者区间向量表达式中,都是以当前时间未基准:

    prometheus_http_requests_total{}:瞬时向量表达式,选择当前最新的数据

    prometheus_http_request_total{}[5m]:区间向量表达式,选择以当前时间为基准,5分钟内的数据

  • 而如果我们想查询,5分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢?这个时候我们就可以使用位移操作,位移操作的关键字为offset,可以使用offset时间位移操作:

    prometheus_http_requests_total{} offset 5m
    prometheus_http_requests_total{} offset 1d
    

    image.png

使用聚合操作
  • 一般来说,如果描述样本特征的标签(label)在并非唯一的情况下,通过PromQL查询数据,会返回多条满足这些特征维度的时间序列

    而PromQL提供的聚合操作可以用来对这些时间序列进行处理,形成一条新的时间序列:

    # 查询系统所有的http请求的总量
    sum(prometheus_http_requests_total)
    # 按照mode计算主机cpu的平均使用时间
    avg(node_cpu_seconds_total) by (idle)
    # 按照主机查询各个主机的CPU使用率
    sum(sum(irate(node_cpu_seconds_total{mode!='idle'}[5m]))  / sum(irate(node_cpu_seconds_total[5m]))) by (instance)
    

    image.png

    image.png

    image.png

标量和字符串
  • 除了使用瞬时向量表达式和区间向量表达式以外,PromQL还直接支持用户使用标量(Scalar)和字符串(String)

    1. 标量(Scalar):一个浮点型的数字值

      标量只有一个数字,没有时序

      需要注意的是,当使用表达式count(prometheus_http_requests_total),返回的数据类型,依然是瞬时向量,用户可以通过内置函数scalar()将单个瞬时向量转换为标量

    1. 字符串(String):一个简单的字符串值

      直接使用字符串,作为PromQL表达式,则会直接返回字符串

合法的PromQL表达式
  • 所有的PromQL表达式都必须至少包含一个指标名称(例如http_request_total),或者一个不会匹配到空字符串的标签过滤器(例如{code="200"})

    因此以下两种方式,均为合法的表达式

    prometheus_http_request_total #合法
    prometheus_http_request_total{} #合法
    

    而如下的表达式,则不合法:

    {job=~".*"} # 不合法
    

    同时,除了使用{label=value}的形式以外,我们还可以使用内置的name标签来指定监控指标名称

    {_name_=~"prometheus_http_requests_total"} # 合法
    {_name_=~"node_disk_bytes_read|node_disk_bytes_written"} # 合法
    

PromQL操作符

  • 使用PromQL除了能够方便的按照查询和过滤时间序列以外,PromQL还支持丰富的操作符,用户可以使用这些操作符进一步的对事件序列进行二次加工,这些操作符包括:数学运算符,逻辑运算符,布尔运算符等等
数学运算

PromQL支持的所有数学运算符如下所示:

  • +(加法)
  • -(减法)
  • *(乘法)
  • / (除法)
  • %(求余)
  • ^ (幂运算)
布尔运算
  1. Prometheus支持以下布尔运算符如下:

image.png

  1. 使用bool修饰符改变布尔运算符的行为

    布尔运算符默认行为是对时序数据进行过滤,而在其它的情况下我们可能需要的是真正的布尔结果

集合相关运算
  • 使用瞬时向量表达式能够获取到一个包含多个时间序列的集合,我们称为瞬时向量,通过集合运算,可以在瞬时向量与瞬时向量之间进行相应的集合操作

    目前,Prometheus支持以下集合运算符:

    • and(并且)
    • or(或者)
    • unless(排除)

    vector1 and vector2 会产生一个由vector1的元素组成的新的向量,该向量包含vector1中完全匹配vector2的元素组成

    vector1 or vector2会产生一个新的向量,该向量包含vector1中所有的样本数据,以及vector2中没有与vector1匹配到的样本数据

操作符优先级
  • 对于复杂类型的表达式,需要了解运算操作的运行优先级。例如,查询主机CPU的使用率,可以使用表达式:

    # 其中irate是PromQL中的内置函数,用于计算区间向量中时间序列每秒的即时增长率
    100*(1-avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (job))
    

    image.png

  • 在 Prometheus 系统中,二元运算符优先级从高到低的顺序为

    image.png

聚合操作
  • Prometheus还提供了下列内置的聚合操作符,这些操作符作用域瞬时向量,可以将瞬时表达式返回的样本数据进行聚合,形成一个新的时间序列

    • sum(求和)
    • min(最小值)
    • max(最大值)
    • avg(平均值)
    • stddev(标准差)
    • stdvar(标准差异)
    • count(计数)
    • count_values(对value进行计数)
    • bottomk(后n条时序)
    • topk(前n条时序)
    • quantile(分布统计)
  • without用于从计算结果中移除列举的标签,而保留其它标签。by则正好相反,结果向量中只保留列出的标签,其余标签则移除。通过without和by可以按照样本的问题对数据进行聚合

    例如:

    sum(prometheus_http_requests_total) without(instance,pod,service,namespace)
    

    等价于

    sum(prometheus_http_requests_total) by (code,endpoint,handler,job)
    

    image.png

  • cout_values 用于时间序列中每一个样本值出现的次数,count_values会为每一个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。例如:

    count_values("count",prometheus_http_requests_total)
    

    image.png

  • topk和bottomk则用于对样本值进行排序,返回当前样本值前n位,或者后n位的时间序列

    获取HTTP请求数前5位的时序样本数据,可以使用表达式:

    topk(5,prometheus_http_requests_total)
    

    image.png

  • quantile 用于计算当前样本数据值的分布情况 quantile(x,express),其中0<=x<=1

    例如x为0.5时,即表示找到当前样本数据中的中位数:

    quantile(0.5,prometheus_http_requests_total)
    

    image.png