Prometheus源码专题【左扬精讲】—— 监控系统 Prometheus 3.4.0 源码解析:Alerting rule 告警规则

Prometheus源码专题【左扬精讲】—— 监控系统 Prometheus 3.4.0 源码解析:Alerting rule 告警规则

https://github.com/prometheus/prometheus/blob/v3.4.0/rules/alerting.go

        在 Prometheus 的监控体系中,规则系统是实现指标预处理与告警能力的核心组件。本文将聚焦于 Alerting Rule(告警规则),从源码角度深入解析其与 Recording Rule 的区别、定义方式、执行流程、状态转换机制、通知重发策略及样本存储逻辑。

一、Alerting Rule vs Recording Rule:核心区别

Prometheus 中的规则分为两类:Recording Rule(记录规则) 和 Alerting Rule(告警规则),二者均基于 PromQL 表达式,但定位与用途截然不同。

维度Recording RuleAlerting Rule
核心用途 预计算复杂 PromQL 结果,优化查询性能 基于指标阈值触发告警,驱动通知流程
输出产物 新的时间序列(存储于 Prometheus 中) 告警状态(Pending/Firing/Inactive)及通知
配置关键字 record 定义生成的指标名 alert 定义告警名称
核心字段 无状态相关字段,仅包含 expr 和 labels 包含 for( pending 时长)、keep_firing_for(持续 firing 时长)等状态控制字段
源码对应结构体 RecordingRule (简化版,无状态管理) AlertingRule (复杂状态机,含 active 告警跟踪)

从源码角度看,两者均实现了 Rule 接口,但 AlertingRule 额外包含了告警状态管理、通知发送、样本生成等复杂逻辑(集中在 rules/alerting.go 中),而 RecordingRule 仅关注表达式计算与结果存储。

二、Alerting Rule 的定义

2.1、配置层面定义

Alerting Rule 通过 YAML 配置文件定义,核心结构如下(源自官方文档):

groups:
- name: example
  rules:
  - alert: HighRequestLatency  # 告警名称
    expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5  # 触发条件(PromQL)
    for: 10m  # 持续满足条件多久后从 Pending 转为 Firing
    keep_firing_for: 5m  # 条件不满足后,保持 Firing 状态的时长
    labels:  # 附加到告警的标签(可覆盖冲突标签)
      severity: page
    annotations:  # 告警描述信息(支持模板)
      summary: High request latency

2.2、源码层面结构

在 https://github.com/prometheus/prometheus/blob/v3.4.0/rules/alerting.go#L108 中,AlertingRule 结构体是告警规则的核心载体,关键字段如下:

// AlertingRule 从其向量表达式生成告警
type AlertingRule struct {
	// 告警的名称
	name string
	// 用于生成告警的向量表达式
	vector parser.Expr
	// 在告警从未决状态转换为触发状态之前,标签集需要在表达式输出向量中持续的时间
	holdDuration time.Duration
	// 告警在问题解决后应保持触发状态的时间量
	keepFiringFor time.Duration
	// 附加到生成的告警样本向量的额外标签
	labels labels.Labels
	// 非标识性的键/值对
	annotations labels.Labels
	// 来自全局配置的外部标签
	externalLabels map[string]string
	// 来自 --web.external-url 标志的外部 URL
	externalURL string
	// 如果旧状态已被恢复,则为 true。我们仅在恢复后才开始为 ALERT_FOR_STATE 持久化样本
	restored *atomic.Bool
	// 评估规则所花费的时间(秒)
	evaluationDuration *atomic.Duration
	// 规则最后一次评估的时间戳
	evaluationTimestamp *atomic.Time
	// 告警规则的健康状态
	health *atomic.String
	// 告警规则遇到的最后一个错误
	lastError *atomic.Error
	// 保护 `active` 映射的互斥锁
	activeMtx sync.Mutex
	// 当前活跃(未决或触发)的告警映射,由它们对应的标签集的指纹作为键
	active map[uint64]*Alert

	logger *slog.Logger

	dependenciesMutex sync.RWMutex
	dependentRules    []Rule
	dependencyRules   []Rule
}

其中,active 字段是状态管理的核心通过标签集的指纹(uint64)映射到具体的 Alert 实例,跟踪每个标签组合的告警状态

三、Alerting Rule 的执行流程

        告警规则的执行由 Rule Manager 驱动,定期(按 evaluation_interval 配置)在规则组(Group)中批量执行。核心流程在 Group.Eval 方法(https://github.com/prometheus/prometheus/blob/v3.4.0/rules/group.go)中实现,针对 AlertingRule 的关键步骤如下:

    • 表达式计算:调用 AlertingRule.Eval 方法,执行 vector 字段对应的 PromQL 表达式,获取当前满足条件的样本集合。
    • 状态更新:对比当前样本与 active 中记录的历史告警,更新每个标签集的状态(Pending/Firing/Inactive)。
    • 样本生成:生成 ALERTS 和 ALERTS_FOR_STATE 指标样本(用于跟踪告警状态)。
    • 通知发送:调用 sendAlerts 方法,根据状态变化触发告警通知(或重发)。
    • 结果存储:将生成的样本写入 Prometheus 存储(通过 Appender 接口)。

四、告警状态转换:从 Inactive 到 Firing 的生命周期

Prometheus 告警存在三种状态,定义于 https://github.com/prometheus/prometheus/blob/v3.4.0/rules/alerting.go 中:

type AlertState int

const (
    StateInactive AlertState = iota  // 非活跃:不满足条件,或已恢复
    StatePending                     // 待触发:满足条件但未达到 for 时长
    StateFiring                      // 触发中:满足条件且超过 for 时长
)

4.1、状态转换逻辑

    • Inactive → Pending:当 PromQL 表达式首次返回某标签集时,若规则配置了 for,则该标签集进入 Pending 状态,记录 ActiveAt 时间戳。
    • Pending → Firing:若标签集在 Pending 状态持续时间超过 holdDuration(即 当前评估时间 - ActiveAt ≥ holdDuration),则转为 Firing 状态,记录 FiredAt 时间戳。
    • Firing/Pending → Inactive:
      • 若表达式不再返回该标签集,且未配置 keepFiringFor,则直接转为 Inactive,记录 ResolvedAt。
      • 若配置了 keepFiringFor,则保持 Firing 状态直至 当前评估时间 - ResolvedAt ≥ keepFiringFor,再转为 Inactive。

4.2、影响状态转换的关键字段

    • holdDuration(for):控制从 Pending 到 Firing 的延迟,避免瞬时波动触发告警。
    • keepFiringFor:控制条件不满足后 Firing 状态的延续时间,防止告警 “抖动”(如短暂数据缺失导致误判恢复)。
    • ActiveAt/FiredAt/ResolvedAt:Alert 结构体中的时间戳字段,用于计算状态持续时长。

五、告警通知与重发机制 

告警触发后,Prometheus 会将通知发送给 Alertmanager,核心逻辑在 AlertingRule.sendAlerts 方法(https://github.com/prometheus/prometheus/blob/v3.4.0/rules/alerting.go#L593)中。

5.1、通知触发条件

needsSending 方法定义了通知发送的判断逻辑:

func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool {
    if a.State == StatePending {  // Pending 状态不发送通知
        return false
    }
    // 若告警已恢复(ResolvedAt 在 LastSentAt 之后),需发送恢复通知
    if a.ResolvedAt.After(a.LastSentAt) {
        return true
    }
    // 若距离上次发送已超过重发间隔,需重发
    return a.LastSentAt.Add(resendDelay).Before(ts)
}

needsSending 方法定义了通知是否需要发送的核心判断逻辑,仅当满足以下条件之一时,才会触发通知请求:

posted @ 2025-10-15 16:22  左扬  阅读(17)  评论(0)    收藏  举报