https://aleiwu.com/post/aliyun-exporter-bp/

Prometheus原理

Prometheus通过HTTP周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口并且符合Prometheus定义的数据格式,就可以接入Prometheus监控。

工作流程

  1. Prometheus Server 以服务发现(如 Kubernetes 等)的方式自动发现或者静态配置添加监控目标;
  2. Prometheus Server 定期从监控目标(Jobs/exporters)或 Pushgateway 中拉取数据(metrics),将时间序列数据保存到其自身的时间序列数据库(TSDB)中;
  3. Prometheus Server 通过 HTTP Server 对外开放接口,可以给可视化工具(如 Prometheus web UI、Grafana 或 你自己开发的工具)用 PromQL 查询/导出数据;
  4. 当有告警产生时,Prometheus Server 将告警信息推送到 Alertmanager ,由 Alertmanager 根据配置的策略发送告警信息到对应的接收方;
  5. Pushgateway 接收 “Short-lived” 类型的 Jobs 推送过来的 metrics 并缓存,等待 Prometheus Server 抓取。

11.png

Prometheus 的组件

  • Prometheus Server 负责从 Exporter 拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)
    • Retrieval: 采样模块
    • TSDB: 存储模块默认本地存储为tsdb
    • HTTP Server: 提供http接口查询和面板,默认端口为9090
  • Exporters/Jobs
    负责收集目标对象(host, container…)的性能数据,并通过 HTTP 接口供 Prometheus Server 获取。支持数据库、硬件、消息中间件、存储系统、http服务器、jmx等。只要符合接口格式,就可以被采集。
  • Short-lived jobs
    瞬时任务的场景,无法通过pull方式拉取,需要使用push方式,与PushGateway搭配使用
  • PushGateway
    由于Prometheus数据采集基于Pull模型进行设计,因此在网络环境的配置上必须要让Prometheus Server能够直接与Exporter进行通信。当这种网络需求无法直接满足时,就可以利用PushGateway来进行中转。可以通过PushGateway将内部网络的监控数据主动Push到Gateway当中。而Prometheus Server则可以采用同样Pull的方式从PushGateway中获取到监控数据。
  • 客户端sdk
    官方提供的客户端类库有go、java、scala、python、ruby,其他还有很多第三方开发的类库,支持nodejs、php、erlang等
  • PromDash
    使用rails开发的dashboard,用于可视化指标数据,已废弃
  • Alertmanager
    从 Prometheus server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对收的接受方式,发出报警。常见的接收方式有:电子邮件,pagerduty,OpsGenie, webhook 等。
  • Service Discovery
    服务发现,Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据。

使用pushgateway的理由

  1. prometheus默认采用pull模式,由于不在一个网络或者防火墙的问题,导致prometheus 无法拉取各个节点的数据。
  2. 监控业务数据时,需要将不同数据汇总,然后由prometheus统一收集

    pushgateway的缺陷

  3. 多个节点的数据汇总到pushgateway,当它宕机后影响很大

  4. pushgateway可以持续化推送所有的监控数据,即使监控已经下线,还会获取旧的监控数据。需手动清理不需要的数据

3、重启后数据丢失

为什么要用pull拉取数据?

Prometheus采集数据是用的pull也就是拉模型,通过HTTP协议去采集指标,只要应用系统能够提供HTTP接口就可以接入监控系统,相比于私有协议或二进制协议来说开发、简单。优点主要是:

  • 如果目标实例挂掉,你可以很快知道
  • 网络环境的配置上必须要让Prometheus Server能够直接与Exporter进行通信。当这种网络需求无法直接满足时,就可以利用PushGateway来进行中转。

准确性与可靠性的权衡

Prometheus 作为一个基于指标(Metric)的监控系统,在设计上就放弃了一部分数据准确性:

  • 比如在两次采样的间隔中,内存用量有一个瞬时小尖峰,那么这次小尖峰我们是观察不到的;
  • 再比如 QPS、RT、P95、P99 这些值都只能估算,无法和日志系统一样做到 100% 准确,下面也会讲一个相关的坑。

放弃一点准确性得到的是更高的可靠性,这里的可靠性体现为架构简单、数据简单、运维简单。假如你维护过 ELK 或其它日志架构的话,就会发现相比于指标,日志系统想要稳定地跑下去需要付出几十倍的机器成本与人力成本。
既然是权衡,那就没有好或不好,只有适合不适合。

警报和监控图不匹配

最近半年常常被问两个问题所困惑:

  • 我的历史趋势图看上去超过水位线了,警报为什么没报?
  • 我的历史趋势图看上去挺正常的,警报为什么报了?

这其中有一个原因是:趋势图上每个采样点的采样时间和警报规则每次的计算时间不是严格一致的。当时间区间拉得比较大的时候,采样点非常稀疏,不如警报计算的间隔来得密集,这个现象尤为明显,比如时序图采样了 0 秒,60 秒,120 秒三个点。而警报在 15 秒,30 秒,45 秒连续计算出了异常,那在图上就看不出来。另外,经过越多的聚合以及函数操作,不同时间点的数据差异会来得越明显,有时确实容易混淆。
这个其实不是问题,碰到时将趋势图的采样间隔拉到最小,仔细比对一下,就能验证警报的准确性。而对于聚合很复杂的警报,可以先写一条 Recording Rule,再针对 Recording Rule 产生的新指标来建警报。这种方式也能帮助我们更高效地去建分级警报(超过不同阈值对应不同的紧急程度)。

Alertmanager的group_interval会影响resolved通知

Alertmanager 里有一个叫 group_interval 5m 的配置,用于控制同一个 group 内的警报最快多久通知一次。这里有一个问题是 firing(激活)和 resolved(已消除)的警报通知是共享同一个 group 的。也就是说,假设我们的 group_interval 5m 是默认的 5 分钟,那么一条警报激活十几秒后立马就消除了,它的消除通知会在报警通知的 5 分钟之后才到,因为在发完报警通知之后,这个 Group 需要等待 5 分钟的 group_interval 才能进行下一次通知。
这个设计让“警报消除就立马发送消除通知”变得几乎不可能,因为假如把 group_interval 变得很小的话,警报通知就会过于频繁,而调大的话,就会拖累到消除通知。
这个问题修改一点源码即可解决,不过无伤大雅,不修也完全没问题。

为什么要 Alertmanager?

Prometheus 生态中的警报是在 Prometheus Server 中计算警报规则(Alert Rule)并产生的,而所谓计算警报规则,其实就是周期性地执行一段 PromQL,得到的查询结果就是警报。只是,当 Prometheus Server 计算出一些警报后,它自己并没有能力将这些警报通知出去,只能将警报推给 Alertmanager,由 Alertmanager 进行发送。
这个切分,一方面是出于单一职责的考虑,另一方面则是因为警报发送确实不是一件”简单”的事,需要一个专门的系统来做好它。可以这么说,Alertmanager 的目标不是简单地”发出警报”,而是”发出高质量的警报”。它提供的高级功能包括但不限于:

  • Go Template 渲染警报内容;
  • 管理警报的重复提醒时机与消除后消除通知的发送;
  • 根据标签定义警报路由,实现警报的优先级、接收人划分,并针对不同的优先级和接收人定制不同的发送策略;
  • 将同类型警报打包成一条通知发送出去,降低警报通知的频率;
  • 支持静默规则: 用户可以定义一条静默规则,在一段时间内停止发送部分特定的警报,比如已经确认是搜索集群问题,在修复搜索集群时,先静默掉搜索集群相关警报;
  • 支持”抑制”规则(Inhibition Rule): 用户可以定义一条”抑制”规则,规定在某种警报发生时,不发送另一种警报,比如在”A 机房网络故障”这条警报发生时,不发送所有”A 机房中的警报”。

为什么要用Exporter?

如果要监控ecs、mysql,redis等服务,那你要调用他们的接口来获取信息,每家都有一套接口,如果prometheus直接集成这些接口有会使得很臃肿和容易出错,所以prometheus做法是每个软件做一个exporter,prometheus使用http读取exporter的信息,而且格式都经过exporter先统一好。
简而言之,exporter就是个翻译,把各种语言翻译成一种统一的Prometheus认识的语言。

如何写一个好用的Exporter?

逻辑就是一句话:

  • 写一个 Web 服务, 每当 Prometheus 请求我们这个服务问我们要指标的时候, 我们就请求云监控的 API 获得监控信息, 再转化为 Prometheus 的格式返回出去;

但这样写完之后仅仅是”能用”, 要做到”好用”, 还有诸多考量.
Prometheus 官方文档中 Writing Exporter

  • 做到开箱即用(默认配置就可以直接开始用)
  • 推荐使用 YAML 作为配置格式
  • 指标使用下划线命名
  • 为指标提供 HELP String (指标上的 # HELP 注释, 事实上这点大部分 exporter 都没做好)
  • 为 Exporter 本身的运行状态提供指标
  • 可以提供一个落地页

官方文档里讲了 Exporter 需要开箱即用, 但其实这只是基本需求, 在开箱即用的基础上, 一个良好的 Exporter 需要做到高度可配置化. 这是因为大部分 Exporter 暴露的指标中, 真正会用到的大概只有 20%, 冗余的 80% 指标不仅会消耗不必要的资源还会拖累整体的性能。对于一般的 Exporter 而言, BP 是默认只提供必要的指标, 并且提供 extra 和 filter 配置, 允许用户配置额外的指标抓取和禁用一部分的默认指标. 而对于阿里云 Exporter 而言, 由于阿里云有数十种类型的资源(RDS, ECS, SLB…), 因此我们无法推测用户到底希望抓哪些监控信息, 因此只能全部交给用户配置. 当然, 项目还是提供了包含 SLB, RDS, ECS 和 Redis 的默认配置文件, 尽力做到开箱即用

什么是Operator?

由于 Prometheus 本身没有提供管理配置的 API 接口(尤其是管理监控目标和管理警报规则),也没有提供好用的多实例管理手段,因此这一块往往要自己写一些代码或脚本。
Operator = Controller + CRD。假如你不了解什么是 Controller 和 CRD,可以看一个 K8s 本身的例子:我们提交一个 Deployment 对象来声明期望状态,比如 3 个副本;而 K8s 的 Controller 会不断地干活(跑控制循环)来达成期望状态,比如看到只有 2 个副本就创建一个,看到有 4 个副本了就删除一个。在这里,Deployment 是 K8s 本身的 API 对象。那假如我们想自己设计一些 API 对象来完成需求呢?K8s 本身提供了 CRD(Custom Resource Definition),允许我们定义新的 API 对象。但在定义完之后,K8s 本身当然不可能知道这些 API 对象的期望状态该如何到达。这时,我们就要写对应的 Controller 去实现这个逻辑。而这种自定义 API 对象 + 自己写 Controller 去解决问题的模式,就是 Operator Pattern。

Prometheus Operator 的好处都有啥?

假如我们用 ConfigMap 来存配置,那就没有任何的校验。万一写错了(比如 yaml 缩进错误):

  • 那么 Prometheus 做配置热更新的时候就会失败,假如配置更新失败没有报警,那么 Game Over;
  • 热更新失败有报警,但这时 Prometheus 突然重启了,于是配置错误重启失败,Game Over;

而在 Prometheus Operator 中,所有在 Prometheus 对象、ServiceMonitor 对象、PrometheusRule 对象中的配置都是有 Schema 校验的,校验失败 apply 直接出错,这就大大降低了配置异常的风险。

**

Prometheus可以监控web地址吗?

Prometheus结合blockbox可以去监控一些web站点是不是健康,它会定时去调用一些你们提供的web接口,然后通过分析接口返回码或者是返回体,判断web服务是否健康,可以作为站点监控。除此之外还支持TCP、DNS、ICMP以及HTTPS。

Prometheus监控Oracle、MySQL数据库方便吗?

由于现在各种Prometheus export非常丰富,针对这些中间件,如Oracle、MySQL这种监控是非常方便的。如果你有一些自己定制的监控指标,也可以去定制一些export,这些东西都比较简单,并不是很困难。

Prometheus用什么存储比较好?本地存储容量有限。

在我们实际生产环境中本地存储只保留一个月数据,历史数据都放到M3db中保存。

exporter的种类越来越多,怎么解决?

大部分的监控对象都需要特定类型exporter,因为每种类型的监控指标的数据格式不一样,都要特定的exporter去解析这种指标,并且转换为Prometheus识别的指标类型。至于说exporter的量非常多,exporter本身是很轻量级的,其实虽然多,但是在我们自己部署的环境里面,有的时候就和我们的应用捆绑到一个Pod去部署,其实非常方便维护,本身也不会有什么负载压力,exporter本身很稳定,维护起来也非常简单。

非容器化部署的中间件也能监控吗?

其实Prometheus采集数据的时候,只适合采集数据的监控格式是否满足它的需求,它本身并不关心被监控对象是不是在容器里面,没有任何关系。只要对外暴露的监控数据格式符合Prometheus的要求就可以。