PromQL 与告警规则实战
学习 PromQL与告警规则实战 相关知识
PromQL 与告警规则实战
1. PromQL 基础
数据类型
| 类型 | 说明 | 示例 |
|---|---|---|
| Instant Vector | 某一时刻的一组时间序列 | node_cpu_seconds_total |
| Range Vector | 一段时间内的一组时间序列 | node_cpu_seconds_total[5m] |
| Scalar | 单个数字 | 1024 |
| String | 字符串 | 较少使用 |
选择器
# 精确匹配
node_memory_MemTotal_bytes{instance="10.0.0.1:9100"}
# 正则匹配(=~ 匹配,!= 不匹配,!~ 正则不匹配)
node_cpu_seconds_total{job=~"node.*", mode!="idle"}
# 范围选择(取过去 5 分钟的数据)
node_cpu_seconds_total[5m]
# 偏移(取 1 小时前的数据)
node_cpu_seconds_total offset 1h
2. 常用 PromQL 查询模式
2.1 CPU 相关
# CPU 使用率(排除 idle)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 每核 CPU 使用率
100 - (avg by(instance, cpu) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# CPU 负载 vs 核心数
node_load1 / count by(instance) (node_cpu_seconds_total{mode="idle"})
2.2 内存相关
# 内存使用率
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# 实际已用内存(不含缓存/buffer)
node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes
# 内存压力(Available 过低告警用)
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.1
2.3 磁盘相关
# 磁盘使用率
100 - ((node_filesystem_avail_bytes{mountpoint="/",fstype!=""} / node_filesystem_size_bytes{mountpoint="/",fstype!=""}) * 100)
# 磁盘 IO 利用率
rate(node_disk_io_time_seconds_total[5m])
# 预测磁盘满时间(7 天趋势)
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[7d], 86400 * 7)
2.4 网络相关
# 网络入流量(bytes/s)
rate(node_network_receive_bytes_total[5m])
# 网络错误率
rate(node_network_receive_errors_total[5m]) / rate(node_network_receive_bytes_total[5m])
2.5 Kubernetes 相关
# Pod CPU 使用率
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod, namespace)
# Pod 内存使用率
sum(container_memory_working_set_bytes{container!=""}) by (pod, namespace)
# 容器重启次数
changes(kube_pod_container_status_restarts_total[1h])
# Node 的 Pod 密度
count(kube_pod_info) by (node)
# HPA 当前/最大副本比
kube_horizontalpodautoscaler_status_current_replicas / kube_horizontalpodautoscaler_spec_max_replicas
3. PromQL 进阶技巧
3.1 聚合操作
# 常用聚合函数
sum by(instance) (...) # 分组求和
avg by(namespace) (...) # 分组平均
max without(cpu) (...) # 排除维度求最大
topk(3, ...) # 取前 3
quantile(0.95, ...) # P95 分位数
count(...) # 计数
# 实例:各 namespace 的 Pod 内存使用 TOP 3
topk(3, sum by(namespace) (container_memory_working_set_bytes{container!=""}))
3.2 数学与比较运算
# 比较(返回 bool 0 或 1)
node_load1 > 2 # 返回实际值(满足条件时)
node_load1 > bool 2 # 返回 0 或 1
# 向量匹配(一对一)
# 要求两边 label 完全一致
node_disk_io_time_seconds_total / node_disk_reads_completed_total
# 向量匹配(多对一) using group_left
# 允许左边有多余的 label
rate(node_cpu_seconds_total[5m]) / on(job) group_left(instance) node_cpu_count
3.3 时间与预测
# 过去 1 小时 vs 现在
avg_over_time(node_load1[1h]) # 1 小时平均
max_over_time(node_load1[1h]) # 1 小时内最大值
# 变化率与预测
deriv(node_load1[30m]) # 30 分钟线性斜率
predict_linear(node_filesystem_avail_bytes[6h], 3600 * 24) # 预测 24h 后
3.4 子查询与直方图
# 子查询:计算过去 1h 内每 5 分钟的 P99 延迟
quantile_over_time(0.99, http_request_duration_seconds{job="api"}[5m])[1h:5m]
# Histogram 使用(适用于请求延迟等已分桶指标)
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# P99
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# P50
4. Recording Rules
为什么需要 Recording Rules
- 复杂查询预计算,Grafana 面板秒级加载
- 减少 Prometheus 查询压力
- 告警规则复用预计算指标
配置结构(Prometheus rules YAML)
groups:
- name: node_recording_rules
interval: 30s # 每 30s 评估一次
rules:
- record: node:cpu_usage_percent:avg_5m
expr: |
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
- record: node:memory_usage_percent
expr: |
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
- record: node:disk_usage_percent:root
expr: |
100 - ((node_filesystem_avail_bytes{mountpoint="/",fstype!=""} / node_filesystem_size_bytes{mountpoint="/",fstype!=""}) * 100)
- record: node:disk_fill_hours:root
expr: |
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 3600 * 24) <= 0
or
node_filesystem_avail_bytes{mountpoint="/"} / deriv(node_filesystem_avail_bytes{mountpoint="/"}[6h]) / 3600
- record: k8s:container_restart_changes_1h
expr: |
changes(kube_pod_container_status_restarts_total[1h])
- record: k8s:pod_cpu_usage:avg_5m
expr: |
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod, namespace)
- record: k8s:pod_memory_usage:bytes
expr: |
sum(container_memory_working_set_bytes{container!=""}) by (pod, namespace)
命名规范建议
<source>:<metric_name>:<aggregation>_<time_range>
# node:cpu_usage_percent:avg_5m
# k8s:pod_memory_usage:bytes
# app:request_latency:p99
5. Alerting Rules
5.1 节点级告警
groups:
- name: node_alerts
interval: 30s
rules:
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} 已离线"
description: "{{ $labels.instance }} 已离线超过 5 分钟"
- alert: NodeCPUUsageHigh
expr: node:cpu_usage_percent:avg_5m > 90
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} CPU 使用率过高"
description: "CPU 使用率: {{ $value | humanizePercentage }}"
- alert: NodeMemoryPressure
expr: node:memory_usage_percent > 90
for: 5m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} 内存不足"
description: "内存使用率: {{ $value | humanizePercentage }}"
- alert: NodeDiskFillPrediction
expr: node:disk_fill_hours:root < 48
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} 根分区将在 48h 内写满"
description: "预计剩余: {{ $value | humanizeDuration }}"
- alert: NodeDiskUsageHigh
expr: node:disk_usage_percent:root > 85
for: 5m
labels:
severity: info
annotations:
summary: "节点 {{ $labels.instance }} 磁盘使用率超过 85%"
5.2 Kubernetes 告警
groups:
- name: kubernetes_alerts
interval: 30s
rules:
- alert: PodCrashLoopBackOff
expr: kube_pod_status_phase{phase="Waiting"} > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 处于 CrashLoopBackOff"
- alert: PodRestartFrequent
expr: k8s:container_restart_changes_1h > 5
for: 2m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 在 1h 内重启 {{ $value }} 次"
- alert: PodMemoryLimitNear
expr: (container_memory_working_set_bytes{container!=""} / container_spec_memory_limit_bytes{container!=""}) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 内存接近 limit"
5.3 服务级告警
groups:
- name: service_alerts
interval: 30s
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
for: 3m
labels:
severity: critical
annotations:
summary: "服务错误率超过 5%"
description: "当前错误率: {{ $value | humanizePercentage }}"
- alert: HighLatency
expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "P99 延迟超过 2s"
6. 与 Grafana Alerting 集成
如果使用的是 Grafana Alerting(而非 Prometheus Alertmanager),有两种方式:
方式 A:Prometheus 规则 → Grafana 读取
Prometheus 保留 alerting rules 文件,Grafana 通过 Prometheus 数据源读取告警状态。这种方式告警触发和路由都在 Prometheus 侧。
方式 B:Grafana 管理规则
在 Grafana UI 中创建告警(Alert → Alert rules),直接基于 PromQL 查询。适合不想维护 YAML 文件的团队。
生产建议
- 中小规模(< 100 个告警规则):用 Grafana Alerting UI 管理,规则可视化、审批流程方便
- 大规模(> 100 个规则):用 Prometheus rules YAML,代码化(git 管理)+ CI 审查
- Recording Rules 必须在 Prometheus 侧定义,Grafana 不支持 recording rules
Grafana Alerting 关键配置
# 告警分组(避免告警风暴)
Group by: severity, namespace
# 抑制规则(高级别告警静默低级别)
If critical for node X fired → silence warnings for node X
# 静默(计划的维护窗口)
Matchers: instance =~ "10.0.0.*"
Duration: 2h
7. 常见错误与排查
| 问题 | 原因 | 解决 |
|---|---|---|
| 查询返回空 | label 拼写错误、目标未 up | 先查 up{job="xxx"} 确认数据存在 |
| 告警不触发 | for 时间不够、表达式逻辑错 | Grafana 中测试表达式,缩短 for 时间测试 |
| recording rule 不生效 | rules 文件语法错误 | promtool check rules rules.yaml 校验 |
| Grafana 面板加载慢 | 查询未用 recording rule | 将复杂查询改为 record: 规则 |
No data 在面板 | 时间范围不对、数据源切换 | 检查面板时间范围匹配 Prom 数据保留期 |
8. 面试表达框架
- 先说明 PromQL 的三种数据类型:Instant/Range/Scalar,以及各自适用场景
- 再讲常用模式:CPU/内存/磁盘/延迟的典型查询写法
- 再讲性能优化:Recording Rules 预计算、避免
rate套rate、合理使用sum by - 再讲告警设计:SLO 驱动、Multi-window Multi-burn-rate、分组抑制
- 最后讲实战经验:遇到过的告警风暴、规则误报调优、Recording Rules 迁移