一、概述
1.1 背景介绍
在大规模微服务架构下,Prometheus 告警系统往往会陷入一个尴尬的境地:告警太多,运维团队开始选择性忽略;告警太少,真正的故障又可能漏掉。我在某电商平台负责监控体系建设时,团队每天要处理超过 2000 条告警,其中 70% 以上是重复的、关联的或者短暂抖动产生的噪声。
Recording Rules 是 Prometheus 提供的预计算机制,可以将复杂的查询表达式预先计算并存储为新的时间序列。通过合理设计 Recording Rules,我们不仅能显著降低 PromQL 查询压力,更重要的是可以实现告警聚合、去重、平滑,从根本上减少告警噪声。
1.2 技术特点
• 预计算优化:将复杂的聚合查询预先计算,避免告警规则重复计算相同的表达式,降低 Prometheus 负载 • 时间窗口平滑:通过 avg_over_time、max_over_time等函数消除瞬时抖动,避免单点毛刺触发告警• 多维度聚合:支持按 namespace、service、pod 等维度聚合指标,实现告警收敛 • 层级化告警:基于 Recording Rules 构建指标层级,实现从基础指标到业务指标的逐层抽象
1.3 适用场景
• 场景一:大规模 Kubernetes 集群监控,Pod 数量超过 1000,原生指标产生的告警风暴需要收敛 • 场景二:微服务架构下,服务间依赖复杂,需要将底层告警聚合为服务级别的健康状态 • 场景三:业务高峰期流量抖动频繁,需要平滑处理避免误告警 • 场景四:多租户环境下,需要按租户/业务线聚合告警,避免一个故障触发上百条告警
1.4 环境要求
二、详细步骤
2.1 准备工作
◆ 2.1.1 现状分析
在动手之前,先要搞清楚当前告警噪声的分布。我习惯用下面这个查询来分析过去一周的告警触发情况:
# 查看 Prometheus 配置
kubectl get configmap prometheus-server -n monitoring -o yaml
# 分析告警触发频次(在 Prometheus UI 执行)
# 统计过去7天各告警规则触发次数
count_over_time(ALERTS{alertstate="firing"}[7d])
# 查看当前活跃告警数量
count(ALERTS{alertstate="firing"})
◆ 2.1.2 识别噪声来源
根据我的经验,告警噪声主要来自以下几类:
# 检查高频告警(每分钟触发超过10次的规则)
# 在 Prometheus 执行
topk(20, count by (alertname) (count_over_time(ALERTS{alertstate="firing"}[1h])))
# 检查短暂告警(持续时间小于5分钟的)
# 这类告警往往是抖动产生的噪声
通常会发现以下几类高频噪声:
• CPU/内存使用率瞬时超阈值 • Pod 重启计数器误报 • 网络延迟抖动 • 磁盘 IO 突增
2.2 核心配置
◆ 2.2.1 Recording Rules 基础结构
# 文件路径:/etc/prometheus/rules/recording_rules.yml
groups:
-name:node_recording_rules
interval:30s# 计算间隔,根据需求调整
rules:
# 规则定义
-record:job:node_cpu_utilization:avg5m
expr:|
1 - avg by (job, instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
)
说明:interval 参数控制 Recording Rules 的计算频率。设置过小会增加计算压力,过大则可能错过关键变化。一般建议设置为告警评估间隔的一半。
◆ 2.2.2 CPU 使用率平滑处理
这是最常见的噪声来源。原始告警规则可能是这样的:
# 原始告警规则(噪声大)
-alert:HighCPUUsage
expr:100-(avgby(instance)(rate(node_cpu_seconds_total{mode="idle"}[1m]))*100)>80
for:1m
使用 Recording Rules 优化后:
# recording_rules.yml
groups:
-name:cpu_smoothing
interval:30s
rules:
# 第一层:5分钟平均 CPU 使用率
-record:instance:node_cpu_utilization:avg5m
expr:|
1 - avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
)
# 第二层:基于5分钟均值的15分钟最大值
# 这样可以捕获持续性高负载,过滤掉短暂峰值
-record:instance:node_cpu_utilization:max15m_avg5m
expr:|
max_over_time(instance:node_cpu_utilization:avg5m[15m])
# 第三层:按集群聚合的 CPU 使用率
-record:cluster:node_cpu_utilization:avg
expr:|
avg(instance:node_cpu_utilization:avg5m)
参数说明:
• avg5m:使用 5 分钟窗口计算平均值,消除秒级抖动• max15m_avg5m:在 5 分钟均值基础上取 15 分钟最大值,只有持续性高负载才会触发• cluster:前缀:表示集群级别聚合,便于识别层级
◆ 2.2.3 内存使用率分层聚合
groups:
-name:memory_recording_rules
interval:30s
rules:
# 节点级别内存使用率
-record:instance:node_memory_utilization:ratio
expr:|
1 - (
node_memory_MemAvailable_bytes
/ node_memory_MemTotal_bytes
)
# 应用级别内存使用(Kubernetes 环境)
-record:namespace_pod:container_memory_usage:sum
expr:|
sum by (namespace, pod) (
container_memory_working_set_bytes{container!="", container!="POD"}
)
# 服务级别内存使用率
-record:namespace_service:memory_utilization:avg5m
expr:|
avg by (namespace, service) (
avg_over_time(
container_memory_working_set_bytes{container!=""}[5m]
)
) / avg by (namespace, service) (
kube_pod_container_resource_limits{resource="memory"}
)
◆ 2.2.4 请求错误率聚合
对于微服务场景,HTTP 错误率告警是另一个噪声重灾区:
groups:
-name:http_error_rate_rules
interval:15s
rules:
# 服务级别错误率(5分钟窗口)
-record:service:http_requests_total:rate5m
expr:|
sum by (namespace, service) (
rate(http_requests_total[5m])
)
-record:service:http_requests_errors:rate5m
expr:|
sum by (namespace, service) (
rate(http_requests_total{status=~"5.."}[5m])
)
# 错误率计算
-record:service:http_error_rate:ratio5m
expr:|
service:http_requests_errors:rate5m
/ service:http_requests_total:rate5m
# 加权错误率:流量越大权重越高
# 避免低流量服务的单个错误触发高错误率告警
-record:service:http_error_rate:weighted5m
expr:|
(service:http_requests_errors:rate5m + 1)
/ (service:http_requests_total:rate5m + 10)
2.3 启动和验证
◆ 2.3.1 配置热加载
# 检查配置语法
promtool check rules /etc/prometheus/rules/recording_rules.yml
# 热加载 Prometheus 配置
curl -X POST http://localhost:9090/-/reload
# 或者发送 SIGHUP 信号
kill -HUP $(pgrep prometheus)
◆ 2.3.2 验证 Recording Rules 生效
# 在 Prometheus UI 查询新生成的指标
# 应该能看到数据
instance:node_cpu_utilization:avg5m
# 检查 Recording Rules 状态
curl -s http://localhost:9090/api/v1/rules | jq '.data.groups[] | select(.name=="cpu_smoothing")'
# 预期输出应显示规则状态为 "health": "ok"
三、示例代码和配置
3.1 完整配置示例
◆ 3.1.1 完整的 Recording Rules 配置
# 文件路径:/etc/prometheus/rules/recording_rules.yml
groups:
# ============ 基础设施层 ============
-name:infrastructure_recording_rules
interval:30s
rules:
# CPU
-record:instance:node_cpu_utilization:avg5m
expr:1-avgby(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m]))
-record:instance:node_cpu_utilization:max_avg5m_over_15m
expr:max_over_time(instance:node_cpu_utilization:avg5m[15m])
# Memory
-record:instance:node_memory_utilization:ratio
expr:1-(node_memory_MemAvailable_bytes/node_memory_MemTotal_bytes)
-record:instance:node_memory_utilization:avg5m
expr:avg_over_time(instance:node_memory_utilization:ratio[5m])
# Disk
-record:instance:node_disk_utilization:ratio
expr:|
1 - (
node_filesystem_avail_bytes{fstype=~"ext4|xfs"}
/ node_filesystem_size_bytes{fstype=~"ext4|xfs"}
)
# Network
-record:instance:node_network_receive:rate5m
expr:sumby(instance)(rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*|br.*"}[5m]))
-record:instance:node_network_transmit:rate5m
expr:sumby(instance)(rate(node_network_transmit_bytes_total{device!~"lo|veth.*|docker.*|br.*"}[5m]))
# ============ Kubernetes 层 ============
-name:kubernetes_recording_rules
interval:30s
rules:
# Pod 资源使用聚合到 namespace
-record:namespace:container_cpu_usage:sum
expr:|
sum by (namespace) (
rate(container_cpu_usage_seconds_total{container!="", container!="POD"}[5m])
)
-record:namespace:container_memory_usage:sum
expr:|
sum by (namespace) (
container_memory_working_set_bytes{container!="", container!="POD"}
)
# Pod 重启率(1小时窗口,避免单次重启告警)
-record:namespace_pod:container_restarts:increase1h
expr:|
increase(kube_pod_container_status_restarts_total[1h])
# Deployment 可用性
-record:namespace_deployment:replicas_unavailable:ratio
expr:|
kube_deployment_status_replicas_unavailable
/ kube_deployment_spec_replicas
# ============ 应用层 ============
-name:application_recording_rules
interval:15s
rules:
# HTTP 请求速率
-record:service:http_requests:rate5m
expr:sumby(namespace,service,method)(rate(http_requests_total[5m]))
# HTTP 错误率
-record:service:http_errors:rate5m
expr:sumby(namespace,service)(rate(http_requests_total{status=~"5.."}[5m]))
# HTTP P99 延迟
-record:service:http_latency_p99:5m
expr:|
histogram_quantile(0.99,
sum by (namespace, service, le) (
rate(http_request_duration_seconds_bucket[5m])
)
)
# 服务健康评分(综合指标)
-record:service:health_score:5m
expr:|
(
1 - clamp_max(service:http_errors:rate5m / service:http_requests:rate5m, 1)
) * 0.4
+
(
1 - clamp_max(service:http_latency_p99:5m / 2, 1)
) * 0.3
+
(
clamp_max(service:http_requests:rate5m / 100, 1)
) * 0.3
◆ 3.1.2 基于 Recording Rules 的告警规则
# 文件路径:/etc/prometheus/rules/alert_rules.yml
groups:
-name:infrastructure_alerts
rules:
# 使用预计算指标,更稳定
-alert:NodeHighCPU
expr:instance:node_cpu_utilization:max_avg5m_over_15m>0.85
for:5m
labels:
severity:warning
annotations:
summary:"节点 {{ $labels.instance }} CPU 持续高负载"
description:"15分钟内 CPU 使用率峰值超过 85%,当前值 {{ $value | humanizePercentage }}"
-alert:NodeHighMemory
expr:instance:node_memory_utilization:avg5m>0.9
for:10m
labels:
severity:warning
annotations:
summary:"节点 {{ $labels.instance }} 内存使用率过高"
description:"内存使用率持续超过 90%"
-name:application_alerts
rules:
# 基于聚合指标的服务健康告警
-alert:ServiceUnhealthy
expr:service:health_score:5m<0.6
for:5m
labels:
severity:critical
annotations:
summary:"服务 {{ $labels.namespace }}/{{ $labels.service }} 健康状态异常"
description:"服务健康评分低于 0.6,当前值 {{ $value }}"
# Pod 频繁重启(使用1小时窗口)
-alert:PodFrequentRestart
expr:namespace_pod:container_restarts:increase1h>3
for:0m
labels:
severity:warning
annotations:
summary:"Pod {{ $labels.namespace }}/{{ $labels.pod }} 频繁重启"
description:"过去1小时重启 {{ $value }} 次"
3.2 实际应用案例
◆ 案例一:电商大促告警收敛
场景描述:双11大促期间,订单服务 QPS 从日常 1000 飙升到 50000,原有的错误率告警(错误数/总请求数 > 1%)频繁触发,因为即使错误率只有 0.5%,绝对错误数也达到了 250/秒。
实现代码:
# Recording Rules
groups:
-name:ecommerce_rules
rules:
# 动态基线:使用过去1小时的错误率作为基线
-record:service:http_error_rate:baseline1h
expr:|
avg_over_time(
(
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
/ sum by (service) (rate(http_requests_total[5m]))
)[1h:]
)
# 当前错误率与基线的比值
-record:service:http_error_rate:ratio_to_baseline
expr:|
(
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
/ sum by (service) (rate(http_requests_total[5m]))
)
/ service:http_error_rate:baseline1h
# 告警规则
-alert:ServiceErrorRateSpike
expr:service:http_error_rate:ratio_to_baseline>3
for:5m
annotations:
summary:"服务 {{ $labels.service }} 错误率异常飙升"
description:"当前错误率是基线的 {{ $value | humanize }} 倍"
运行结果:
大促期间告警数量对比:
- 优化前:每小时平均 150+ 条告警
- 优化后:每小时平均 5-10 条告警
- 误告警率:从 85% 降低到 15%
◆ 案例二:多租户环境告警聚合
场景描述:SaaS 平台有 200+ 租户,每个租户有独立的命名空间。当某个底层节点故障时,原有配置会触发 200+ 条 Pod 不可用告警。
实现步骤:
1. 创建租户级别的聚合指标 2. 实现告警收敛,只告警受影响的节点,附带影响的租户列表 3. 配合 Alertmanager 的 group_by 实现进一步收敛
# Recording Rules
groups:
-name:tenant_aggregation
rules:
# 按租户聚合不可用 Pod 数量
-record:tenant:pods_unavailable:count
expr:|
count by (tenant) (
kube_pod_status_phase{phase!="Running", phase!="Succeeded"}
)
# 租户服务可用性
-record:tenant:service_availability:ratio
expr:|
sum by (tenant) (kube_deployment_status_replicas_available)
/ sum by (tenant) (kube_deployment_spec_replicas)
# 故障影响范围
-record:node:affected_tenants:count
expr:|
count by (node) (
count by (node, tenant) (
kube_pod_info * on(pod, namespace) group_left(tenant)
kube_namespace_labels
)
)
四、最佳实践和注意事项
4.1 最佳实践
◆ 4.1.1 Recording Rules 命名规范
遵循一致的命名规范对于维护大规模 Recording Rules 至关重要:
# 命名格式:level:metric_name:aggregation_window
# level: 聚合级别,如 instance, namespace, cluster
# metric_name: 指标名称
# aggregation_window: 时间窗口和聚合方式
# 好的命名示例
instance:node_cpu_utilization:avg5m
namespace:container_memory:sum
cluster:http_requests:rate15m
# 避免的命名方式
cpu_high # 缺少层级和时间窗口信息
recording_rule_1 # 无意义的名称
◆ 4.1.2 分层设计
# 三层架构设计
# 第一层:原始指标清洗和标准化
-record:instance:node_cpu_seconds:rate5m
expr:rate(node_cpu_seconds_total[5m])
# 第二层:指标聚合
-record:instance:node_cpu_utilization:avg5m
expr:1-avgby(instance)(instance:node_cpu_seconds:rate5m{mode="idle"})
# 第三层:业务指标
-record:service:availability:ratio
expr:|
(
sum(instance:node_cpu_utilization:avg5m < 0.9)
/ count(instance:node_cpu_utilization:avg5m)
)
◆ 4.1.3 性能优化
• 控制 Recording Rules 数量:每增加一条规则,就会产生新的时间序列 # 检查 Recording Rules 产生的时序数量
prometheus_tsdb_head_series
# 建议:单个 Prometheus 实例的 Recording Rules 产生的时序不超过总时序的 20%• 合理设置 interval:根据指标变化频率设置 • 基础设施指标:30s • 应用指标:15s • 业务指标:60s • 使用 limit 防止基数爆炸: -record:topk_services:http_requests:rate5m
expr:topk(100,sumby(service)(rate(http_requests_total[5m])))
◆ 4.1.4 高可用配置
• 多副本一致性:Recording Rules 在多个 Prometheus 副本上执行,确保配置完全相同 • 告警去重:配合 Alertmanager 的 group_by和inhibit_rules进一步去重• 联邦集群:在联邦架构中,Recording Rules 应该在被采集端执行,减少联邦查询压力
# Alertmanager 配置配合
route:
group_by: ['alertname', 'cluster', 'service']
group_wait:30s
group_interval:5m
repeat_interval:4h
inhibit_rules:
# 节点故障时抑制该节点上的所有 Pod 告警
-source_match:
alertname:NodeDown
target_match_re:
alertname:Pod.*
equal: ['node']
4.2 注意事项
◆ 4.2.1 配置注意事项
警告:Recording Rules 配置错误可能导致 Prometheus 无法启动或产生错误数据
• 注意事项一:Recording Rules 的 record字段必须是合法的指标名称,不能包含特殊字符• 注意事项二:避免 Recording Rules 之间的循环依赖,虽然 Prometheus 会检测,但可能导致计算延迟 • 注意事项三:修改已有 Recording Rules 的表达式时,注意下游依赖的告警规则可能需要同步调整阈值
◆ 4.2.2 常见错误
◆ 4.2.3 兼容性问题
• 版本兼容: limit功能需要 Prometheus 2.28+,低版本需要在表达式中使用topk替代• 平台兼容:使用 Prometheus Operator 时,Recording Rules 需要通过 PrometheusRule CRD 管理 • 组件依赖:Recording Rules 依赖的源指标(如 node_exporter、kube-state-metrics)版本变更可能导致表达式失效
