Prometheus Alertmanager 详解及实战

一、概述

Alertmanager 是 Prometheus 生态的专属告警调度组件,承接 Prometheus Server 推送的告警数据,实现告警的去重、分组、抑制、静默、分级分发。在 Prometheus 架构中,告警由两个相互独立的组件配合完成:

  • Prometheus Server:根据配置的告警规则周期性计算指标,当规则触发时生成告警并推送给 Alertmanager。

  • Alertmanager:专注于告警的聚合、路由、静默与通知,最终通过接收器发送给指定用户。

举个典型场景:当 10 个节点同时宕机,如果直接发送 10 条通知,运维人员会被信息淹没。Alertmanager 通过分组机制将相同类型的告警合并为一条通知,显著提升可读性。

核心区别:Prometheus 负责“判定告警是否产生”,Alertmanager 负责“管控告警如何发送”。

二、Alertmanager 核心架构与工作流程

2.1 核心功能

功能 说明
告警分组(Grouping) 将同一类型、同一主机、同一业务的多条告警合并为一条批量通知,避免告警轰炸
告警去重(Deduplication) 相同重复告警自动合并,避免短时间内频繁推送
告警抑制(Inhibition) 重大故障触发时,抑制衍生的次要告警(如主机宕机后抑制该主机所有容器告警)
告警静默(Silences) 临时屏蔽指定告警,适配维护升级、停机演练场景
路由分发(Routing) 根据告警标签,将不同级别、不同业务的告警推送至不同接收渠道(钉钉、企业微信、邮件、短信)
告警防抖 支持配置等待时间、持续告警时间,避免瞬时抖动触发误告警

2.2 完整告警链路

deepseek_mermaid_20260809_fe0434

deepseek_mermaid_20260809_faf2c4

2.3 告警的两种状态(Prometheus 侧)

  • Pending(待定):指标触发告警规则,但未达到配置的持续时间(for),暂时不推送,用于过滤瞬时异常。

  • Firing(触发):异常持续时间满足规则条件,正式激活告警,推送至 Alertmanager。

2.4 完整告警生命周期

一个告警从产生到最终处理完成,会经历以下5个阶段:

image

  1. 规则加载:Prometheus 启动时或配置重载时加载告警规则文件。

  2. 规则评估:Prometheus 按照 evaluation_interval(默认 1 分钟)周期性执行告警表达式。

  3. 状态转换:告警从 InactivePendingFiringResolved

  4. 告警推送:Prometheus 将 Firing 状态的告警推送给 Alertmanager。

  5. 告警处理:Alertmanager 对告警进行分组、抑制、静默后,通过配置的渠道发送通知。

2.5 Alertmanager 内部处理流水线

Alertmanager接收到Prometheus推送的告警后,会按照严格的顺序进行处理:

接收告警 → 告警去重 → 分组聚合 → 抑制检查 → 静默检查 → 路由匹配 → 模板渲染 → 通知发送

三、Prometheus Server 端:告警规则评估逻辑

这是告警的核心生成阶段,完全由Prometheus Server独立完成。

3.1 告警规则基本结构

[root@Ans-prometheus ~]# cat /data/prometheus-3.12.0/prometheus.yml |grep rules
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
  - "./rules/node_rules.yml"   #规则配置文件
  # - "second_rules.yml"
[root@Ans-prometheus ~]# cat /data/prometheus-3.12.0/rules/node_rules.yml
groups:
- name: node_alerts
  interval: 30s  # 该组规则的评估间隔,覆盖全局evaluation_interval
  rules:
  - alert: HighCPUUsage
    expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
    for: 5m
    labels:
      severity: warning
      team: infra
    annotations:
      summary: "Instance {{ $labels.instance }} CPU usage is high"
      description: "CPU usage is above 80% for 5 minutes (current value: {{ $value | printf \"%.2f\" }}%)"
      runbook_url: "https://wiki.example.com/runbooks/high-cpu-usage"

3.2 核心评估逻辑

Prometheus会周期性地执行所有告警规则中的expr表达式。如果表达式返回任何时间序列,则认为告警条件满足。

关键参数详解

  • expr:告警触发条件,是一个PromQL表达式。这是告警规则的核心。

  • for:告警持续时间。这是最容易被误解的参数!

    • ❌ 错误理解:"每隔多久检查一次"

    • ✅ 正确理解:"告警条件必须连续满足多长时间"才会从Pending变为Firing

    • 如果在for时间内,表达式有一次不返回结果,计时器会完全重置

  • labels:附加到告警上的标签,用于Alertmanager的路由、分组和抑制

  • annotations:告警的描述性信息,不会影响告警的唯一性,通常包含摘要、详细描述和处理指南

评估过程详解

HighCPUUsage规则(for: 5mevaluation_interval: 1m)为例:

时间点 表达式结果 连续满足次数 状态变化 动作
00:00 85% (>80) 1 Inactive → Pending 开始计数
00:01 88% (>80) 2 Pending 继续计数
00:02 82% (>80) 3 Pending 继续计数
00:03 78% (≤80) 0 (重置) Pending → Inactive 计数归零
00:04 90% (>80) 1 Inactive → Pending 重新开始计数
00:05 92% (>80) 2 Pending 继续计数
00:06 91% (>80) 3 Pending 继续计数
00:07 89% (>80) 4 Pending 继续计数
00:08 87% (>80) 5 Pending → Firing 立即推送告警
00:09 86% (>80) 6 Firing 不再重复推送(已触发)

重要结论:在最理想情况下,一个配置了for: 5m的告警,从条件首次满足到实际触发,至少需要5分钟

3.3 告警状态转换

Prometheus中的每个告警实例(由唯一的标签集标识)都有且只有以下四种状态:

  • Inactive(非活跃):告警条件不满足,系统正常运行

  • Pending(待触发):告警条件已满足,但还未达到for指定的持续时间

  • Firing(已触发):告警条件连续满足了for指定的时间,已推送给Alertmanager

  • Resolved(已恢复):告警条件不再满足,已发送恢复通知

3.4 告警推送逻辑

  • Prometheus只会将Firing状态的告警推送给Alertmanager

  • 推送频率由evaluation_interval控制,默认每分钟一次

  • 当告警条件不再满足时,Prometheus会向Alertmanager发送一个Resolved通知

  • Resolved通知会在告警恢复后,**再等待一个evaluation_interval**发送,以避免因指标抖动导致的频繁恢复/触发

四、Alertmanager 核心功能详解

4.1 分组机制(Grouping)

分组是Alertmanager最基础也是最重要的降噪手段。它通过group_by标签将具有相同特征的告警合并为一个通知,避免告警风暴。

核心时间参数

参数 含义 推荐值
group_wait 首次告警等待时间,用于收集同组的其他告警后再发送 10-30s
group_interval 同一组内有新告警加入时,需要等待多久再发送更新后的通知 5m
repeat_interval 如果告警一直处于Firing状态,每隔多久重复发送一次通知 1-4h

配置示例

route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 2h

该配置将按告警名称、集群和服务维度分组:

  • 收到第一个告警后,等待30秒收集同组的其他告警

  • 30秒后发送第一个包含所有已收集告警的通知

  • 如果在5分钟内有新的告警加入该组,会在5分钟后发送更新后的通知

  • 如果告警一直未恢复,每2小时重复发送一次通知

4.2 抑制规则(Inhibition)

抑制规则用于定义告警间的依赖关系——当某个"根源告警"触发时,自动抑制其引发的"衍生告警"。这是解决故障雪崩式告警的最有效手段。

配置示例

inhibit_rules:
  - source_match:
      severity: 'critical'
      alertname: 'NodeDown'
    target_match:
      severity: 'warning'
    equal: ['instance']

这条规则的含义是:当某个节点宕机(severity=criticalalertname=NodeDown)时,同一实例上所有severity=warning级别的告警都会被自动抑制。

典型应用场景

  • 节点宕机 → 抑制该节点上的所有服务告警(CPU、内存、磁盘、端口等)

  • 数据库主节点不可用 → 抑制从节点延迟告警

  • 网络设备故障 → 抑制该设备下的所有链路和服务告警

  • Kubernetes集群节点NotReady → 抑制该节点上所有Pod的告警

4.3 静默(Silence)

静默与分组和抑制的核心区别在于:分组和抑制是在配置文件中静态定义的,而静默是通过Alertmanager的Web UI或API动态创建的,适合临时屏蔽场景。

使用场景

  • 计划内的服务器升级和维护

  • 已知问题正在处理中,暂时不需要重复告警

  • 误报告警的临时屏蔽

  • 非工作时间的低优先级告警屏蔽

通过API创建静默

curl -X POST http://alertmanager:9093/api/v1/silences \
  -H "Content-Type: application/json" \
  -d '{
    "matchers": [
      {"name": "alertname", "value": "DiskFull", "isRegex": false},
      {"name": "mountpoint", "value": "/var", "isRegex": false},
      {"name": "instance", "value": "server-01.example.com", "isRegex": false}
    ],
    "startsAt": "2024-07-01T08:00:00Z",
    "endsAt": "2024-07-02T08:00:00Z",
    "createdBy": "ops-team",
    "comment": "Scheduled backup operation on /var partition"
  }'

4.4 三大机制对比

机制 配置位置 典型用途 生效方式 生命周期
分组 配置文件 合并同类告警,降低通知量 静态,需更新配置 长期
抑制 配置文件 定义告警优先级和依赖关系 静态,需更新配置 长期
静默 Web UI/API 临时屏蔽(如计划内维护) 动态,即时生效 临时

五、Alertmanager 核心配置详解

Alertmanager 配置文件 alertmanager.yml 分为四大核心模块:globalroutereceiversinhibit_rules

5.1 global 全局配置

定义全局参数,如告警恢复超时、SMTP 配置等。

global:
  resolve_timeout: 5m               # 告警恢复后,等待多久标记为已解决并推送恢复通知
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: 'alertmanager@example.com'
  smtp_auth_username: 'alertmanager'
  smtp_auth_password: 'password'    # 生产环境建议使用环境变量或外部文件

5.2 route 路由配置(核心)

路由是Alertmanager配置中最核心的部分,它采用树形结构实现多级匹配,将不同的告警发送给不同的接收者。

5.2.1 完整配置示例

route:
  receiver: 'default-receiver'
  group_by: ['alertname', 'datacenter']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 2h
  
  routes:
    # 子路由1:critical级别告警发送给紧急团队
    - match:
        severity: 'critical'
      receiver: 'emergency-team'
      continue: false   # 匹配成功后不再继续匹配同级其他路由
      
    # 子路由2:warning级别,按服务再分流
    - match:
        severity: 'warning'
      receiver: 'warning-team'
      routes:            # 二级子路由
        - match:
            service: 'database'
          receiver: 'db-warning'
        - match:
            service: 'frontend'
          receiver: 'frontend-warning'
          
    # 子路由3:使用正则匹配
    - match_re:
        service: 'payment.*'
      receiver: 'finance-team'
      continue: true  # 继续匹配后续路由,实现告警广播

5.2.2 路由匹配机制

  • 匹配顺序:从上到下依次匹配子路由,先匹配到的规则优先生效

  • 深度优先:匹配到一个子路由后,会先递归匹配它的所有子路由

  • continue 标志:决定当一个告警匹配了当前路由节点后,是否继续在同级(同一父节点下的兄弟路由)中进行后续匹配。

    • continue: false(默认值):匹配成功后,立即停止遍历当前父节点下的其他兄弟路由。告警只会被发送给当前匹配到的路由所指定的接收器(以及该路由的祖先节点,如果祖先没有设置 continue)。

    • continue: true:匹配成功后,继续尝试匹配同级后续的兄弟路由。这样一条告警可以同时发送给多个接收器。

continue: true 的典型使用场景:当你希望一条告警同时通知多个团队时,例如支付系统的告警需要同时通知运维团队和财务团队。

5.2.3 生产级三级路由结构设计

生产环境建议采用以下分级设计,既能保证关键告警的及时处理,又能实现精细化的告警分发:

根路由 (receiver: default-admin)
├── severity: critical
│   ├── service: database → db-oncall
│   ├── service: frontend → frontend-oncall
│   └── service: backend → backend-oncall
├── severity: warning
│   ├── service: database → db-team
│   ├── service: frontend → frontend-team
│   └── service: backend → backend-team
├── severity: info
│   └── (skip or send to slack channel)
└── (default fallback)

5.3 接收器(Receiver)实战

接收器定义了告警最终通过什么渠道发送给谁。Alertmanager支持多种通知渠道,包括邮件、钉钉、企业微信、Slack、短信、电话和Webhook。

5.3.1 邮件通知配置

global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: 'alertmanager@example.com'
  smtp_auth_username: 'alertmanager@example.com'
  smtp_auth_password: 'your-email-password'
  smtp_require_tls: true

receivers:
  - name: 'email-notify'
    email_configs:
      - to: 'ops-team@example.com'
        send_resolved: true
        headers:
          Subject: '[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}'
        html: |
          <h3>{{ .CommonAnnotations.summary }}</h3>
          <p>{{ .CommonAnnotations.description }}</p>
          <p>告警时间: {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</p>

5.3.2 钉钉通知配置

钉钉是国内企业最常用的通知渠道之一。Alertmanager通过Webhook与钉钉机器人集成。

注意:钉钉Webhook只接受POST请求,GET请求会返回43002错误。

receivers:
  - name: 'dingtalk-notify'
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=your-dingtalk-token'
        send_resolved: true
        http_config:
          tls_config:
            insecure_skip_verify: false

为了获得更好的钉钉通知格式,建议使用专门的钉钉告警转发服务,如prometheus-webhook-dingtalk。

5.3.3 企业微信通知配置

企业微信是国内企业广泛使用的办公沟通工具。Alertmanager通过Webhook与企业微信机器人集成。

receivers:
  - name: 'wecom-notify'
    webhook_configs:
      - url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-wecom-key'
        send_resolved: true
        http_config:
          tls_config:
            insecure_skip_verify: false

企业微信机器人支持markdown格式的消息,可以创建更美观的告警通知。

5.3.4 Slack通知配置

receivers:
  - name: 'slack-notify'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
        channel: '#alerts'
        title: '[{{ .Status | toUpper }}] {{ .CommonLabels.alertname }}'
        text: |
          *告警详情*
          > 级别: {{ .CommonLabels.severity }}
          > 实例: {{ .CommonLabels.instance }}
          > 摘要: {{ .CommonAnnotations.summary }}
          > 时间: {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }} (UTC+8)
        send_resolved: true

5.3.5 Webhook通用集成

Webhook是最灵活的通知方式,可以对接任意自定义告警处理系统,如内部工单系统、短信网关、电话告警系统等。

receivers:
  - name: 'webhook-processor'
    webhook_configs:
      - url: 'http://alert-processor:8080/webhook'
        send_resolved: true
        http_config:
          basic_auth:
            username: 'alertmanager'
            password: 'secure-password'
          tls_config:
            ca_file: '/etc/alertmanager/ca.crt'

5.3.6 多接收器组合

Alertmanager支持在一个receiver中配置多个通知渠道,实现告警的多渠道同时发送:

receivers:
  - name: 'critical-alerts'
    email_configs:
      - to: 'oncall@example.com'
    webhook_configs:
      - url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-wecom-key'
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=your-dingtalk-token'

5.4 inhibit_rules 抑制规则

生产降噪核心。典型场景:主机宕机(源告警)→ 抑制该主机所有容器、端口、服务告警(目标告警)。

inhibit_rules:
- source_match:
    severity: 'critical'
    alertname: 'NodeDown'
  target_match:
    severity: 'warning'
  equal: ['instance']               # 要求源和目标告警的 instance 标签值相等

六、通知模板(Templating)

Alertmanager使用Go模板引擎定制通知内容,可以极大提升通知的信息密度和可读性。

6.1 企业微信模板示例

首先在Alertmanager配置文件中指定模板路径:

templates:
  - '/etc/alertmanager/templates/*.tmpl'

然后创建模板文件wecom.tmpl

{{ define "wecom.message" }}
{
  "msgtype": "markdown",
  "markdown": {
    "content": "{{ if eq .Status \"firing\" }}<font color=\"#FF0000\">🔴 告警触发</font>{{ else }}<font color=\"#008000\">🟢 告警恢复</font>{{ end }}

**告警名称**: {{ .CommonLabels.alertname }}
**告警级别**: {{ .CommonLabels.severity }}
**告警实例**: {{ .CommonLabels.instance }}
{{ if .CommonLabels.service }}**服务名称**: {{ .CommonLabels.service }}{{ end }}

**告警摘要**: {{ .CommonAnnotations.summary }}
**告警详情**: {{ .CommonAnnotations.description }}

**触发时间**: {{ .StartsAt.Local.Format "2006-01-02 15:04:05" }} (UTC+8)
{{ if eq .Status "resolved" }}
**恢复时间**: {{ .EndsAt.Local.Format "2006-01-02 15:04:05" }} (UTC+8)
{{ end }}

**Grafana主机面板**: [点击跳转](http://10.0.0.42:3000/d/rYdddlPWk/node-exporter-full?orgId=1&from=now-24h&to=now&timezone=browser&var-ds_prometheus=afpljo037x62oa&var-job={{ .CommonLabels.job }}&var-node={{ .CommonLabels.instance }}&refresh=1m)

{{ if .CommonAnnotations.runbook_url }}**处理手册**: [点击打开]({{ .CommonAnnotations.runbook_url }}){{ end }}"
  }
}
{{ end }}

{{ define "wecom.critical.message" }}
{
  "msgtype": "markdown",
  "markdown": {
    "content": "<font color=\"#FF0000\">🚨 紧急告警触发 🚨</font>

**告警名称**: {{ .CommonLabels.alertname }}
**告警级别**: <font color=\"#FF0000\">CRITICAL</font>
**告警实例**: {{ .CommonLabels.instance }}

**告警摘要**: {{ .CommonAnnotations.summary }}
**告警详情**: {{ .CommonAnnotations.description }}

**触发时间**: {{ .StartsAt.Local.Format "2006-01-02 15:04:05" }} (UTC+8)

**Grafana主机面板**: [点击跳转](http://10.0.0.42:3000/d/rYdddlPWk/node-exporter-full?orgId=1&from=now-24h&to=now&timezone=browser&var-ds_prometheus=afpljo037x62oa&var-job={{ .CommonLabels.job }}&var-node={{ .CommonLabels.instance }}&refresh=1m)

**请立即处理!**
@all"
  }
}
{{ end }}

最后在接收器中引用这个模板:

receivers:
  - name: 'wecom-notify'
    webhook_configs:
      - url: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-wecom-key'
        send_resolved: true
        body: '{{ template "wecom.message" . }}'

6.2 email模板示例

[root@Ans-prometheus /data/alertmanager-0.32.2]# cat templates/email.tmpl
{{/* ========== 邮件主题模板 ========== */}}
{{ define "email.default.subject" -}}
{{- if eq .Status "resolved" -}}[✅已恢复]{{- else -}}[🚨告警中]{{- end -}}
[{{ .CommonLabels.severity | toUpper }}]
{{ .CommonLabels.alertname }} @ {{ .CommonLabels.instance }}
{{- end }}

{{/* ========== 邮件正文模板 ========== */}}
{{ define "email.default.html" }}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ template "email.default.subject" . }}</title>
<style>
  body, table, td, p, div {
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif;
    font-size: 14px;
    line-height: 1.6;
    color: #1e293b;
  }
  body {
    background-color: #f1f5f9;
    padding: 20px 0;
  }
  .container {
    max-width: 680px;
    margin: 0 auto;
    background: #ffffff;
    border-radius: 16px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.08);
    overflow: hidden;
  }
  .header {
    padding: 28px 32px;
    text-align: center;
    font-size: 20px;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: #fff;
  }
  .header-critical  { background: linear-gradient(135deg, #dc2626, #b91c1c); }
  .header-warning   { background: linear-gradient(135deg, #f59e0b, #d97706); }
  .header-info      { background: linear-gradient(135deg, #6366f1, #4f46e5); }
  .header-resolved  { background: linear-gradient(135deg, #22c55e, #16a34a); }
  .header .badge {
    display: inline-block;
    background: rgba(255,255,255,0.2);
    padding: 2px 16px;
    border-radius: 20px;
    font-size: 13px;
    font-weight: 500;
    margin-left: 12px;
  }
  .content {
    padding: 32px 32px 20px;
  }
  .section-title {
    font-size: 16px;
    font-weight: 600;
    color: #0f172a;
    border-bottom: 2px solid #e2e8f0;
    padding-bottom: 10px;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .summary-grid {
    display: table;
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 28px;
  }
  .summary-grid td {
    padding: 8px 12px 8px 0;
    vertical-align: top;
  }
  .summary-grid .label {
    color: #64748b;
    font-weight: 500;
    width: 100px;
    white-space: nowrap;
  }
  .summary-grid .value {
    font-weight: 500;
    color: #0f172a;
  }
  .severity-tag {
    display: inline-block;
    padding: 2px 14px;
    border-radius: 30px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.3px;
    text-transform: uppercase;
  }
  .sev-critical { background: #fef2f2; color: #dc2626; }
  .sev-warning  { background: #fffbeb; color: #d97706; }
  .sev-info     { background: #eef2ff; color: #4f46e5; }
  .sev-resolved { background: #f0fdf4; color: #16a34a; }

  .alert-card {
    background: #f8fafc;
    border-radius: 12px;
    padding: 18px 24px;
    margin-bottom: 16px;
    border-left: 5px solid #94a3b8;
  }
  .alert-card-critical { border-left-color: #dc2626; }
  .alert-card-warning  { border-left-color: #f59e0b; }
  .alert-card-info     { border-left-color: #6366f1; }
  .alert-card-resolved { border-left-color: #22c55e; }

  .alert-title {
    font-size: 15px;
    font-weight: 600;
    color: #0f172a;
    margin-bottom: 6px;
  }
  .alert-desc {
    background: #ffffff;
    padding: 10px 14px;
    border-radius: 6px;
    border: 1px solid #e2e8f0;
    margin: 6px 0 10px;
    color: #1e293b;
    font-size: 13px;
  }
  .alert-desc .value-highlight {
    font-weight: 700;
    color: #dc2626;
  }
  .alert-labels {
    font-size: 12px;
    color: #64748b;
    margin-top: 4px;
    word-break: break-all;
  }
  .alert-labels code {
    background: #e9edf2;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 12px;
    color: #1e293b;
  }
  .runbook-link {
    display: inline-block;
    margin-top: 8px;
    background: #e9edf2;
    padding: 4px 14px;
    border-radius: 30px;
    font-size: 13px;
    color: #1e293b;
    text-decoration: none;
    font-weight: 500;
  }
  .btn-group {
    margin: 24px 0 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
  }
  .btn {
    display: inline-block;
    padding: 10px 24px;
    border-radius: 40px;
    font-weight: 600;
    font-size: 14px;
    text-decoration: none;
    background: #f1f5f9;
    color: #1e293b;
    border: 1px solid #e2e8f0;
  }
  .btn-primary {
    background: #1f2937;
    color: #fff;
    border: none;
  }
  .btn-primary:hover {
    background: #374151;
  }
  .footer {
    text-align: center;
    padding: 20px 32px;
    font-size: 12px;
    color: #94a3b8;
    border-top: 1px solid #e2e8f0;
    background: #fafcff;
  }
  @media only screen and (max-width: 640px) {
    .container { border-radius: 0; }
    .header { font-size: 18px; padding: 20px; }
    .content { padding: 20px; }
    .summary-grid td { display: block; padding: 4px 0; }
    .summary-grid .label { width: auto; }
    .alert-card { padding: 14px 16px; }
    .btn-group { flex-direction: column; }
    .btn { text-align: center; }
  }
</style>
</head>
<body>

<div class="container">

  <!-- ====== 头部 ====== -->
  {{ if eq .Status "resolved" }}
  <div class="header header-resolved">
    ✅ 告警已全部恢复
    <span class="badge">恢复时间 {{ (index .Alerts 0).EndsAt.Local.Format "15:04:05" }}</span>
  </div>
  {{ else }}
    {{ $sev := .CommonLabels.severity }}
    {{ if eq $sev "critical" }}
    <div class="header header-critical">🚨 紧急告警</div>
    {{ else if eq $sev "warning" }}
    <div class="header header-warning">⚠️ 警告通知</div>
    {{ else }}
    <div class="header header-info">📢 信息通知</div>
    {{ end }}
  {{ end }}

  <!-- ====== 正文 ====== -->
  <div class="content">

    <!-- ===== 1. 告警概览(表格,唯一信息源) ===== -->
    <div class="section-title">📊 告警概览</div>
    <table class="summary-grid">
      <tr><td class="label">告警名称</td><td class="value">{{ .CommonLabels.alertname }}</td></tr>
      <tr><td class="label">故障实例</td><td class="value">{{ .CommonLabels.instance }}</td></tr>
      <tr>
        <td class="label">严重级别</td>
        <td class="value">
          {{ $sev := .CommonLabels.severity }}
          {{ if eq $sev "critical" }}
          <span class="severity-tag sev-critical">CRITICAL</span>
          {{ else if eq $sev "warning" }}
          <span class="severity-tag sev-warning">WARNING</span>
          {{ else }}
          <span class="severity-tag sev-info">INFO</span>
          {{ end }}
        </td>
      </tr>
      {{ if eq .Status "firing" }}
      <tr>
        <td class="label">触发时间</td>
        <td class="value">{{ (index .Alerts 0).StartsAt.Local.Format "2006-01-02 15:04:05" }}</td>
      </tr>
      {{ else }}
      <tr>
        <td class="label">恢复时间</td>
        <td class="value">{{ (index .Alerts 0).EndsAt.Local.Format "2006-01-02 15:04:05" }}</td>
      </tr>
      {{ end }}
    </table>

    <!-- ===== 2. 触发告警详情(精简版,无重复信息) ===== -->
    {{ if .Alerts.Firing }}
    <div class="section-title">🔥 触发告警详情 {{ .Alerts.Firing | len }} 条)</div>
    {{ range .Alerts.Firing }}
      {{ $sev := .Labels.severity }}
      {{ $alertClass := "info" }}
      {{ if eq $sev "critical" }}{{ $alertClass = "critical" }}{{ else if eq $sev "warning" }}{{ $alertClass = "warning" }}{{ end }}

      <div class="alert-card alert-card-{{ $alertClass }}">

        <!-- 标题:节点 + 告警摘要 -->
        <div class="alert-title">
          {{ if .Annotations.summary }}{{ .Annotations.summary }}{{ else }}{{ .Labels.alertname }}{{ end }}
        </div>

        <!-- 详细描述 -->
        <div class="alert-desc">
          {{ .Annotations.description }}
          {{ if .Annotations.value }}
            <br><span style="font-weight:600;">当前值:</span><span class="value-highlight">{{ .Annotations.value }}</span>
          {{ end }}
        </div>

        <!-- 处理手册按钮 -->
        {{ if .Annotations.runbook_url }}
        <a href="{{ .Annotations.runbook_url }}" class="runbook-link" target="_blank">📖 查看处理手册</a>
        {{ end }}

        <!-- 标签信息(紧凑单行) -->
        <div class="alert-labels">
          <strong>标签信息:</strong>
          {{ range $i, $pair := .Labels.SortedPairs }}
            {{- if gt $i 0 }} {{ end -}}
            <code>{{ $pair.Name }}</code> = {{ $pair.Value }}
          {{- end }}
        </div>

      </div>
    {{ end }}
    {{ end }}

    <!-- ===== 3. 已恢复告警详情(精简版) ===== -->
    {{ if .Alerts.Resolved }}
    <div class="section-title">✅ 已恢复告警({{ .Alerts.Resolved | len }} 条)</div>
    {{ range .Alerts.Resolved }}
      <div class="alert-card alert-card-resolved">

        <div class="alert-title">
          {{ if .Annotations.summary }}{{ .Annotations.summary }}{{ else }}{{ .Labels.alertname }}{{ end }}
        </div>

        <div class="alert-desc">
          {{ .Annotations.description }}
          {{ if .Annotations.value }}
            <br><span style="font-weight:600;">恢复前值:</span><span class="value-highlight">{{ .Annotations.value }}</span>
          {{ end }}
        </div>

        <div class="alert-labels">
          <strong>标签信息:</strong>
          {{ range $i, $pair := .Labels.SortedPairs }}
            {{- if gt $i 0 }} {{ end -}}
            <code>{{ $pair.Name }}</code> = {{ $pair.Value }}
          {{- end }}
        </div>

      </div>
    {{ end }}
    {{ end }}

    <!-- ===== 4. 操作按钮 ===== -->
    <div class="btn-group">
      {{ if .Alerts.Firing }}
        {{ $alert := index .Alerts.Firing 0 }}
        {{ $baseUrl := "http://10.0.0.42:3000/d/rYdddlPWk/node-exporter-full" }}
        {{ $ds := "afpljo037x62oa" }}
        {{ $job := "Node-Exporter" }}
        {{ if $alert.Labels.job }}{{ $job = $alert.Labels.job }}{{ end }}
        {{ $node := $alert.Labels.instance }}
        {{ $grafanaUrl := printf "%s?orgId=1&from=now-24h&to=now&timezone=browser&var-ds_prometheus=%s&var-job=%s&var-node=%s&refresh=1m" $baseUrl $ds $job $node }}
        <a href="{{ $grafanaUrl }}" class="btn btn-primary" target="_blank">📊 定位该节点(Grafana)</a>
      {{ else }}
        {{ $alert := index .Alerts.Resolved 0 }}
        {{ if $alert }}
          {{ $baseUrl := "http://10.0.0.42:3000/d/rYdddlPWk/node-exporter-full" }}
          {{ $ds := "afpljo037x62oa" }}
          {{ $job := "Node-Exporter" }}
          {{ if $alert.Labels.job }}{{ $job = $alert.Labels.job }}{{ end }}
          {{ $node := $alert.Labels.instance }}
          {{ $grafanaUrl := printf "%s?orgId=1&from=now-24h&to=now&timezone=browser&var-ds_prometheus=%s&var-job=%s&var-node=%s&refresh=1m" $baseUrl $ds $job $node }}
          <a href="{{ $grafanaUrl }}" class="btn btn-primary" target="_blank" style="background:#6b7280;border-color:#6b7280;">📊 查看恢复节点数据(Grafana)</a>
        {{ end }}
      {{ end }}
    </div>

  </div>

  <!-- ====== 页脚 ====== -->
  <div class="footer">
    <p>此邮件由 Prometheus + Alertmanager 自动发出,请勿直接回复</p>
    <p style="margin-top:4px; opacity:0.7;">如有疑问请联系运维团队</p>
  </div>

</div>
</body>
</html>
{{ end }}


接收器中引用这个模板:

# 告警接收器配置
receivers:
  - name: "email-alerts"
    # 邮件通知:发送给值班人员和经理
    email_configs:
      - to: "xxx"  # 接收人邮箱
        send_resolved: true
        # 调用 HTML 正文模板
        html: '{{ template "email.default.html" . }}'
        headers:
          Subject: |-
            {{- if eq .Status "resolved" }}[✅ 已恢复]
            {{- else if eq .CommonLabels.severity "critical" }}[🚨 紧急告警]
            {{- else if eq .CommonLabels.severity "warning" }}[⚠️ 警告通知]
            {{- else }}[📢 信息通知]
            {{- end -}}
            {{ .CommonLabels.alertname }} - {{ .CommonLabels.instance }}

6.3 模板常用数据结构

变量 类型 说明
.Status string 告警状态,firingresolved
.Alerts []Alert 当前分组中的所有告警
.Alerts.Firing []Alert 当前分组中处于firing状态的告警
.Alerts.Resolved []Alert 当前分组中处于resolved状态的告警
.CommonLabels map 所有告警共有的标签
.CommonAnnotations map 所有告警共有的注解
.GroupLabels map 分组所使用的标签
.ExternalURL string Alertmanager的外部访问地址
.StartsAt time.Time 告警首次触发的时间(UTC)
.EndsAt time.Time 告警恢复的时间(UTC)

七、生产级二进制部署 + Systemd托管

采用二进制部署方式,轻量无依赖,搭配systemd实现开机自启和崩溃重启,是生产环境的首选部署方案。

7.1 下载安装

Alertmanager 0.27.0是当前最新的稳定版本(截至2024年7月):

# 下载最新稳定版
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz

# 解压部署
tar -zxvf alertmanager-0.27.0.linux-amd64.tar.gz
cd alertmanager-0.27.0.linux-amd64

# 复制二进制文件到系统目录
cp alertmanager /usr/local/bin/
cp amtool /usr/local/bin/

# 赋予执行权限
chmod +x /usr/local/bin/alertmanager /usr/local/bin/amtool

7.2 创建目录与用户

# 创建专用用户
useradd -M -s /sbin/nologin alertmanager

# 创建配置和数据目录
mkdir -p /etc/alertmanager /var/lib/alertmanager /etc/alertmanager/templates

# 设置目录权限
chown -R alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager

7.3 生产完整配置文件

创建/etc/alertmanager/alertmanager.yml

global:
  # 告警恢复后等待5分钟再发送恢复通知,避免指标抖动导致的频繁恢复/触发
  resolve_timeout: 5m

  # QQ邮箱SMTP配置(注意:smtp_auth_password需使用QQ邮箱授权码,非登录密码)
  smtp_from: "xxx@qq.com"
  smtp_smarthost: "smtp.qq.com:465"
  smtp_auth_username: "xxx@qq.com"
  smtp_auth_password: "your-qq-authorization-code"
  smtp_require_tls: true
  smtp_hello: "alertmanager.example.com"  # 增加SMTP握手标识,提高邮件送达率

# 告警模板文件路径(确保目录存在且Alertmanager有读取权限)
templates:
  - "/data/alertmanager/templates/*.tmpl"

# 根路由配置(所有告警从这里进入)
route:
  # 默认接收器:所有未匹配到子路由的告警都发送到默认企业微信群
  receiver: "default-wecom"
  
  # 全局分组策略:按告警级别、名称、实例分组,精准区分不同告警
  group_by: ["severity", "alertname", "instance"]
  
  # 首次告警等待10秒,收集同组内的其他告警后批量发送
  group_wait: 10s
  
  # 同组内有新告警加入时,等待5分钟再发送更新后的通知
  group_interval: 5m
  
  # 普通告警持续未恢复时,每2小时重复发送一次
  repeat_interval: 2h

  # 子路由列表(从上到下匹配,先匹配到的优先生效)
  routes:
    # 1. 严重级别告警:最高优先级,缩短重复间隔
    - match:
        severity: "critical"
      receiver: "critical-alerts"
      repeat_interval: 30m  # 严重告警30分钟重复一次
      continue: false       # 匹配成功后不再继续匹配后续路由

    # 2. 数据库服务告警:单独路由给DBA团队
    - match:
        service: "database"
      receiver: "dba-alerts"
      continue: true  # 数据库告警同时发送给DBA和运维团队

    # 3. 运维团队通用告警
    - match:
        team: "operations"
      receiver: "operations-team"
      continue: false

# 告警接收器配置
receivers:
  # 默认接收器:普通告警发送到运维大群
  - name: "default-wecom"
    # 企业微信群机器人Webhook(修复93000错误:替换为真实机器人key)
    webhook_configs:
      - url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_YOUR_REAL_DEFAULT_KEY"
        send_resolved: true
        body: '{{ template "wecom.message" . }}'
        http_config:
          tls_config:
            insecure_skip_verify: false

  # 严重告警接收器:同时发送邮件、钉钉、企业微信紧急群并@所有人
  - name: "critical-alerts"
    # 邮件通知:发送给值班人员和经理
    email_configs:
      - to: "oncall@example.com,manager@example.com"
        send_resolved: true
        headers:
          Subject: "[紧急告警] {{ .CommonLabels.alertname }} - {{ .CommonLabels.instance }}"
          From: "监控告警中心 <xxx@qq.com>"
    
    # 钉钉通知:发送到紧急告警群
    webhook_configs:
      - url: "http://localhost:8070/dingtalk/critical/send"
        send_resolved: true
      # 企业微信紧急群机器人
      - url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_YOUR_REAL_CRITICAL_KEY"
        send_resolved: true
        body: '{{ template "wecom.critical.message" . }}'
    
    # 企业微信原生应用通知:推送到企业微信客户端
    wechat_configs:
      - corp_id: "ww5421dksajhdasjkhj"  # 企业ID
        agent_id: "1000002"             # 应用ID
        api_secret: "your-wechat-api-secret"  # 应用密钥
        to_party: "2"                   # 接收部门ID
        to_user: "@all"                 # @部门所有人
        send_resolved: true

  # DBA团队接收器:数据库相关告警
  - name: "dba-alerts"
    # 邮件通知
    email_configs:
      - to: "dba-team@example.com"
        send_resolved: true
        headers:
          Subject: "[数据库告警] {{ .CommonLabels.alertname }}"
    
    # 钉钉通知
    webhook_configs:
      - url: "http://localhost:8070/dingtalk/db/send"
        send_resolved: true
      # 企业微信DBA群机器人
      - url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_YOUR_REAL_DBA_KEY"
        send_resolved: true
        body: '{{ template "wecom.message" . }}'

  # 运维团队接收器
  - name: "operations-team"
    email_configs:
      - to: "ops-team@example.com"
        send_resolved: true
        headers:
          Subject: "[运维告警] {{ .CommonLabels.alertname }}"
    
    webhook_configs:
      - url: "http://localhost:8070/dingtalk/ops/send"
        send_resolved: true

# 告警抑制规则(解决故障雪崩问题)
inhibit_rules:
  # 规则1:当节点宕机时,抑制该节点上的所有Warning级别告警
  - source_match:
      severity: "critical"
      alertname: "NodeDown"
    target_match:
      severity: "warning"
    equal: ["instance"]  # 只有相同实例的告警才会被抑制

  # 规则2:当Kubernetes节点不可用时,抑制该节点上的所有Pod告警
  - source_match:
      severity: "critical"
      alertname: "KubeNodeNotReady"
    target_match:
      severity: "warning"
    equal: ["node"]

  # 规则3:当数据库主节点不可用时,抑制从节点的延迟告警
  - source_match:
      severity: "critical"
      alertname: "MysqlMasterDown"
    target_match:
      alertname: "MysqlSlaveDelay"
    equal: ["cluster"]

7.4 Systemd服务托管

创建/etc/systemd/system/alertmanager.service

[Unit]
Description=Prometheus Alertmanager
Documentation=https://prometheus.io/docs/alerting/latest/alertmanager/
After=network.target

[Service]
Type=simple
User=alertmanager
Group=alertmanager
ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager \
  --web.listen-address=:9093 \
  --cluster.listen-address=:9094 \
  --log.level=info

Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

7.5 启动并开机自启

# 重新加载systemd配置
systemctl daemon-reload

# 设置开机自启
systemctl enable alertmanager

# 启动服务
systemctl start alertmanager

# 查看服务状态
systemctl status alertmanager

7.6 访问验证

浏览器访问:http://服务器IP:9093,进入Alertmanager可视化后台,可查看当前告警、静默记录、路由状态和集群信息。

八、Prometheus关联Alertmanager配置

修改Prometheus主配置文件prometheus.yml,添加Alertmanager配置:

# 关联告警组件
alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - 127.0.0.1:9093
            # 如果是集群部署,添加所有实例地址
            # - alertmanager-1:9093
            # - alertmanager-2:9093

# 加载自定义告警规则文件
rule_files:
  - "rules/*.yml"

重载Prometheus配置:

curl -X POST http://prometheus-ip:9090/-/reload

九、高可用(High Availability)

9.1 为什么需要高可用?

单个Alertmanager实例存在单点故障风险。如果Alertmanager宕机,所有告警都将无法发送,这在生产环境中是不可接受的。

9.2 HA架构核心设计

Alertmanager集群由多个通过Gossip协议进行通信的实例组成。每个实例:

  • 独立从Prometheus服务器接收告警

  • 参与对等的Gossip网状网络

  • 将状态(静默规则和通知日志)复制到其他集群成员

  • 独立处理并发送通知

Gossip协议负责:

  • 成员管理:节点发现、健康检查、故障检测

  • 状态复制:静默规则、通知日志的跨节点同步

gossip流言算法协议原理分析:

  • 1.前提设定: Gossip时周期性的散播消息,把周期限定为1秒;

  • 2.被感染节点随机选择K个临近节点(fan-out)散播消息,这里把fan-out设置为3,每次最多往3个节点散播;

  • 3.每次散播消息都选择尚未发送过的节点进行散播;

  • 4.收到消息的节点不再往发送节点散播,比如A发送给B,那么B进行散播的时候,不在发送A;

  • 5.Gossip过程是异步的,这就是说发消息的节点不会关注对方是否收到,即不等待响应,不管对方有没有收到,它都会每隔1秒向周围节点发消息,异步是它的优点,而消息冗余是它的弱点;

Gossip的优劣势:

优点:
	- 扩展性:
		网络可以允许节点的任意增加和减少,新增加的节点状态最终会与其他节点一致。
	
	- 容错:
		网络中任何节点的宕机和重启都不会影响Gossip消息的传播,Gossip协议具有天然的分布式系统容错特性。

	- 去中心化:
		Gossip协议不要求任何中心节点,所有节点都可以是对等的,任何一个节点无需知道整个网络状况,只要网络是连通的。
		换句话说,任意一个节点就可以把消息散播到全网。
		
	- 一致性收敛:
		Gossip协议中的消息会以一传十,十传百一样的指数速级速度在网络中快速传播,因此系统状态不一致可以在很快的时间内收敛到一致。
		换句话说,消息传播速度达到了logN。
		
	- 简单:
		Gossip协议的过程及其简单,实现起来几乎没有太多复杂性。
		
		
缺点:
	分布式网络中,没有一种完美的解决方案,Gossip协议和其他协议一样,也有一些不可避免的缺陷,主要有是在数据延迟和冗余。
	
	- 消息的延迟:
		由于Gossip协议中,节点指挥随机向少数几个节点发送消息,消息最终是通过多个轮次的散播而到达全网的。
		因此使用Gossip协议会造成不可避免的消息延迟,不适合用在实时性要求较高的场景下。
		
	- 消息冗余:
		Gossip协议规定,节点会定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤,因此就不可避免存在消息重复发送给同一节点的情况。
		造成了消息的冗余,同时也增加收到消息的节点处理的压力,而且由于定期发送,即使收到了消息的节点还会反复收到重复消息,加重了消息的冗余。

Gossip中通信模式

参考
	https://github.com/hashicorp/memberlist

在Gossip协议下,网络中两个节点之间有三种通信模式: "PUSH","PULL"和"PUSH/PULL"。
    - PUSH:
        节点A将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A中比自己新的数据。

    - PULL:
        A仅将数据key,vaerion推送给B,B将本地比A新的数据Key,value,version推送给A,A更新本地。

    - PUSH/PULL:
        与PULL类似,只是多了一步,A再将本地比B新的数据推送给B,B则更新本地。
	
如果把两个节点数据同步一次定义为一个周期,则在一个周期内,PUSH需通信1次,PULL需通信2次,PULL/PUSH则需要三次。

虽然消息数增加了,但从效果来讲,PUSH/PULL最好,理论上一个周期可以使两个节点完全一致,直观上PUSH/PULL的收敛速度也是最快的。

9.3 HA的三个核心原则

Alertmanager的高可用设计基于以下原则:

  1. 统一视图与管理:可从任意集群成员查看和管理静默规则和告警

  2. 故障开放(fail open):网络分区期间倾向于发送重复告警,而非遗漏关键告警

  3. 至少一次交付:保证告警至少被发送一次,符合"故障开放"理念

9.4 集群配置方法

二进制部署方式

# 实例1
alertmanager --config.file=/etc/alertmanager/alertmanager.yml \
             --storage.path=/var/lib/alertmanager \
             --cluster.listen-address=0.0.0.0:9094 \
             --cluster.peer=10.0.1.101:9094 \
             --cluster.peer=10.0.1.102:9094

# 实例2
alertmanager --config.file=/etc/alertmanager/alertmanager.yml \
             --storage.path=/var/lib/alertmanager \
             --cluster.listen-address=0.0.0.0:9094 \
             --cluster.peer=10.0.1.100:9094 \
             --cluster.peer=10.0.1.102:9094

# 实例3
alertmanager --config.file=/etc/alertmanager/alertmanager.yml \
             --storage.path=/var/lib/alertmanager \
             --cluster.listen-address=0.0.0.0:9094 \
             --cluster.peer=10.0.1.100:9094 \
             --cluster.peer=10.0.1.101:9094


测试gossip方法1:
	- 在一个10.0.1.101节点创建静默;
	- 在10.0.1.100,10.0.1.102页面上能看到静默的记录;

Kubernetes部署方式(Prometheus Operator)

apiVersion: monitoring.coreos.com/v1
kind: Alertmanager
metadata:
  name: main
  namespace: monitoring
spec:
  replicas: 3
  version: v0.27.0
  storage:
    volumeClaimTemplate:
      spec:
        resources:
          requests:
            storage: 10Gi

9.5 Prometheus侧配置

Prometheus的alerting段应配置所有Alertmanager实例的地址:

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager-0:9093
          - alertmanager-1:9093
          - alertmanager-2:9093

Prometheus会将告警发送到所有Alertmanager实例,由集群内部的去重机制保证最终不会重复发送。

十、常用告警规则示例

以下是生产环境中最常用的告警规则,放入Prometheus的rules目录即可生效:

groups:
- name: node_alerts
  rules:
  # 主机宕机告警
  - alert: NodeDown
    expr: up{job="node_exporter"} == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "节点 {{ $labels.instance }} 服务失联"
      description: "节点 {{ $labels.instance }} 已经超过1分钟无法访问,请立即检查"

  # CPU使用率过高
  - alert: HighCPUUsage
    expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "节点 {{ $labels.instance }} CPU使用率过高"
      description: "CPU使用率已超过85%,当前值: {{ $value | printf \"%.2f\" }}%"

  # 内存使用率过高
  - alert: HighMemoryUsage
    expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "节点 {{ $labels.instance }} 内存使用率过高"
      description: "内存使用率已超过85%,当前值: {{ $value | printf \"%.2f\" }}%"

  # 磁盘使用率过高
  - alert: HighDiskUsage
    expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 15
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "节点 {{ $labels.instance }} 磁盘使用率过高"
      description: "磁盘 {{ $labels.mountpoint }} 使用率已超过85%,当前可用: {{ $value | printf \"%.2f\" }}%"

- name: blackbox_alerts
  rules:
  # 端点探测失败
  - alert: EndpointDown
    expr: probe_success == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "端点 {{ $labels.instance }} 探测失败"
      description: "端点 {{ $labels.instance }} 已经连续1分钟探测失败,请检查服务状态"

十一、常见问题与排错指南

11.1 收不到任何告警

可能原因及解决方案

  • Prometheus未配置alerting段:检查prometheus.yml中的alerting配置

  • 告警规则未加载:在Prometheus UI的"Rules"页面检查规则状态

  • 告警处于Pending状态:等待for指定的持续时间

  • 网络不通:检查Prometheus到Alertmanager的9093端口是否开放

  • Alertmanager服务未启动:使用systemctl status alertmanager检查服务状态

11.2 告警轰炸、重复推送过多

可能原因及解决方案

  • group_by配置不合理:增加更多的分组标签,如alertname、instance、service

  • group_interval和repeat_interval设置过短:调大这两个参数

  • 缺少抑制规则:配置合理的抑制规则,屏蔽衍生告警

  • 告警规则过于敏感:调大for参数或调整告警阈值

  • HA 集群状态未同步:检查 --cluster.peer 和网络连通性(UDP 9094)

11.3 无告警恢复通知

可能原因及解决方案

  • send_resolved未设置为true:在接收器配置中添加send_resolved: true

  • resolve_timeout设置过长:根据实际场景调整该参数,默认5分钟

  • 告警恢复后立即又触发:检查指标是否存在抖动,可适当调大for参数

11.4 静默规则不生效

可能原因及解决方案

  • 标签匹配规则写错:检查静默中的matchers与告警标签是否完全一致

  • 静默时间设置错误:检查开始时间和结束时间是否正确

  • 静默规则未同步到所有集群节点:检查集群状态和网络连通性

11.5 集群成员失联

可能原因及解决方案

  • Gossip端口(9094)被防火墙阻断:开放UDP和TCP 9094端口

  • cluster.peer配置错误:检查所有实例的cluster.peer参数

  • 主机名解析失败:使用IP地址代替主机名

十二、最佳实践

12.1 告警规则设计

  • 合理设置 for:关键业务 1-2 分钟,非关键 5-10 分钟,避免为 0 导致抖动告警。

  • 分层告警warning(工作时间处理)、critical(立即处理),不同级别不同接收者。

  • 完善 annotations:包含问题描述、处理建议、Grafana 链接、文档链接。

12.2 降噪效果度量

建议企业建立“告警治理”流程,定期评估:

  • 告警准确率 = 有效告警数 / 总告警数

  • 告警压缩率 = 分组聚合前后通知数量比

  • 处理及时率 = 关键告警在规定时间内响应的比例

12.3 生产环境部署建议

  • 部署3节点Alertmanager集群实现高可用

  • 对Alertmanager自身进行监控,关注以下指标:

    • alertmanager_alerts_received_total:接收的告警总数

    • alertmanager_notifications_failed_total:发送失败的通知数

    • alertmanager_silences_active:活跃的静默规则数

  • 开发、测试、生产环境使用独立的Alertmanager实例

  • 配置合理的数据保留时间,默认120小时

12.4 配置管理最佳实践

  • 使用版本控制(Git)管理所有配置文件

  • 敏感信息(API Key、密码)使用外部文件或环境变量管理

  • 配置变更前先在测试环境验证

  • 使用amtool check-config命令检查配置文件语法

  • 配置变更后使用热加载方式生效:curl -X POST http://alertmanager:9093/-/reload

12.5 告警治理最佳实践

  • 建立告警分级制度:

    • Critical:紧急问题,需要立即处理(24x7)

    • Warning:重要问题,工作时间内处理

    • Info:一般信息,无需立即处理

  • 定期清理无效告警规则

  • 建立告警响应SLA

  • 持续优化告警准确率和压缩率

十三、总结

Alertmanager是Prometheus监控体系中不可或缺的告警管理平台。它通过分组、抑制、静默等机制有效解决了告警风暴问题,通过灵活的路由和模板系统实现了告警的精细化分发和定制化通知。

核心要点回顾

  • 分离式架构:Prometheus负责告警判定,Alertmanager负责告警处理

  • 三大核心机制:分组(去重)、抑制(优先级)、静默(临时屏蔽)

  • 树形路由:支持多级匹配和continue标志,实现灵活的告警分发

  • 多渠道通知:支持邮件、钉钉、企业微信、Slack、Webhook等多种通知方式

  • 高可用集群:基于Gossip协议,保证告警不丢失

通过合理配置和使用Alertmanager,可以显著降低告警噪音,帮助运维团队从被动的"告警疲劳"中解放出来,聚焦于真正需要处理的关键问题,提升整个系统的可靠性和可维护性。

posted @ 2026-06-07 19:14  kyle_7Qc  阅读(120)  评论(0)    收藏  举报