Prometheus + Alertmanager 把告警送到 Slack / 邮件 / 钉钉

起因

装了 Prometheus + Grafana 后能看图了,但"半夜 3 点磁盘满了"还得
有人盯仪表盘才发现。要把"触发某指标条件"自动转成 push 告警。
Alertmanager 是 Prometheus 生态官方告警分发组件。

解决方案

1. 写告警规则(Prometheus 端)

/etc/prometheus/rules/node.yml

groups:
  - name: node-alerts
    interval: 30s
    rules:
      - alert: NodeDown
        expr: up{job="node"} == 0
        for: 2m
        labels:
          severity: critical
          team: ops
        annotations:
          summary: '节点 {{ $labels.instance }} 离线'
          description: 'Prometheus 已经 2 分钟无法 scrape {{ $labels.instance }}'

      - alert: HighCPU
        expr: 100 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: '{{ $labels.instance }} CPU > 85% 持续 10 分钟'

      - alert: DiskAlmostFull
        expr: 100 - node_filesystem_avail_bytes{mountpoint="/"} * 100 / node_filesystem_size_bytes{mountpoint="/"} > 90
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: '{{ $labels.instance }} 根分区 > 90%'
          description: '目前 {{ $value | humanizePercentage }}'

      - alert: MemoryPressure
        expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 92
        for: 15m
        labels:
          severity: warning

关键点:

  • expr:PromQL 表达式,结果非空时触发
  • for:持续多久才真触发(避免瞬间抖动告警)
  • labels:可以路由用(severity / team)
  • annotations:人读的内容,模板支持 {{ $labels.x }}{{ $value }}

prometheus.yml 引入:

rule_files:
  - /etc/prometheus/rules/*.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

reload Prometheus:

curl -X POST http://localhost:9090/-/reload

UI 看告警状态:http://prom:9090/alerts,三种状态:Inactive / Pending / Firing。

2. 装 Alertmanager

curl -fsSL https://github.com/prometheus/alertmanager/releases/latest/download/alertmanager-0.27.0.linux-amd64.tar.gz \
  | sudo tar xz -C /opt/

sudo ln -s /opt/alertmanager-0.27.0.linux-amd64 /opt/alertmanager
sudo useradd -rs /bin/false alertmanager
sudo mkdir -p /var/lib/alertmanager
sudo chown -R alertmanager:alertmanager /var/lib/alertmanager

3. 配置 Alertmanager

/etc/alertmanager/alertmanager.yml

global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: 'notifier'
  smtp_auth_password: '...'

# 路由树
route:
  receiver: default
  group_by: ['alertname', 'instance']
  group_wait: 30s          # 第一条告警等 30s 看有没有同组的
  group_interval: 5m        # 同组下一批告警最少间隔
  repeat_interval: 4h       # 同告警重复发的最短间隔
  routes:
    - matchers:
        - severity = critical
      receiver: pagerduty
      group_wait: 10s
      repeat_interval: 1h

    - matchers:
        - severity = warning
        - team = ops
      receiver: slack-ops

inhibit_rules:
  - source_matchers: [severity="critical"]
    target_matchers: [severity="warning"]
    equal: ['instance']
    # 同一台机器既有 critical 又有 warning,warning 被抑制(避免噪音)

receivers:
  - name: default
    email_configs:
      - to: '[email protected]'

  - name: slack-ops
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/...'
        channel: '#alerts-ops'
        title: '{{ .CommonAnnotations.summary }}'
        text: |
          {{ range .Alerts }}
          *{{ .Annotations.summary }}*
          {{ .Annotations.description }}
          severity: {{ .Labels.severity }}
          {{ end }}

  - name: pagerduty
    pagerduty_configs:
      - service_key: '<PD key>'

  - name: dingtalk
    webhook_configs:
      - url: 'https://oapi.dingtalk.com/robot/send?access_token=...'
        send_resolved: true

钉钉 webhook 需要特定 JSON 格式
通常需要中间 adapter(prometheus-webhook-dingtalk)转换。

systemd unit:

[Service]
User=alertmanager
ExecStart=/opt/alertmanager/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager
Restart=on-failure
sudo systemctl enable --now alertmanager

4. 测试

手动触发一条假告警:

curl -XPOST http://alertmanager:9093/api/v2/alerts -d '
[{
  "labels": {"alertname": "TestAlert", "severity": "critical", "instance": "test"},
  "annotations": {"summary": "测试告警"},
  "startsAt": "2026-05-24T10:00:00Z"
}]'

应该几秒内收到 Slack / 邮件。

5. 抑制 + 静默

# 系统维护期间静默
amtool silence add alertname=NodeDown instance=app1.example.com \
  --duration=2h --comment 'planned maintenance'
amtool silence query
amtool silence expire <id>

或 Alertmanager UI(端口 9093)有 silence 表单。

效果

  • 告警从"刷仪表盘" → "手机 push"
  • 通过 group_by + group_interval 把"集群批量告警"合并成几条消息,
    不再被刷屏
  • inhibit 让"机器挂了之后机器上每个服务都告警"自动只剩一条 NodeDown
  • on-call 工程师 MTTR 从 30 分钟(发现 + 上线诊断)降到 5 分钟

几个最佳实践

  1. for: 5m 不要省:磁盘 90% 持续 5 分钟才真告警,避免 cron 任务
    瞬间冲高引起假警

  2. 每条 alert 配 runbook 链接:annotation 加 runbook_url,告警
    消息里点击直达"出现 X 怎么处理"文档

  3. 不要告警一切:CPU 80% 不需要立刻人工干预,写到 daily report
    就好。半夜 page 应当是"现在不处理业务挂"级别

  4. repeat_interval: 4h 平衡:太短刷屏;太长重要告警睡过

  5. 定期 review alert noise:跑 amtool alert query 看哪些告警
    反复 firing 没人理 → 要么调阈值要么删

踩过的坑

  1. rule reload 没生效:Prometheus reload /-/reload 端点默认禁用,
    --web.enable-lifecycle 启动。

  2. 告警时间戳错乱:Prometheus / Alertmanager 时区不一致 → UI 显示
    告警是几年前的。两端都 UTC。

  3. expr 写错没语法报错:Prometheus 接受语法对但语义错的表达式
    (如 metric > 100 而 metric 单位是 GB),跑出来永远空。在
    Prometheus UI Graph 选项卡先跑 expr 看结果再写规则。

  4. Slack webhook URL 进 git:泄露了被人乱发消息。放 env / secret
    或 alertmanager 的 file: 引用:
    yaml api_url_file: /etc/alertmanager/slack_url

  5. inhibit_rules 的 equal 字段不一致:source 和 target 没共同
    label 时 inhibit 不生效。仔细 check label 列表。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。