路由树与燃烧率告警
目标
亲手编写 Alertmanager 配置,从路由树到抑制规则;创建两个从 SLO 反推得出的燃烧率告警;并编写一个用于在构建阶段阻止运行手册缺失的门禁。
为什么重要
告警的成功标准不是‘是否检测到了问题’,而是 ‘现在是否需要有人采取行动’。无法满足这一标准的告警即使准确,也会造成伤害。如果 400 条准确告警拖垮了值班人员的响应速度,准确率便毫无意义。因此,实施顺序是明确的:根据用户症状定义 SLO,从预算反推阈值,将长窗口和短窗口以 AND 连接,同时兼顾灵敏度与误报;再通过分组和抑制,将告警数量压缩到人能够阅读的程度,并强制要求页面告警附带运行手册。如果用‘历史最大值的 1.2 倍’之类的方法设定阈值,就无法解释为什么是这个值;而无法解释的阈值,每次发生事故后都会被逐渐调高。
步骤
- 创建
/root/pca-alerting/alertmanager.yml并编写根route。设置receiver: ticket-queue、group_by: ['alertname', 'cluster', 'slo']、group_wait: 30s、group_interval: 5m、repeat_interval: 4h。 - 在根路由的
routes第一项中添加子路由。设置matchers: ['severity="page"']、receiver: oncall-pager、group_wait: 10s、repeat_interval: 1h。 - 在
receivers中定义两个接收器。分别为ticket-queue和oncall-pager配置webhook_configs,url可以是任何以http://开头的内部地址。 - 编写
inhibit_rules的第一项。设置source_matchers: ['alertname="ClusterDown"', 'severity="page"']、target_matchers: ['severity=~"page|ticket"']、equal: ['cluster']。 - 创建
/root/pca-alerting/burnrate.yml,并在组下编写告警CheckoutErrorBudgetBurnFast。expr包含job:slo_errors:ratio_rate1h{job="checkout-api"} > (14.4 * 0.001)、job:slo_errors:ratio_rate5m{job="checkout-api"} > (14.4 * 0.001)和job:http_requests:rate5m{job="checkout-api"} > 1三个条件,并用and连接;设置for: 2m;在labels中设置severity: page和slo: checkout-availability;在annotations中设置summary和runbook_url。 - 在同一文件中添加告警
CheckoutErrorBudgetBurnSlow。expr包含job:slo_errors:ratio_rate6h{job="checkout-api"} > (6 * 0.001)和job:slo_errors:ratio_rate30m{job="checkout-api"} > (6 * 0.001)两个条件,并用and连接;设置for: 15m;在labels中设置severity: ticket和slo: checkout-availability。 - 编写
/root/pca-alerting/runbook-gate.sh。它接收规则文件路径作为第一个参数。如果存在任何labels.severity为page、但没有annotations.runbook_url的告警,则输出其名称并 以非 0 状态码退出;如果一个也没有,则以 0 退出。
参考
- 第 5 步和第 6 步的
expr也可以用expr: |块分成多行编写。评分时会忽略空白。 - 第 7 步会用两种文件进行验证:你创建的
burnrate.yml(应当通过),以及评分器创建的缺少运行手册的文件(应当被阻止)。如果连severity: ticket的告警也阻止,则会失败。 - 提示:使用
yq '.groups[].rules[] | select(...) | .alert' "$1"提取列表;如果列表不为空,则执行exit 1。 - 常见错误 1:在
group_by中加入instance。这样会收到与 Pod 数量相同的告警。 - 常见错误 2:在抑制规则中漏掉
equal。这样连其他集群的告警也会被抑制。
编写根路由
创建 /root/pca-alerting/alertmanager.yml 并编写根 route。设置 receiver: ticket-queue、group_by: ['alertname', 'cluster', 'slo']、group_wait: 30s、group_interval: 5m、repeat_interval: 4h。
根路由的 receiver 是没有匹配到子路由时使用的默认接收器。请记住,在 group_by 中加入哪些标签会决定告警数量。三个定时参数分别表示首次发送前的等待时间、组更新间隔,以及状态无变化时的重复通知间隔。
按严重级别设置子路由
在根路由的 routes 第一项中添加子路由。设置 matchers: ['severity="page"']、receiver: oncall-pager、group_wait: 10s、repeat_interval: 1h。
routes 是根路由下的列表,匹配顺序为从上到下。若未启用 continue,则会在首次匹配后停止。子路由会继承父路由的配置,只覆盖明确指定的值。
定义接收器
在 receivers 中定义两个接收器。分别为 ticket-queue 和 oncall-pager 配置 webhook_configs,url 可以是任何以 http:// 开头的内部地址。
如果路由按名称引用的接收器不在 receivers 列表中,配置将无法加载。此实验环境没有实际的发送目标,因此只需使用 Webhook URL 满足配置格式。
抑制规则
编写 inhibit_rules 的第一项。设置 source_matchers: ['alertname="ClusterDown"', 'severity="page"']、target_matchers: ['severity=~"page|ticket"']、equal: ['cluster']。
抑制会在根因告警处于活动状态时阻止下游症状告警。只有 equal 中列出的标签在双方取值相同时规则才成立,因此漏掉该标签会连其他集群的告警也一并抑制。
快速燃烧告警
创建 /root/pca-alerting/burnrate.yml,并在组下编写告警 CheckoutErrorBudgetBurnFast。expr 包含 job:slo_errors:ratio_rate1h{job="checkout-api"} > (14.4 * 0.001)、job:slo_errors:ratio_rate5m{job="checkout-api"} > (14.4 * 0.001) 和 job:http_requests:rate5m{job="checkout-api"} > 1 三个条件,并用 and 连接;设置 for: 2m;在 labels 中设置 severity: page 和 slo: checkout-availability;在 annotations 中设置 summary 和 runbook_url。
长窗口判断错误预算的燃烧速度,短窗口判断该状态当前是否仍在持续。再加上最低流量门槛,用 AND 连接三个条件。由于已经使用了长窗口,for 只需设置为较短时间,用于吸收短暂的数据缺失。
慢速燃烧告警
在同一文件中添加告警 CheckoutErrorBudgetBurnSlow。expr 包含 job:slo_errors:ratio_rate6h{job="checkout-api"} > (6 * 0.001) 和 job:slo_errors:ratio_rate30m{job="checkout-api"} > (6 * 0.001) 两个条件,并用 and 连接;设置 for: 15m;在 labels 中设置 severity: ticket 和 slo: checkout-availability。
当错误预算正在消耗但速度较慢时,无需唤醒值班人员,创建工单即可。按照惯例,短窗口取长窗口的十二分之一,并且 severity 不应设置为页面告警。
运行手册 CI 门禁脚本
编写 /root/pca-alerting/runbook-gate.sh。它接收规则文件路径作为第一个参数。如果存在任何 labels.severity 为 page、但没有 annotations.runbook_url 的告警,则输出其名称并 以非 0 状态码退出;如果一个也没有,则以 0 退出。
脚本接收规则文件路径作为第一个参数。使用 yq 找出 severity 为 page 且没有 runbook_url 注解的规则;如果存在任何一条,就输出其名称并以失败状态结束。不能阻止工单级别的告警。