3. Prometheus深入:PromQL查询语言、Exporter原理与部署、告警规则配置(Alertmanager)

聊到Prometheus,很多人第一反应是“哦,一个监控系统”。但说实话,如果只会装个Server、配个Target,那你只用了它20%的能力。真正让Prometheus强大的,是它的查询语言PromQL,以及围绕Exporter和Alertmanager构建的生态。

这一章,我会把这三块核心内容掰开揉碎讲清楚。嗯,都是我在生产环境里踩过坑、流过泪之后总结出来的经验。

3.1 PromQL:不只是查数据,是数据分析

PromQL(Prometheus Query Language)是Prometheus的查询语言。它跟SQL有点像,但又不完全一样。SQL查的是关系型数据库里的表,PromQL查的是时序数据库里的指标。

说白了,PromQL就是用来“算”指标的。

3.1.1 基础查询:Instant Vector与Range Vector

先搞清楚两个核心概念:

  • Instant Vector(瞬时向量):某个时间点上的数据。比如 node_cpu_seconds_total,查的是“现在”这个时刻的CPU使用时间。
  • Range Vector(范围向量):一段时间内的数据。比如 node_cpu_seconds_total[5m],查的是过去5分钟内的所有数据点。

我刚开始学的时候,经常搞混这两个概念。后来我给自己总结了一句话:“瞬时向量看快照,范围向量看趋势”

重要提示: 在写告警规则时,绝大多数场景用的是Instant Vector。因为告警需要的是“当前是否异常”,而不是“过去5分钟的数据长什么样”。

3.1.2 常用函数:rate、irate、increase

这三个函数,我敢说你在生产环境里天天都会用到。它们专门用来处理Counter类型的指标。

  • rate():计算一段时间内的平均每秒增长率。比如 rate(node_cpu_seconds_total[5m]),算的是过去5分钟内CPU使用率的平均值。
  • irate():计算瞬时增长率。它只看最近两个数据点,反应更灵敏,但容易抖动。
  • increase():计算一段时间内的总增量。比如 increase(node_network_receive_bytes_total[1h]),算的是过去1小时内网络接收的总字节数。

我的习惯: 在告警规则里,我一般用rate(),因为它平滑,不容易误报。在Grafana图表里,我偶尔用irate(),因为它能更快反映变化。你想想看,告警要是天天抖动,运维同学会疯掉的。

3.1.3 聚合操作:by、without、topk

有时候我们需要把多个维度的数据合并起来看。比如,我想知道所有机器的平均CPU使用率,而不是每台机器的。

# 按instance分组,计算平均CPU使用率
avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))

# 排除mode维度,计算所有mode的总和
sum without(mode) (rate(node_cpu_seconds_total[5m]))

# 取CPU使用率最高的前5台机器
topk(5, avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))

我曾经在排查一个性能问题时,用topk快速定位到了某台机器CPU异常飙升。如果没有这个函数,你得一台一台看,效率差太多了。

3.2 Exporter原理与部署:把数据喂给Prometheus

Prometheus自己不会凭空产生数据。它需要Exporter来采集。Exporter说白了就是一个“数据翻译官”,把各种系统、中间件的指标翻译成Prometheus能理解的格式。

3.2.1 Exporter的工作原理

Exporter的核心逻辑其实很简单:

  1. 通过系统调用、API、或者读取文件等方式获取原始数据。
  2. 把数据转换成Prometheus的Metrics格式(通常是# HELP# TYPE开头的文本)。
  3. 暴露一个HTTP端点(通常是/metrics),等待Prometheus来抓取。

嗯,这里要注意:Exporter本身不存储数据,它只负责“采集-转换-暴露”。数据存哪里?那是Prometheus Server的事。

3.2.2 常用Exporter部署示例

我挑两个最常用的Exporter讲讲:Node Exporter和Blackbox Exporter。

Node Exporter: 采集Linux系统指标(CPU、内存、磁盘、网络等)。

# 下载并启动Node Exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.0/node_exporter-1.6.0.linux-amd64.tar.gz
tar xvf node_exporter-1.6.0.linux-amd64.tar.gz
cd node_exporter-1.6.0.linux-amd64
./node_exporter --web.listen-address=":9100"

Blackbox Exporter: 用于探测外部服务的可用性(HTTP、TCP、ICMP等)。

# 启动Blackbox Exporter
./blackbox_exporter --config.file=blackbox.yml --web.listen-address=":9115"

避坑指南: 我曾经在生产环境里直接用了默认端口,结果跟其他服务冲突了。建议你提前规划好端口分配,比如Node Exporter统一用9100,Blackbox用9115,MySQL Exporter用9104。这样后期维护起来省心很多。

3.2.3 自定义Exporter:什么时候需要自己写?

官方和社区提供的Exporter已经覆盖了大部分场景。但如果你遇到以下情况,就得自己动手了:

  • 监控一个自研的中间件或内部系统。
  • 需要采集一些业务层面的指标(比如订单量、用户活跃数)。
  • 官方Exporter的性能或功能不满足需求。

写一个自定义Exporter其实不难。你只需要暴露一个/metrics端点,返回符合Prometheus格式的文本就行。我建议用Go语言写,因为Prometheus官方提供了Client Library,几行代码就能搞定。

3.3 告警规则配置与Alertmanager

数据有了,查询也会了,接下来就是告警。告警是监控的“最后一公里”,也是最有价值的一环。

3.3.1 告警规则怎么写?

告警规则定义在Prometheus Server的配置文件中。核心是rules字段,里面包含一组alert规则。

groups:
  - name: node_alerts
    rules:
      - alert: NodeCPUUsageHigh
        expr: (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) > 0.9
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} CPU使用率超过90%"
          description: "当前CPU使用率: {{ $value | humanizePercentage }}"

这里有几个关键点:

  • expr:触发条件,就是一条PromQL表达式。
  • for:持续时间。比如上面这个规则,CPU使用率超过90%持续5分钟才告警。这能有效避免瞬时抖动导致的误报。
  • labels:告警的标签,可以用来区分告警级别。
  • annotations:告警的描述信息,支持模板变量。

重要提示: for字段一定要用好。我见过很多新手不写for,结果CPU一波动就收到一堆告警。你想想看,凌晨3点被叫起来,结果发现只是瞬间抖动,那得多崩溃。

3.3.2 Alertmanager:告警的“调度中心”

Prometheus Server只负责“产生告警”,不负责“发送告警”。发送告警是Alertmanager的事。

Alertmanager的核心功能有三个:

  • 分组(Grouping):把相似的告警合并成一条通知。比如100台机器同时CPU高,你不会想收到100条短信的。
  • 抑制(Inhibition):如果某个高级别告警触发了,就屏蔽相关的低级别告警。比如机器宕机了,那CPU高、内存高这些告警就没意义了。
  • 静默(Silencing):在维护窗口期,可以临时屏蔽某些告警。

下面是一个简单的Alertmanager配置示例:

route:
  receiver: 'email-admin'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'

receivers:
  - name: 'email-admin'
    email_configs:
      - to: 'admin@example.com'
  - name: 'pagerduty-critical'
    pagerduty_configs:
      - routing_key: 'your-pagerduty-key'

我个人习惯把告警分成两个级别:WarningCritical。Warning走邮件或钉钉,Critical走PagerDuty或电话。这样既不会漏掉重要告警,也不会被无关紧要的通知淹没。

3.3.3 告警的“去重”与“降噪”

告警疲劳是运维的大敌。我见过一个团队,一天收到上千条告警,最后大家直接无视了所有告警。这跟没有监控有什么区别?

要解决这个问题,可以从几个方面入手:

  • 合理设置for持续时间:避免瞬时抖动。
  • 用好分组和抑制:减少重复告警。
  • 设置合适的repeat_interval:告警恢复前,不要频繁重复发送。我一般设成4小时。
  • 定期Review告警规则:把那些“从来没人看”的告警删掉或降级。

我的经验: 告警规则不是越多越好。我建议你从“最核心的5个指标”开始,比如CPU、内存、磁盘、网络、进程存活。等稳定了再慢慢加。一口吃不成胖子,告警也一样。

3.4 本章知识体系图

下面这张图展示了Prometheus核心三件套的关系:

Prometheus核心三件套 Exporter 采集数据 暴露 /metrics 端点 PromQL 查询与分析 Instant / Range Vector Alertmanager 告警分组与发送 抑制 / 静默 抓取 触发 核心流程: 1. Exporter 采集系统/中间件指标,暴露 HTTP 端点 2. Prometheus Server 定期抓取 Exporter 数据,存入时序数据库 3. PromQL 对数据进行分析,满足条件时触发告警 4. Alertmanager 接收告警,进行分组、抑制,最终发送通知

这张图把Prometheus的核心逻辑串起来了。你想想看,Exporter是“眼睛”,负责看;PromQL是“大脑”,负责分析;Alertmanager是“嘴巴”,负责喊人。三者缺一不可。


公众号:蓝海资料掘金营,微信deep3321