Kubernetes 监控实战:使用 Alertmanager 实现邮件告警、分组路由与告警抑制

发布时间:2026/7/15 23:30:32
Kubernetes 监控实战:使用 Alertmanager 实现邮件告警、分组路由与告警抑制 前言在前面的监控实践中我们已经通过ServiceMonitor让 Prometheus 抓取了 NVIDIA DCGM Exporter 和 vLLM 的指标并使用PrometheusRule编写了 GPU 与推理服务告警规则。但是Prometheus 负责的是“判断告警是否成立”并不负责完成告警分组、通知路由、静默、抑制以及邮件发送。Alertmanager 正是用来处理这些工作的组件。Prometheus 官方也将两者的职责进行了区分Prometheus 通过告警规则识别当前故障Alertmanager 负责后续的聚合、限流、静默、依赖处理和通知分发。本文基于 Prometheus Operator 提供的AlertmanagerConfigCRD完成以下功能普通告警 → 发送到默认邮箱 带有 notify_groupai-infra 标签的告警 → 发送到 AI Infra 专用邮箱 Watchdog 心跳告警 → 保留在 Alertmanager 中但不发送邮件 同一 namespace、同一 alertname critical 告警触发 → 抑制 warning 和 info 通知 告警恢复 → 发送恢复邮件本文使用的实验环境如下Kubernetes 命名空间monitoring Prometheusprometheus-k8s Alertmanageralertmanager-main 配置方式Prometheus Operator AlertmanagerConfig 通知方式QQ 邮箱 SMTP 业务监控对象vLLM 推理服务 基础设施监控对象NVIDIA GPU一、Prometheus 和 Alertmanager 的告警链路完整链路如下业务服务或 Exporter ↓ Prometheus 抓取指标 ↓ PrometheusRule 计算告警条件 ↓ 告警持续满足 for 后进入 FIRING ↓ Prometheus 将告警发送给 Alertmanager ↓ Alertmanager 执行分组、去重、路由、抑制 ↓ 通过 SMTP 发送邮件通知Prometheus 告警规则常见的状态包括INACTIVE当前表达式不满足告警条件。PENDING表达式已经满足但持续时间还没有达到for。FIRING表达式持续满足的时间已经达到for告警正式触发。PENDING阶段主要由 Prometheus 自己维护当告警进入FIRING后Prometheus 才会把它作为需要通知的告警发送给 Alertmanager。Prometheus 官方文档也说明for用于要求告警条件持续满足指定时间在达到该时间前告警处于pending状态。下面是本次实验中 Prometheus 已加载的 vLLM 告警规则二、检查 AlertmanagerConfig 是否能够生效2.1 检查 Alertmanager 运行状态先确认 Alertmanager Pod、Service 和自定义资源正常存在kubectl -n monitoring get pod | grep alertmanager kubectl -n monitoring get svc | grep alertmanager kubectl -n monitoring get alertmanager在 Prometheus Operator 环境中Alertmanager自定义资源负责声明 Alertmanager 实例Operator 再根据该资源创建 StatefulSet、Pod 和配套 Service。2.2 检查 AlertmanagerConfig CRD 版本AlertmanagerConfig的 API 版本不能直接照抄其他环境必须以当前集群已经安装并处于servedtrue状态的版本为准kubectl get crd alertmanagerconfigs.monitoring.coreos.com \ -o jsonpath{range .spec.versions[?(.servedtrue)]}{.name}{\n}{end}本次实验返回v1alpha1因此本文使用apiVersion: monitoring.coreos.com/v1alpha1 kind: AlertmanagerConfig如果你的集群返回其他版本应同步修改 YAML 中的apiVersion不能只修改 CRD 名称。2.3 检查 Alertmanager 对 AlertmanagerConfig 的选择范围创建AlertmanagerConfig并不代表 Alertmanager 一定会加载它。需要检查Alertmanager自定义资源中的选择器kubectl -n monitoring get alertmanager main -o yaml重点检查spec: alertmanagerConfigSelector: {} alertmanagerConfigMatcherStrategy: type: None这里有两个关键点。alertmanagerConfigSelector: {}表示选择符合命名空间范围的所有AlertmanagerConfig。如果该字段完全未设置资源选择器默认不会选择任何AlertmanagerConfig。alertmanagerConfigNamespaceSelector如果未设置默认只发现与Alertmanager自定义资源位于同一命名空间中的AlertmanagerConfig。本文的Alertmanager和AlertmanagerConfig都位于monitoring因此可以不额外配置该字段。注意如果你的Alertmanager资源由 Helm 或 Kustomize 管理应在对应的 values 或清单源文件中修改避免手工修改后又被重新覆盖。三、创建 SMTP 授权码 Secret邮件密码不能直接写在AlertmanagerConfig中。本文使用 Kubernetes Secret 保存 QQ 邮箱 SMTP 授权码。QQ 邮箱的 SMTP 授权码与网页登录密码不是同一个概念。创建alertmanager-smtp-auth.yamlapiVersion: v1 kind: Secret metadata: name: smtp-credentials namespace: monitoring type: Opaque stringData: password: QQ邮箱SMTP授权码创建 Secret后检查资源kubectl -n monitoring get secret smtp-credentials后续AlertmanagerConfig通过下面的方式引用它authPassword: name: smtp-credentials key: password其中name必须与 Secret 的名称一致。key必须与stringData或data中保存授权码的键一致。Secret 必须与AlertmanagerConfig位于同一个命名空间并且 Prometheus Operator 需要有权限读取它。四、设计告警分组、路由和抑制规则4.1 告警路由树本文设计的路由树如下所有进入 Alertmanager 的告警 ↓ alertname 是否为 Watchdog ├── 是 → null不发送邮件 └── 否 ↓ 是否带有 notify_groupai-infra ├── 是 → email-ai-infra └── 否 → email-defaultAlertmanager 的路由是一个树形结构。每条告警先进入顶层路由再依次匹配子路由。默认情况下continue为false告警匹配到第一个子路由后不再继续匹配后续同级路由如果没有匹配任何子路由则由当前路由的接收器处理。根路由配置为route: receiver: email-default表示未匹配专用子路由的告警统一发送到默认邮箱。AI Infra 专用路由如下- receiver: email-ai-infra matchers: - name: notify_group value: ai-infra matchType: 只要告警中包含labels: notify_group: ai-infra就会进入email-ai-infra接收器。因为没有设置continue: true所以该告警不会再发送给默认接收器。4.2 丢弃 Watchdog 邮件通知Watchdog是 kube-prometheus 中用于验证告警链路的心跳告警通常会长期保持触发状态。本文让它保留在 Alertmanager 页面中但不发送邮件- receiver: null matchers: - name: alertname value: Watchdog matchType: 对应的空接收器为- name: null这里必须把null加上引号避免 YAML 将其解析成空值。4.3 告警分组根路由使用以下分组字段groupBy: - namespace - alertname这表示namespace和alertname都相同的告警会进入同一个通知组。例如下面两个 Pod 告警alertnameKubePodNotReady namespaceai-demo podpod-aalertnameKubePodNotReady namespaceai-demo podpod-b由于pod没有放入groupBy所以它们可以被合并到同一个通知组中而不是为每个 Pod 单独发送一封邮件。Alertmanager 的分组机制主要用于在大规模故障中减少重复通知。4.4 groupWait、groupInterval 和 repeatInterval本文配置groupWait: 30s groupInterval: 5m repeatInterval: 10mgroupWait: 30s表示一个新的告警组第一次出现后先等待 30 秒再发送第一条通知。这样可以收集同组的其他告警也可以等待更高级别的抑制源告警到达。如果告警在groupWait结束前恢复该告警不会发送通知。groupInterval: 5m表示首条通知发送后每隔 5 分钟检查该通知组是否有新告警加入或者是否有告警恢复。只有通知组发生变化时才会发送更新通知。repeatInterval: 10m表示告警持续未恢复并且通知组没有变化时距离上一次通知达到 10 分钟后再次发送提醒。Alertmanager 在每个groupInterval周期检查是否达到repeatInterval因此repeatInterval最好设置为groupInterval的整数倍。以本文配置为例假设第一封通知发送后告警一直处于触发状态并且告警组没有发生变化 告警出现时 新告警组首次出现开始等待 groupWait。 30 秒后 groupWait 结束发送第一封通知。 5 分 30 秒后 到达第一个 groupInterval 检查点。 此时距离上一封通知只有 5 分钟尚未达到 repeatInterval 因此不发送重复通知。 10 分 30 秒后 到达第二个 groupInterval 检查点。 此时距离上一封通知已经达到 10 分钟并且告警仍未恢复、 告警组也没有发生变化因此发送重复通知。4.5 邮件接收器邮件接收器主要使用以下字段emailConfigs: - to: 收件邮箱 from: 发件邮箱 smarthost: smtp.qq.com:587 authUsername: 发件邮箱 authPassword: name: smtp-credentials key: password requireTLS: true sendResolved: true字段含义如下to接收告警通知的邮箱。from邮件头中的发件地址。smarthostSMTP 服务器地址和端口格式为hostname:port。authUsernameSMTP 认证用户名本实验使用完整 QQ 邮箱地址。authPassword从 Kubernetes Secret 中读取 SMTP 授权码。requireTLS要求 SMTP 连接使用 TLS。sendResolved允许在告警恢复后发送恢复通知。这些字段均由 Prometheus Operator 的EmailConfigAPI 定义。4.6 critical 抑制 warning 和 info本文配置以下抑制规则inhibitRules: - sourceMatch: - name: severity value: critical matchType: targetMatch: - name: severity value: warning|info matchType: ~ equal: - namespace - alertname它的含义是存在 severitycritical 的源告警 ↓ 查找 severitywarning 或 severityinfo 的目标告警 ↓ 要求源告警与目标告警的 namespace 和 alertname 相同 ↓ 抑制目标告警的通知需要注意抑制只是不再发送目标告警的通知不会把目标告警从FIRING改成INACTIVE也不会从 Alertmanager 页面中删除它。Alertmanager 官方将 Inhibition 定义为当某些源告警已经触发时抑制符合条件的其他告警通知。例如两个告警规则可以使用相同的alertname但使用不同严重级别- alert: VllmRequestWaiting labels: severity: warning- alert: VllmRequestWaiting labels: severity: critical当两者位于同一个namespace时critical 告警触发后warning 通知会被抑制。Silence与Inhibition不同Silence是运维人员按照标签和时间范围手工创建的静默适合计划维护Inhibition是根据其他正在触发的告警自动执行的抑制。五、创建完整的 AlertmanagerConfig创建alertmanager-main-config.yamlapiVersion: monitoring.coreos.com/v1alpha1 kind: AlertmanagerConfig metadata: name: main-config namespace: monitoring spec: route: # 未匹配到子路由的告警发送给默认收件人。 receiver: email-default # 同 namespace、同 alertname 的告警合并为一个通知组。 groupBy: - namespace - alertname # 新告警组首次出现后等待 30 秒再发送通知。 groupWait: 30s # 已经发送过通知的告警组每 5 分钟检查一次是否发生变化。 groupInterval: 5m # 告警持续未恢复且告警组没有变化时每 10 分钟重复通知。 repeatInterval: 10m routes: # Watchdog 保留在 Alertmanager 中但不发送邮件。 - receiver: null matchers: - name: alertname value: Watchdog matchType: # 带有 notify_groupai-infra 标签的告警发送到专用邮箱。 # continue 默认为 false匹配后不会继续匹配后续同级路由。 - receiver: email-ai-infra matchers: - name: notify_group value: ai-infra matchType: receivers: # 未匹配专用子路由的普通告警接收器。 - name: email-default emailConfigs: - to: 默认告警收件邮箱 from: QQ发件邮箱 smarthost: smtp.qq.com:587 authUsername: QQ发件邮箱 authPassword: name: smtp-credentials key: password requireTLS: true sendResolved: true # AI Infra 专用告警接收器。 - name: email-ai-infra emailConfigs: - to: AI Infra告警收件邮箱 from: QQ发件邮箱 smarthost: smtp.qq.com:587 authUsername: QQ发件邮箱 authPassword: name: smtp-credentials key: password requireTLS: true sendResolved: true # 空接收器只用于匹配后不发送任何通知。 - name: null inhibitRules: # 同 namespace、同 alertname 时critical 抑制 warning 和 info。 - sourceMatch: - name: severity value: critical matchType: targetMatch: - name: severity value: warning|info matchType: ~ equal: - namespace - alertname将占位符替换为实际邮箱后创建资源检查创建结果kubectl -n monitoring get alertmanagerconfig kubectl -n monitoring describe alertmanagerconfig main-config六、通过 NodePort 访问 Alertmanager Web 页面创建alertmanager-main-nodeport.yamlapiVersion: v1 kind: Service metadata: name: alertmanager-main-nodeport namespace: monitoring spec: type: NodePort selector: alertmanager: main ports: - name: web port: 9093 targetPort: web nodePort: 30993浏览器访问http://NodeIP:30993下面是本次实验中的 Alertmanager 页面页面中可以看到不同告警已经进入不同接收器monitoring/main-config/email-default monitoring/main-config/email-ai-infra例如Kubernetes 组件告警、Pod 告警和 GPU 告警进入email-default。VllmTargetDown带有notify_groupai-infra因此进入email-ai-infra。七、验证告警触发和邮件路由7.1 验证告警从 PENDING 进入 FIRING当告警表达式已经满足但还没有持续达到for指定的时间时Prometheus 页面显示为PENDING告警条件持续达到for后状态变成FIRING进入FIRING后Prometheus 将告警状态发送给 AlertmanagerAlertmanager 再根据路由和接收器配置发送邮件。7.2 验证普通 GPU 告警GPU 温度告警没有配置notify_group: ai-infra因此不会匹配 AI Infra 子路由最终由根路由发送到email-default收到的邮件如下邮件中包含告警名称、GPU UUID、主机名、GPU 编号、命名空间、严重级别、当前指标值和告警描述。需要特别说明为了快速验证邮件链路本次实验期间曾临时把 PromQL 的温度阈值调低但没有同步修改annotations.description所以截图中出现了“描述为超过 80 度但当前值为 51”的不一致。这只是测试配置造成的展示问题不代表51 80。实际使用时PromQL 阈值和告警描述必须保持一致避免误导接收人。7.3 验证 AI Infra 专用路由vLLM 告警规则中需要包含labels: severity: critical notify_group: ai-infra当 Prometheus 无法持续抓取 vLLM/metrics并且告警达到for指定的持续时间后VllmTargetDown进入FIRING。由于该告警带有notify_groupai-infra因此匹配email-ai-infra子路由而不会发送给默认接收器。收到的 vLLM 告警邮件如下从邮件中可以看到alertnameVllmTargetDown namespaceai-demo notify_groupai-infra severitycritical7.4 验证恢复通知恢复 vLLM 服务使 Prometheus 能够重新抓取/metrics。当告警条件不再满足后Prometheus 会把告警恢复状态发送给 Alertmanager。由于两个邮件接收器都配置了sendResolved: true因此 Alertmanager 可以向对应收件人发送恢复通知。需要注意sendResolved只控制接收器是否发送恢复消息不负责决定告警什么时候恢复。告警是否恢复仍然由 Prometheus 的表达式计算结果决定。7.5 验证告警抑制准备两个同名、同命名空间但严重级别不同的告警alertnameVllmRequestWaiting namespaceai-demo severitywarningalertnameVllmRequestWaiting namespaceai-demo severitycritical当二者同时触发时critical 告警满足sourceMatchwarning 告警满足targetMatch并且两者的namespace和alertname相同因此 warning 通知会被抑制。预期结果是critical正常发送邮件 warning仍然处于 FIRING但显示为 Inhibited不再重复发送邮件八、总结本文完成了从 Prometheus 告警规则到 Alertmanager 邮件通知的完整链路PrometheusRule 判断告警 ↓ 告警经过 PENDING 后进入 FIRING ↓ Prometheus 将告警发送给 Alertmanager ↓ Alertmanager 按 namespace 和 alertname 分组 ↓ 根据 alertname 和 notify_group 匹配路由 ↓ critical 抑制同名 warning 和 info 通知 ↓ 通过 QQ SMTP 发送告警与恢复邮件最终实现效果如下普通告警 → email-default notify_groupai-infra → email-ai-infra Watchdog → null不发送邮件 同 namespace、同 alertname 的 critical → 抑制 warning 和 info 通知 告警恢复 → sendResolved 发送恢复邮件至此Prometheus 不仅可以发现 GPU 和 vLLM 服务异常Alertmanager 也能够按照告警类型和严重程度把通知发送给正确的接收人并通过分组和抑制减少重复告警。