TT Lab
开始
学习 学习路径 课程

CNPE — 云原生平台工程师

修复告警传递中断的三个环节

在 TT Lab 中继续学习

目标

在真实的 k3s 和 Prometheus Operator 上,依次修复没有被选中的规则、只触发而没有送达的告警,以及不发送通知的 Alertmanager。真实接收当前 Pod 的故障和恢复 Webhook 之后,实现一个区分证据不足与证据矛盾的诊断函数。

为什么重要

Kubernetes API 保存了规则,并不意味着 Prometheus 读取了这条规则。 告警触发了,也不意味着已经送达了人。只有亲手修复官方配置中的每一条边界,并通过 API 查询,才能区分配置错误、仍在生效中的状态,以及真实的送达失败。

在个人 VM 内会准备好 Operator v0.94.0、Prometheus v3.14.0、Alertmanager v0.34.0 和发放应用。 这是有实际运行进程的 k3s,不是 KWOK。安装可能需要几分钟。 预计作业需要 55 分钟。如果需要更多时间,请在到期前延长时间,并在结束前 下载需要的记录。VM 和文件会在会话结束时回收。不要应用到 VM 之外的生产集群。

已准备的内容

observe 查询一次,capture 步骤编号最多等待 150 秒,然后保存规定的 JSON。 两者都不会修改配置。如果超时,不要重新安装,而要先调查当前的配置和 行为,再重新观测。grade 步骤编号不会修改已保存的文件。

步骤

  1. 查询已准备的 ServiceMonitor 和 PrometheusRule,并用 capture 1 保存 /root/cnpe-alerts/baseline.json。确认指标目标 up 以及规则在 API 中的存储。一开始规则没有被选中,所以没有被加载。之后重新采集时,要记录当时的实际状态,不要编造最初的失败。
  2. 只把 team-lab 命名空间的 labhub.io/rules 标签修改为 allowed。不要把 Prometheus 的选择器扩大到所有命名空间。用 capture 2 保存 namespace.json,并确认是否还剩下其他的规则选择条件。
  3. 给 team-lab 的 PrometheusRule provision-ready 加上 platform=training 标签。不要修改规则的 spec。用 capture 3 保存 selected.json,确认 Prometheus API 中 provision 规则组是否真的已经加载。
  4. 向 VM 内部发放应用的 POST /mode 发送 JSON {"ready":false},制造依赖故障。用 capture 4 保存 firing.json。确认发放响应为 503、Prometheus 为 firing,但 Alertmanager 和 Webhook 中没有告警的状态。不要对外部服务注入故障。
  5. 在 platform-monitoring 的 Prometheus platform 中加入 spec.alerting.alertmanagers。这是一个 namespace=platform-monitoring、name=alertmanager、port=web 的条目。保持已准备好的查询 RBAC。用 capture 5 保存 received.json,把 Alertmanager 的接收与 Webhook 的未接收区分开。
  6. 在 /root/cnpe-alerts/alertmanager.yaml 中编写内部 receiver。route.receiver=local-webhook,group_by=[alertname],group_wait=1s,group_interval=5s,repeat_interval=1h。receivers 中的 local-webhook 使用 URL http://workbench.team-lab.svc:8080/hook과 以及 send_resolved=true。把它以同一个命名空间的 Secret alertmanager-platform、键 alertmanager.yaml 应用之后,用 capture 6 保存 notified.json。不要向这个 URL 之外发送。
  7. 先确认当前 Pod 的 firing Webhook。向 POST /mode 发送 {"ready":true} 进行恢复,并用 capture 7 保存 recovered.json。要确认 201 响应、活动告警已解除,以及同一个 Pod 的 firing 和 resolved 都已被接收。不要把旧 Pod 的 resolved 认定为这次恢复。
  8. 在 /root/cnpe-alerts/diagnose.py 中实现 diagnose(e)。按照下面的诊断契约,返回一个字符串。把没有观测、矛盾、缺失、以及用数字或字符串写成的布尔值,都按 unknown 处理,并且不要把存储、触发、接收混为一个成功。

第 8 步的诊断契约

e 中必须包含 observed、selected、loaded、firing、received、notified、recovered 七个真正的 bool。 received 和 notified 是同一个故障通过相应边界的历史, firing 则是当前是否触发。这个函数接收的是已经核对过的证据摘要。

参考

正常的指标采集 up 并不是业务成功的指标。本实验的 gauge 是用来 重现依赖状态的值,不是生产 SLO。不要仅凭组的 status 来确定单个告警的状态。 本步骤的记录会与 rule UID、pod UID 和规则内容进行核对。如果重新创建了 Pod, 也必须重新观测。这种文件比较不是密码学意义上的远程证明,也不是防作弊装置。 第 1、4、5、6 步使用保存下来的过去观测,第 2、3、7 步还会确认当前状态。即使在正常恢复之后, 也不要用正常的 JSON 覆盖过去的故障记录。接收记录只存在于应用内存中,重启之后就会消失。 步骤准备只会请求之前的配置,不会生成当前的答案或观测文件。第 7 步 在恢复之前,要先等待 firing 被接收。第 8 步是独立的代码任务。

不涉及外部 Webhook、电子邮件、聊天的发送,不涉及高可用的告警路径,也不涉及真实用户 SLO 的验证。 官方文档:规则选择 · ServiceMonitor 故障排查 · 告警状态。

区分指标与已存储的规则

查询已准备的 ServiceMonitor 和 PrometheusRule,并用 capture 1 保存 /root/cnpe-alerts/baseline.json。确认指标目标 up 以及规则在 API 中的存储。一开始规则没有被选中,所以没有被加载。之后重新采集时,要记录当时的实际状态,不要编造最初的失败。

即使 targets 是 up,rules 中也可能没有 provision。这两个 API 回答的是不同的问题。

把命名空间选择修改得窄一些

只把 team-lab 命名空间的 labhub.io/rules 标签修改为 allowed。不要把 Prometheus 的选择器扩大到所有命名空间。用 capture 2 保存 namespace.json,并确认是否还剩下其他的规则选择条件。

ruleNamespaceSelector 读取的是 Namespace 的标签。PrometheusRule 的标签是下一个条件。

修改标签并确认真实加载

给 team-lab 的 PrometheusRule provision-ready 加上 platform=training 标签。不要修改规则的 spec。用 capture 3 保存 selected.json,确认 Prometheus API 中 provision 规则组是否真的已经加载。

kubectl 成功之后,到真正加载还需要一些时间。不要只看声明文件,要确认 /api/v1/rules를 的结果。

已触发却没有送达的故障

向 VM 内部发放应用的 POST /mode 发送 JSON {"ready":false},制造依赖故障。用 capture 4 保存 firing.json。确认发放响应为 503、Prometheus 为 firing,但 Alertmanager 和 Webhook 中没有告警的状态。不要对外部服务注入故障。

for: 10s 要求条件在真实的评估中持续成立。/healthz 成功并不等于 /provision 成功。

连接 Alertmanager 并确认接收

在 platform-monitoring 的 Prometheus platform 中加入 spec.alerting.alertmanagers。这是一个 namespace=platform-monitoring、name=alertmanager、port=web 的条目。保持已准备好的查询 RBAC。用 capture 5 保存 received.json,把 Alertmanager 的接收与 Webhook 的未接收区分开。

在修复 receiver 之前,请先看 Prometheus 向哪个 Alertmanager 发送,以及 Endpoints 的查询权限。

通过内部 Webhook 真实发送

在 /root/cnpe-alerts/alertmanager.yaml 中编写内部 receiver。route.receiver=local-webhook,group_by=[alertname],group_wait=1s,group_interval=5s,repeat_interval=1h。receivers 中的 local-webhook 使用 URL http://workbench.team-lab.svc:8080/hook과 以及 send_resolved=true。把它以同一个命名空间的 Secret alertmanager-platform、键 alertmanager.yaml 应用之后,用 capture 6 保存 notified.json。不要向这个 URL 之外发送。

不仅 Secret 的名称,键的名称以及 route 中对 receiver 的引用也必须一致。也要等待配置生效的时间。

核对是否为这次故障的恢复

先确认当前 Pod 的 firing Webhook。向 POST /mode 发送 {"ready":true} 进行恢复,并用 capture 7 保存 recovered.json。要确认 201 响应、活动告警已解除,以及同一个 Pod 的 firing 和 resolved 都已被接收。不要把旧 Pod 的 resolved 认定为这次恢复。

不要只看组的 status 这一个值,而要比较 alerts 中各个告警的 status 和 pod 标签。

不会把空证据变成成功的诊断器

在 /root/cnpe-alerts/diagnose.py 中实现 diagnose(e)。按照下面的诊断契约,返回一个字符串。把没有观测、矛盾、缺失、以及用数字或字符串写成的布尔值,都按 unknown 处理,并且不要把存储、触发、接收混为一个成功。

确认所有必需字段都是真正的 bool 之后,从前面的边界开始看。如果下游成功与上游失败同时存在,就是矛盾。