PromQL作为Prometheus的核心查询语言,掌握其基础概念是构建有效告警规则的前提。我们先从最基础的数据类型讲起,这就像学做菜要先认识食材一样重要。
Prometheus主要处理四种数据类型,但实际使用中最常用的是Gauge和Counter。想象一下你家的水表,Gauge就像当前的水位读数,可能忽高忽低;而Counter则像累计用水量,只会不断增加。这种直观的类比能帮助我们快速理解:
Gauge类型:可升可降的瞬时值,比如:
promql复制node_memory_MemFree_bytes # 当前空闲内存量
node_load1 # 1分钟系统负载
这类指标我们通常关注当前值是否超过阈值。
Counter类型:单调递增的计数器,典型例子:
promql复制http_requests_total # HTTP请求总数
node_network_receive_bytes_total # 网卡接收字节数
对于这类指标,我们更关心变化率而非绝对值,比如用rate()函数计算每秒请求量。
理解了数据类型后,我们来看查询选择器——相当于SQL中的WHERE条件。假设我们要监控所有生产环境的Nginx错误请求:
promql复制http_requests_total{status=~"5..",env="production",job="nginx"}
这里的=~是正则匹配,表示匹配所有5xx状态码。选择器支持四种匹配操作符:
=:精确匹配!=:不等于=~:正则匹配!~:正则不匹配设计告警规则就像设置烟雾报警器——太敏感会误报,太迟钝会漏报。我们先从一个最简单的CPU告警开始:
promql复制100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100) > 80
这个查询计算CPU使用率,当超过80%时触发告警。但实际生产中,这种简单规则往往不够用,我们需要更精细的控制。
多条件组合是常见需求。比如磁盘告警要考虑使用率和剩余空间:
promql复制(
node_filesystem_usage_percent{mountpoint="/"} > 80
and
node_filesystem_size_bytes{mountpoint="/"} > 100GB
)
or
node_filesystem_avail_bytes{mountpoint="/"} < 10GB
这个规则表示:当根分区使用率超过80%且总容量大于100GB,或者剩余空间小于10GB时告警。
对于瞬时抖动问题,可以使用for子句设置持续时间:
yaml复制alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01
for: 15m # 持续15分钟满足条件才告警
实际运维中总会遇到各种"特殊情况",这时候就需要搬出PromQL的高级函数了。
nodata检测是常见痛点。比如检测机器是否存活:
promql复制absent_over_time(up{job="node-exporter"}[5m])
但这个写法有个坑——必须为每台机器单独写规则。更聪明的做法是:
promql复制count by(job)(up{job="node-exporter"}) == 0
变化率计算也有讲究。rate()和irate()的区别就像移动平均线和瞬时速度:
promql复制rate(http_requests_total[5m]) # 5分钟平均请求率
irate(http_requests_total[2m]) # 基于最后两个点的瞬时请求率
对于延迟监控,分位数计算是核心需求。假设我们监控API的99分位延迟:
promql复制histogram_quantile(0.99,
sum by(le, path)(rate(http_request_duration_seconds_bucket[5m]))
)
这个查询会按API路径(path标签)分别计算各自的P99延迟。注意histogram的bucket设置要合理,否则会影响分位数准确性。
告警风暴是运维人员的噩梦。通过以下技巧可以显著提升告警质量:
时间窗口策略能过滤瞬时抖动。比如只在工作时间触发业务告警:
promql复制hour() >= 9 and hour() < 18 and
rate(order_failed_total[1h]) > 10
同环比检测可以发现异常趋势。比较当前值与上周同期:
promql复制abs(
(node_memory_MemFree_bytes - node_memory_MemFree_bytes offset 1w)
/ node_memory_MemFree_bytes offset 1w
) > 0.3 # 内存变化超过30%
告警分级也很重要。通过标签区分严重程度:
yaml复制groups:
- name: critical
rules:
- alert: OutOfMemory
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.05
labels:
severity: critical
- name: warning
rules:
- alert: HighMemoryUsage
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.2
labels:
severity: warning
最后,记得为每个告警添加有意义的注解:
yaml复制annotations:
summary: "高内存使用率 ({{ $value }}%)"
description: "{{ $labels.instance }} 内存可用率低于20%"
runbook: "http://wiki/故障处理#内存不足"
让我们通过一个电商网站的例子,串联前面学到的知识。假设需要监控以下核心指标:
promql复制sum(rate(order_total{status="success"}[5m])) by(service)
/
sum(rate(order_total[5m])) by(service) < 0.99
promql复制histogram_quantile(0.99,
rate(payment_duration_seconds_bucket[5m])
) > 2 # P99延迟超过2秒
promql复制abs(
(inventory_current - inventory_expected)
/ inventory_expected
) > 0.1 # 库存差异超过10%
promql复制abs(
(rate(visitor_total[1h]) - rate(visitor_total[1h] offset 1d))
/ rate(visitor_total[1h] offset 1d)
) > 0.5 # 相比昨日同期变化超过50%
将这些规则按业务重要性分级配置,配合合理的通知策略,就能构建出既全面又不扰民的监控体系。在实际部署时,建议先用record_rules预计算常用指标,减轻查询压力:
yaml复制groups:
- name: business_metrics
rules:
- record: job:order_success_rate:5m
expr: sum(rate(order_total{status="success"}[5m])) by(job) / sum(rate(order_total[5m])) by(job)
监控配置从来不是一劳永逸的工作。我建议每周花半小时review告警触发情况,剔除无效规则,优化阈值设置。经过几个迭代周期后,你会发现告警真正成为了得力的助手,而非扰人的噪音。