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

可观测性

一夜响了七十次告警,用户却什么都没遇到

在 TT Lab 中继续学习

目标

在真实数据之上统计四条基于原因的告警和一条基于症状的告警,并根据呼叫数量和无效呼叫的时长,决定哪些发给人、哪些下放。

为什么重要

增加告警很容易,减少告警很难。难的原因是没有依据——“这条告警是不是太吵了?”是一种意见,而“这条告警在 12 小时内响了 65 次,其中 230 分钟用户根本没有遭遇任何事情”是数据。有了数据,会议就会变短。而且同样的数据还能决定持续时间这个旋钮的取值。告警设计不是挑选阈值,而是在人能够承受的呼叫总量之内,挑选要检测什么。

步骤

  1. 在 /root/obs-alert-symptom/rules/causes.yml 中写 Prometheus 规则文件。组名是 causes,其中有四条告警。CauseQueueDepth 在 queue_depth{job="shop-api",queue="orders"} 超过 100 时响起;CauseDiskPredict 在用 node_filesystem_avail_bytes{job="node"} 最近 1 小时的趋势预测 6 小时后低于 0 时响起;CauseLatencyTail 在以最近 5 分钟为准的 p99 延迟超过 0.5 秒时响起;CauseTrafficLow 在以最近 5 分钟为准的每秒请求数低于 55 时响起。必须能通过 promtool check rules 的检查。这一步不要加 for:。
  2. 创建 /root/obs-alert-symptom/count.py。以 python3 count.py '<경보 식>' <for 분>(占位符依次为告警表达式、for 的分钟数)调用时,按 1 分钟间隔扫描最近 12 小时,输出一行 episodes=<호출 수> minutes=<울린 분>(占位符依次为呼叫次数、响起的分钟数)。当条件为真的分钟连续持续了 L 分钟时,如果 L 大于等于 for + 1,就算作一次呼叫,响起的时间是 L - for 分钟。如果不给 for,就按 0 处理。
  3. 创建 /root/obs-alert-symptom/counts.tsv。没有表头,共四行,每行是以制表符分隔的三个字段 <경보이름> <호출 수> <울린 분>(占位符依次为告警名称、呼叫次数、响起的分钟数)。for 设为 0,写第 1 步规则文件中的四条告警,名称保持原样。值用第 2 步的工具求得。
  4. 从第 3 步中挑出响起次数最多的一条告警,把 for 依次改为 0、5、15 分钟,再统计一遍。在 /root/obs-alert-symptom/for-effect.tsv 中写三行,没有表头,每行是 <for 분> <호출 수> <울린 분>(占位符依次为 for 的分钟数、呼叫次数、响起的分钟数)。
  5. 在 /root/obs-alert-symptom/rules/symptom.yml 中写 symptom 组和一条告警 SymptomErrorRatio。条件是以最近 5 分钟为准的 5xx 响应比例超过 1%,并附上 for: 5m 和 runbook_url 注解。通过 promtool check rules 之后,把同一个表达式按 for 为 0 统计,在 /root/obs-alert-symptom/symptom.txt 中写一行 episodes=<수> minutes=<분>(占位符依次为次数、分钟数)。
  6. 创建 /root/obs-alert-symptom/overlap.py。以 python3 overlap.py '<원인 식>' '<증상 식>'(占位符依次为原因表达式、症状表达式)调用时,按 1 分钟间隔扫描最近 12 小时,输出一行 cause_minutes=<원인이 참인 분> outside_minutes=<그중 증상이 참이 아닌 분>(占位符依次为原因为真的分钟数、其中症状不为真的分钟数)。用这个工具分别测量四条基于原因的告警,在 /root/obs-alert-symptom/falsepages.tsv 中写四行以制表符分隔的三个字段 <경보이름> <원인 분> <증상 밖 분>(占位符依次为告警名称、原因分钟数、症状之外的分钟数)。
  7. 在 /root/obs-alert-symptom/triage.tsv 中写五行。每行是以制表符分隔的三个字段 <경보이름> <page|ticket|dashboard> <근거>(占位符依次为告警名称、page、ticket 或 dashboard 之一、依据),四条基于原因的告警和 SymptomErrorRatio 都要写。规则有两条——症状告警必须是 page;并且在第 6 步中症状之外的分钟数超过 60 分钟的原因告警,不能设为 page。依据中必须至少包含一个前面几步得到的数字,并且至少 20 个字符。
  8. 在 /root/obs-alert-symptom/rules/page.yml 中只放第 7 步归为 page 的告警。组名是 page,每条告警都必须有至少 5 分钟的 for:、labels 中的 severity: page、annotations 中的 runbook_url。通过 promtool check rules 之后,把这些告警按各自的 for 值统计,把呼叫次数的总和,用一行 pages_after=<수>(占位符为数量)写入 /root/obs-alert-symptom/after.txt。

参考

把四条基于原因的告警写成规则文件

在 /root/obs-alert-symptom/rules/causes.yml 中写 Prometheus 规则文件。组名是 causes,其中有四条告警。CauseQueueDepth 在 queue_depth{job="shop-api",queue="orders"} 超过 100 时响起;CauseDiskPredict 在用 node_filesystem_avail_bytes{job="node"} 最近 1 小时的趋势预测 6 小时后低于 0 时响起;CauseLatencyTail 在以最近 5 分钟为准的 p99 延迟超过 0.5 秒时响起;CauseTrafficLow 在以最近 5 分钟为准的每秒请求数低于 55 时响起。必须能通过 promtool check rules 的检查。这一步不要加 for:。

规则文件的骨架是 groups: → - name: → rules: → - alert: 和 expr:。预测是把范围向量和以秒为单位的未来时长交给 predict_linear。p99 是把按 le 汇总的 rate 交给 histogram_quantile。检查用 promtool check rules /root/obs-alert-symptom/rules/causes.yml。

制作统计会响起多少次的工具

创建 /root/obs-alert-symptom/count.py。以 python3 count.py '<경보 식>' <for 분>(占位符依次为告警表达式、for 的分钟数)调用时,按 1 分钟间隔扫描最近 12 小时,输出一行 episodes=<호출 수> minutes=<울린 분>(占位符依次为呼叫次数、响起的分钟数)。当条件为真的分钟连续持续了 L 分钟时,如果 L 大于等于 for + 1,就算作一次呼叫,响起的时间是 L - for 分钟。如果不给 for,就按 0 处理。

向 /api/v1/query_range 传入 start、end、step,就会得到范围数据。条件为假的分钟本身没有样本,所以只要构造出响应中包含的时间戳集合,在 1 分钟的网格上统计连续区间即可。只使用 Python 标准库(urllib.request、json、time)。

四条基于原因的告警在 12 小时内各响了多少次

创建 /root/obs-alert-symptom/counts.tsv。没有表头,共四行,每行是以制表符分隔的三个字段 <경보이름> <호출 수> <울린 분>(占位符依次为告警名称、呼叫次数、响起的分钟数)。for 设为 0,写第 1 步规则文件中的四条告警,名称保持原样。值用第 2 步的工具求得。

如果从规则文件中取出表达式交给工具,就能减少手工誊写的错误。这一步的关键是四个数字相差多大——其中一个会响 60 多次。

仅凭一个持续时间,呼叫能减少多少

从第 3 步中挑出响起次数最多的一条告警,把 for 依次改为 0、5、15 分钟,再统计一遍。在 /root/obs-alert-symptom/for-effect.tsv 中写三行,没有表头,每行是 <for 분> <호출 수> <울린 분>(占位符依次为 for 的分钟数、呼叫次数、响起的分钟数)。

条件表达式保持不变,只改第二个参数。除了呼叫次数减少,也要看看失去了什么——响起的分钟数减少,意味着检测相应变慢。

写一条基于症状的告警

在 /root/obs-alert-symptom/rules/symptom.yml 中写 symptom 组和一条告警 SymptomErrorRatio。条件是以最近 5 分钟为准的 5xx 响应比例超过 1%,并附上 for: 5m 和 runbook_url 注解。通过 promtool check rules 之后,把同一个表达式按 for 为 0 统计,在 /root/obs-alert-symptom/symptom.txt 中写一行 episodes=<수> minutes=<분>(占位符依次为次数、分钟数)。

比率是用 5xx 的 rate 之和除以全部 rate 之和。要点是,这条告警响的次数远远少于那些基于原因的告警。runbook_url 放在 annotations 之下。

在用户没有遭遇任何事情的时间里,响了多少分钟

创建 /root/obs-alert-symptom/overlap.py。以 python3 overlap.py '<원인 식>' '<증상 식>'(占位符依次为原因表达式、症状表达式)调用时,按 1 分钟间隔扫描最近 12 小时,输出一行 cause_minutes=<원인이 참인 분> outside_minutes=<그중 증상이 참이 아닌 분>(占位符依次为原因为真的分钟数、其中症状不为真的分钟数)。用这个工具分别测量四条基于原因的告警,在 /root/obs-alert-symptom/falsepages.tsv 中写四行以制表符分隔的三个字段 <경보이름> <원인 분> <증상 밖 분>(占位符依次为告警名称、原因分钟数、症状之外的分钟数)。

分别构造两个表达式的“为真的分钟集合”,再统计差集即可。把第 2 步工具的一半拆成函数,就可以直接使用。症状之外的分钟数越大,说明“用户完好无损,却只是把人叫醒”的时间越长。

用数字作依据,区分呼叫、工单和仪表板

在 /root/obs-alert-symptom/triage.tsv 中写五行。每行是以制表符分隔的三个字段 <경보이름> <page|ticket|dashboard> <근거>(占位符依次为告警名称、page、ticket 或 dashboard 之一、依据),四条基于原因的告警和 SymptomErrorRatio 都要写。规则有两条——症状告警必须是 page;并且在第 6 步中症状之外的分钟数超过 60 分钟的原因告警,不能设为 page。依据中必须至少包含一个前面几步得到的数字,并且至少 20 个字符。

症状之外的分钟数接近 0 的原因告警,意味着它几乎只在与症状相同的时间响起,所以可以保留为呼叫,节省调查时间。反过来,接近一半时间为真的告警如果设为呼叫,就会逼着人去建过滤器。

只保留用于呼叫的规则文件

在 /root/obs-alert-symptom/rules/page.yml 中只放第 7 步归为 page 的告警。组名是 page,每条告警都必须有至少 5 分钟的 for:、labels 中的 severity: page、annotations 中的 runbook_url。通过 promtool check rules 之后,把这些告警按各自的 for 值统计,把呼叫次数的总和,用一行 pages_after=<수>(占位符为数量)写入 /root/obs-alert-symptom/after.txt。

如果从规则文件中读取 alert 名称、expr、for 并交给第 2 步的工具,就无需手工相加。for 的值像 5m 这样是字符串,所以必须转换成以分钟为单位的数字。请与第 3 步的呼叫次数总和比较一下。