六个月里一次都没变红的仪表板
目标
先计算默认阈值在我们的指标上意味着什么,然后让阈值从 SLO 中取得,分别使用绝对值阈值和百分比阈值,让它在颜色之外也用文字说话,并把面板的阈值与已有告警的阈值改成从同一个值取得。
为什么重要
仪表板的颜色替代了判断。所以颜色没有依据,判断也就没有依据。Grafana 的默认阈值是从 80 起变红,如果这个值留在一个值介于 0 到 1 之间的比率面板上,那么这个面板无论发生什么都是绿色。可以解释的阈值,只有由对用户做出的承诺算出来的那种——30 天可用性目标决定错误预算,错误预算决定阈值。此外还有两点:只靠颜色说话的界面,在色觉异常的人和黑白截图面前什么也说不出来,所以数值和文字必须同时存在;面板的阈值与告警的阈值如果被手工写在不同的文件里,总有一天会错位,出现“界面是红的,却没有人被呼叫”的时间。
步骤
- 用
lab-start-grafana启动 Grafana,并在/root/gfd-thresholds/dash.json中创建 uid 为gfd-thresh的仪表板并上传。面板只有一个,id是1,类型是stat,标题是5xx 비율 (기본 문턱 그대로 - 비교용)(韩文,意为“5xx 比率(保持默认阈值——用于对比)”),查询是sum(rate(http_requests_total{job="shop-api",status=~"5.."}[1h])) / sum(rate(http_requests_total{job="shop-api"}[1h])),单位标识符选 0..1 比率那一类。阈值保持 Grafana 的默认值——基线绿色,80 处为红色,方式是绝对值。然后在/root/gfd-thresholds/01-default.txt中写五行——now=<지금 이 쿼리가 내는 값>、red_at=<빨강 문턱 값>、max_possible=<이 쿼리가 낼 수 있는 가장 큰 값>、reachable=<빨강에 닿을 수 있으면 yes, 없으면 no>、reason=<왜 그런지 40자 이상>(占位符依次为此查询当前给出的值、红色阈值的值、此查询所能给出的最大值、能否达到红色(能则为 yes,不能则为 no)、原因(不少于 40 个字符))。 /opt/lab/gfd/gfd-thresholds/slo.yml中写有 30 天可用性目标。错误预算是 1 减去该目标得到的值。在同一个仪表板中再添加一个id为2的stat面板。标题是5xx 비율 (SLO 문턱)(韩文,意为“5xx 比率(SLO 阈值)”),查询与 1 号面板相同,单位也相同。阈值采用绝对值方式,共三个档位——基线是green,在错误预算值处为#EAB839,在它的两倍处为red。然后在/root/gfd-thresholds/02-slo.txt中写五行——budget=<오류 예산>、warn=<노랑 문턱>、crit=<빨강 문턱>、now=<지금 값>、color_now=<지금 값의 색>(占位符依次为错误预算、黄色阈值、红色阈值、当前值、当前值的颜色)。颜色按面板中所写的颜色字符串原样填写。- 在同一个仪表板中再添加一个
id为3的gauge面板。标题是남은 디스크(韩文,意为“剩余磁盘”),查询是node_filesystem_avail_bytes{job="node",mountpoint="/data"},单位是按 1024 换算的字节。轴的最小值是0,最大值是/opt/lab/gfd/gfd-thresholds/slo.yml中data_volume_bytes的值。阈值采用百分比方式(thresholds.mode为percentage),共三个档位——基线red,在20处为#EAB839,在40处为green。然后在/root/gfd-thresholds/03-percentage.txt中写四行——avail=<지금 남은 바이트>、max=<최댓값>、pct=<남은 비율을 퍼센트로, 소수 두 자리>、color=<지금 색>(占位符依次为当前剩余字节数、最大值、剩余比例的百分比(保留两位小数)、当前颜色)。 - 给 2 号面板再添加三个值映射。全部是
range规则,依次为:从0到黄色阈值是정상(韩文,意为“正常”),从黄色阈值到红色阈值是예산 소진 중(韩文,意为“预算消耗中”),从红色阈值到1是사고(韩文,意为“事故”)。并把该面板的options.textMode设为value_and_name,让数字和名称一起显示。在/root/gfd-thresholds/04-text.txt中写两行——now=<지금 값>、label=<지금 값에 걸리는 글자>(占位符依次为当前值、当前值命中的文字)。 - 创建
/root/gfd-thresholds/color.py。以python3 color.py <값> [패널 id](占位符依次为值、面板 id)调用时,它从 Grafana 读取该面板的阈值,并以一行颜色字符串输出该值会变成什么颜色(面板 id 的默认值是2)。规则与 Grafana 相同——把档位按值排序,选择值大于等于该档位的最后一个档位的颜色,把值为空的最前面的档位视为负无穷。然后用这个工具制作/root/gfd-thresholds/05-boundary.tsv。没有表头,共六行,每行用制表符分成两列<값> <색>(占位符依次为值、颜色),值依次为0、0.0049、0.005、0.0051、0.01、0.02。 /opt/lab/gfd/gfd-thresholds/alerts.yml是已经在生产环境中运行的告警规则。这条规则的阈值与 2 号面板的红色阈值目前彼此不同。请改成从同一个地方取得这两个数字。在/root/gfd-thresholds/thresholds.env中写WARN=<노랑 문턱>和CRIT=<빨강 문턱>两行(占位符依次为黄色阈值、红色阈值;值是第 2 步按 SLO 算出的),用该文件的CRIT值修改规则的阈值,并保存到/root/gfd-thresholds/alerts.yml。规则的其余部分保持不变。然后在/root/gfd-thresholds/06-match.tsv中写三行,没有表头,每行用制表符分成两列,依次写before、after、panel——原始规则的阈值、修改后规则的阈值、面板的红色阈值。- 在同一个仪表板中再添加一个
id为4的timeseries面板。标题是p99 응답 시간(韩文,意为“p99 响应时间”),查询是histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{job="shop-api"}[6h]))),单位是秒。阈值采用绝对值方式,共两个档位,基线是green,在/opt/lab/gfd/gfd-thresholds/slo.yml中latency_p99_target_seconds的值处为red。然后在/root/gfd-thresholds/07-latency.txt中写四行——p99_now=<지금 값>、threshold_s=<초로 적은 문턱>、threshold_ms=<같은 문턱을 밀리초로 환산한 값>、color=<지금 색>(占位符依次为当前值、以秒表示的阈值、把同一阈值换算成毫秒的值、当前颜色)。 /opt/lab/gfd/gfd-thresholds/broken.json是从生产环境取回的仪表板(uid 为gfd-thresh-fix)。四个面板的阈值都有缺陷。1 号保留了默认阈值,导致达不到(把红色降到 SLO 的crit值),2 号是百分比方式却没有最小值、最大值(填入 0 和data_volume_bytes),3 号是越大越好的指标,但档位顺序是反的(基线必须是红色,往上必须是绿色),4 号是在以秒为单位的面板中,把毫秒数字放进了阈值(改成latency_p99_target_seconds)。把修正后的仪表板以 uidgfd-thresh-fix上传,并在/root/gfd-thresholds/08-report.tsv中写四行,没有表头,每行用制表符分成三列<패널 id> <결함 코드> <무엇이 틀렸었나, 20자 이상이고 숫자를 하나 이상 포함>(占位符依次为面板 id、缺陷代码、原来错在哪里(不少于 20 个字符且至少包含一个数字))。缺陷代码是unreachable、no-minmax、direction、scale之一,各行按面板 id 的顺序排列。
参考
- 工作目录是
/root/gfd-thresholds。Grafana 用lab-start-grafana启动,也可以通过网页预览的 3000 端口用眼睛查看。 - 仪表板可以在界面上创建,也可以通过 API 上传。评分器只看已经上传到 Grafana 的结果。上传后的结果用
curl -s http://127.0.0.1:3000/api/dashboards/uid/gfd-thresh | jq '.dashboard.panels'确认。 - 请把面板的
datasource留空。数据源 uid 在每个 Pod 中生成的都不同。 - 素材在
/opt/lab/gfd/gfd-thresholds中——写有目标的slo.yml、已经在生产环境中运行的alerts.yml,以及要在第 8 步修正的broken.json。 - 这个环境无法判定实际涂出来的颜色(没有图像渲染器)。评分器只看阈值模型和查询结果,颜色则按 Grafana 使用的规则原样计算来确认。需要用眼睛看的内容,请通过网页预览打开。
- 常见错误:把阈值定成“好看的数字”。能够辩护的阈值,只有由目标算出来的那种。
- 常见错误:对越大越好的指标保持档位顺序不变。完全空了的磁盘会显示为绿色。
- Configure thresholds · Configure standard options · Configure value mappings · Prometheus - Alerting rules · Implementing SLOs (SRE Workbook)
追问默认阈值 80 在我们的指标上意味着什么
用 lab-start-grafana 启动 Grafana,并在 /root/gfd-thresholds/dash.json 中创建 uid 为 gfd-thresh 的仪表板并上传。面板只有一个,id 是 1,类型是 stat,标题是 5xx 비율 (기본 문턱 그대로 - 비교용)(韩文,意为“5xx 比率(保持默认阈值——用于对比)”),查询是 sum(rate(http_requests_total{job="shop-api",status=~"5.."}[1h])) / sum(rate(http_requests_total{job="shop-api"}[1h])),单位标识符选 0..1 比率那一类。阈值保持 Grafana 的默认值——基线绿色,80 处为红色,方式是绝对值。然后在 /root/gfd-thresholds/01-default.txt 中写五行——now=<지금 이 쿼리가 내는 값>、red_at=<빨강 문턱 값>、max_possible=<이 쿼리가 낼 수 있는 가장 큰 값>、reachable=<빨강에 닿을 수 있으면 yes, 없으면 no>、reason=<왜 그런지 40자 이상>(占位符依次为此查询当前给出的值、红色阈值的值、此查询所能给出的最大值、能否达到红色(能则为 yes,不能则为 no)、原因(不少于 40 个字符))。
默认阈值是 {"mode": "absolute", "steps": [{"color": "green", "value": null}, {"color": "red", "value": 80}]}。最前面那个档位的 value 为 null,就是基线,表示负无穷。这个查询是比率,所以分子等于分母时是最大值——把这个值与 80 比一比。
让阈值从目标中取得
/opt/lab/gfd/gfd-thresholds/slo.yml 中写有 30 天可用性目标。错误预算是 1 减去该目标得到的值。在同一个仪表板中再添加一个 id 为 2 的 stat 面板。标题是 5xx 비율 (SLO 문턱)(韩文,意为“5xx 比率(SLO 阈值)”),查询与 1 号面板相同,单位也相同。阈值采用绝对值方式,共三个档位——基线是 green,在错误预算值处为 #EAB839,在它的两倍处为 red。然后在 /root/gfd-thresholds/02-slo.txt 中写五行——budget=<오류 예산>、warn=<노랑 문턱>、crit=<빨강 문턱>、now=<지금 값>、color_now=<지금 값의 색>(占位符依次为错误预算、黄色阈值、红色阈值、当前值、当前值的颜色)。颜色按面板中所写的颜色字符串原样填写。
如果目标是 0.995,错误预算就是 0.005。不要凭手工挑选颜色,而要用规则来确定——Grafana 会把阈值按值排序,然后选择“值大于等于该阈值”的最后一个档位的颜色。边界是包含的。当前值可以通过 promq 或数据源代理发出查询得到。
绝对值阈值和百分比阈值回答的是不同的问题
在同一个仪表板中再添加一个 id 为 3 的 gauge 面板。标题是 남은 디스크(韩文,意为“剩余磁盘”),查询是 node_filesystem_avail_bytes{job="node",mountpoint="/data"},单位是按 1024 换算的字节。轴的最小值是 0,最大值是 /opt/lab/gfd/gfd-thresholds/slo.yml 中 data_volume_bytes 的值。阈值采用百分比方式(thresholds.mode 为 percentage),共三个档位——基线 red,在 20 处为 #EAB839,在 40 处为 green。然后在 /root/gfd-thresholds/03-percentage.txt 中写四行——avail=<지금 남은 바이트>、max=<최댓값>、pct=<남은 비율을 퍼센트로, 소수 두 자리>、color=<지금 색>(占位符依次为当前剩余字节数、最大值、剩余比例的百分比(保留两位小数)、当前颜色)。
百分比阈值比较的不是值本身,而是“在最小值与最大值之间处于哪个位置”。所以如果不填最大值,Grafana 就会从界面上的数据猜测,每次改变时间范围,颜色都会不同。剩余容量是越大越好的值,所以档位的顺序要反过来——基线是红色。
不只靠颜色说话
给 2 号面板再添加三个值映射。全部是 range 规则,依次为:从 0 到黄色阈值是 정상(韩文,意为“正常”),从黄色阈值到红色阈值是 예산 소진 중(韩文,意为“预算消耗中”),从红色阈值到 1 是 사고(韩文,意为“事故”)。并把该面板的 options.textMode 设为 value_and_name,让数字和名称一起显示。在 /root/gfd-thresholds/04-text.txt 中写两行——now=<지금 값>、label=<지금 값에 걸리는 글자>(占位符依次为当前值、当前值命中的文字)。
一个值映射项的形式是 {"type": "range", "options": {"from": 0, "to": 0.005, "result": {"text": "정상", "index": 0}}}。映射从上往下扫描,第一个命中的规则胜出,并且两端都包含,所以边界值由前面的规则取走。这一步的理由是:对色觉异常的人来说,红色和绿色无法区分。
用实际的值确认阈值的顺序和边界
创建 /root/gfd-thresholds/color.py。以 python3 color.py <값> [패널 id](占位符依次为值、面板 id)调用时,它从 Grafana 读取该面板的阈值,并以一行颜色字符串输出该值会变成什么颜色(面板 id 的默认值是 2)。规则与 Grafana 相同——把档位按值排序,选择值大于等于该档位的最后一个档位的颜色,把值为空的最前面的档位视为负无穷。然后用这个工具制作 /root/gfd-thresholds/05-boundary.tsv。没有表头,共六行,每行用制表符分成两列 <값> <색>(占位符依次为值、颜色),值依次为 0、0.0049、0.005、0.0051、0.01、0.02。
仪表板用 curl -s http://127.0.0.1:3000/api/dashboards/uid/gfd-thresh 获取,从 .dashboard.panels[] | select(.id == 2) | .fieldConfig.defaults.thresholds.steps 中取出档位。只使用标准库(json、sys、urllib.request)。边界值 0.005 是前一个颜色还是后一个颜色,是这一步的核心。
消除“界面是红的却没有人被呼叫”的地方
/opt/lab/gfd/gfd-thresholds/alerts.yml 是已经在生产环境中运行的告警规则。这条规则的阈值与 2 号面板的红色阈值目前彼此不同。请改成从同一个地方取得这两个数字。在 /root/gfd-thresholds/thresholds.env 中写 WARN=<노랑 문턱> 和 CRIT=<빨강 문턱> 两行(占位符依次为黄色阈值、红色阈值;值是第 2 步按 SLO 算出的),用该文件的 CRIT 值修改规则的阈值,并保存到 /root/gfd-thresholds/alerts.yml。规则的其余部分保持不变。然后在 /root/gfd-thresholds/06-match.tsv 中写三行,没有表头,每行用制表符分成两列,依次写 before、after、panel——原始规则的阈值、修改后规则的阈值、面板的红色阈值。
把原始文件复制到工作目录后,只需改数字。规则文件的语法用 promtool check rules /root/gfd-thresholds/alerts.yml 确认——评分器也使用同一个命令。如何新建告警规则,这里不涉及。这一步要看的只有一点:两个数字是否相同。
应用 ① ——延迟阈值要跟随面板的单位
在同一个仪表板中再添加一个 id 为 4 的 timeseries 面板。标题是 p99 응답 시간(韩文,意为“p99 响应时间”),查询是 histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{job="shop-api"}[6h]))),单位是秒。阈值采用绝对值方式,共两个档位,基线是 green,在 /opt/lab/gfd/gfd-thresholds/slo.yml 中 latency_p99_target_seconds 的值处为 red。然后在 /root/gfd-thresholds/07-latency.txt 中写四行——p99_now=<지금 값>、threshold_s=<초로 적은 문턱>、threshold_ms=<같은 문턱을 밀리초로 환산한 값>、color=<지금 색>(占位符依次为当前值、以秒表示的阈值、把同一阈值换算成毫秒的值、当前颜色)。
阈值必须用与面板所绘制的值相同的单位来写。这个面板的值是以秒为单位的,所以如果目标是 1 秒,阈值是 1,而不是 1000。如果写成毫秒,这个面板就永远不会变红——这与第 1 步看到的是同一类事故。颜色可以把面板 id 传给第 5 步做出的工具来得到。
应用 ② ——把生产仪表板的阈值缺陷全部修正并提交
/opt/lab/gfd/gfd-thresholds/broken.json 是从生产环境取回的仪表板(uid 为 gfd-thresh-fix)。四个面板的阈值都有缺陷。1 号保留了默认阈值,导致达不到(把红色降到 SLO 的 crit 值),2 号是百分比方式却没有最小值、最大值(填入 0 和 data_volume_bytes),3 号是越大越好的指标,但档位顺序是反的(基线必须是红色,往上必须是绿色),4 号是在以秒为单位的面板中,把毫秒数字放进了阈值(改成 latency_p99_target_seconds)。把修正后的仪表板以 uid gfd-thresh-fix 上传,并在 /root/gfd-thresholds/08-report.tsv 中写四行,没有表头,每行用制表符分成三列 <패널 id> <결함 코드> <무엇이 틀렸었나, 20자 이상이고 숫자를 하나 이상 포함>(占位符依次为面板 id、缺陷代码、原来错在哪里(不少于 20 个字符且至少包含一个数字))。缺陷代码是 unreachable、no-minmax、direction、scale 之一,各行按面板 id 的顺序排列。
用 cp /opt/lab/gfd/gfd-thresholds/broken.json /root/gfd-thresholds/fixed.json 复制一份副本,修改后上传即可。有的面板保留颜色字符串不动,只需改值和顺序,有的面板则需要调换颜色的位置。修改之后,用第 5 步的工具输入几个值,方向对不对马上就能看出来——把面板 id 作为第二个参数传入即可(不过那个工具查看的是 uid 为 gfd-thresh 的仪表板)。