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

Grafana — 仪表盘是一个问题

把问排名的面板搬进表格

在 TT Lab 中继续学习

目标

拿到一个四条折线图叠在一起的仪表板,把询问排名的面板迁移到表格中,并用 reduce、organize、joinByField 转换做出可以阅读的形式。

为什么重要

仪表板要回答的问题,大致有两个——“从什么时候开始这样的”和“哪个最糟糕”。前一个是随时间的变化,所以折线图合适;后一个是某一时刻排好序的列表,折线图回答不了。四个序列的值彼此接近时,线条会缠在一起,要知道排名,就得选一个时刻读出四个值,再在脑子里排序。变成八个的时候,没有人会去做这件事。表格就是由界面代替你完成这项排序的形式,而转换是把时间序列变成表格能读取的形式的工具。不过,如果把所有面板都改成表格,只会更糟——该迁移的,只有询问排名的面板。

步骤

  1. 用 lab-start-grafana 启动 Grafana,并把 /opt/lab/gfd/gfd-table/ranking.json 不做修改、原样上传到 Grafana(uid 是文件中写的 gfd-table,共 4 个面板)。用 curl 向 /api/dashboards/db 发送 POST 即可。上传之后,通过网页预览的 3000 端口打开一次,用眼睛确认四个面板是什么样子。
  2. 1 号面板把四个处理器的 p95 叠在一起绘制。选一个已经过去的时刻(比现在早 1 小时以上,在 6 小时之内),测出那一刻每个处理器的 p95,并在 /root/gfd-table/02-rank.txt 中用 at=、rank1=、rank2=、rank3=、rank4=、gap= 六行写下来。at 是 epoch 秒,rank1 到 rank4 是按 p95 从大到小依次写下的处理器名称,gap 是第 1 名与第 4 名的 p95 之差(秒)。
  3. 先量一量表格会有多少行。选一个已经过去的区间(1 小时以上,结束时间早于现在),把 1 号面板的查询在该区间上以 step 60 发出,并在 /root/gfd-table/03-table.txt 中写 start=、end=、series=、points=、rows= 五行——series 是把同一个查询按 end 时刻的瞬时值发出时返回的序列数,points 是区间查询为一个序列返回的点的数量,rows 是两者相乘的值。然后新建 5 号面板:type 为 table,查询与 1 号面板相同,把 format 设为 table,并以区间查询发出(不要把 instant 设为真)。
  4. 给 5 号面板添加 reduce 转换。在 transformations 数组中放入一个 id 为 reduce 的条目,options.mode 为 seriesToRows,options.reducers 只放入 lastNotNull 一项。查询仍保持为区间查询——因为要看的是由转换来完成压缩。保存修改后的仪表板。
  5. 在 5 号面板的 transformations 数组中,于 reduce 之后再追加一个 id 为 organize 的条目。通过 options.renameByName,把 Field 和 Last * 两列的名称分别改成不同的韩文名称,并通过 options.indexByName 把这两列的顺序固定为 0 和 1。保存修改后的仪表板。
  6. 新建 6 号面板:type 为 table,查询有两个——refId 为 A 的是 sum by (handler) (rate(http_requests_total{job="shop-api"}[5m])),refId 为 B 的是与 1 号面板相同的 p95 查询。两个查询都把 instant 设为真,把 format 设为 table。然后在 transformations 中放入一个 id 为 joinByField 的条目,并把 options.byField 设为 handler,把 options.mode 设为 outer。
  7. 把 6 号面板的默认单元格显示(fieldConfig.defaults.custom.cellOptions.type)保持为 auto,并在 fieldConfig.overrides 中追加一个只指明 p95 值列这一个列的覆盖。覆盖的 matcher.id 为 byName,properties 中放入 custom.cellOptions(把 type 设为 color-background)和 thresholds(2 个以上的档位,最后一个档位是数字阈值)。然后在 /root/gfd-table/07-cells.txt 中用 rule1=、rule2=、rule3= 三行,写下给表格单元格添加颜色时应遵守的规则,每条不少于 40 个字符,且互不相同。
  8. 2 号面板(핸들러별 5xx 비율,韩文,意为“按处理器的 5xx 比率”)也是询问排名的面板。把 type 改为 table,查询保持不变,把 instant 设为真、format 设为 table,并通过 organize 转换隐藏 Time 列(在 excludeByName 中把 Time 设为真),再给剩下的两列重命名。请不要动 4 号面板(전체 요청률,韩文,意为“总请求率”)——它不是询问排名的面板。然后把当前已上传的仪表板原样下载,保存到 /root/gfd-table/fixed.json(只取 .dashboard 正文),并在 /root/gfd-table/08-review.md 中用 R1= 到 R4= 四行,分别用不少于 30 个字符写明改了什么、为什么改。

参考

把四条折线图的仪表板原样上传

用 lab-start-grafana 启动 Grafana,并把 /opt/lab/gfd/gfd-table/ranking.json 不做修改、原样上传到 Grafana(uid 是文件中写的 gfd-table,共 4 个面板)。用 curl 向 /api/dashboards/db 发送 POST 即可。上传之后,通过网页预览的 3000 端口打开一次,用眼睛确认四个面板是什么样子。

保存 API 把仪表板正文放在 dashboard 键中,并同时发送 overwrite。从文件构造这种形式,用 jq -n --slurpfile 比较方便。Grafana 启动需要几十秒,请先看 /api/health 是否有响应。

尝试在折线图上读出排名

1 号面板把四个处理器的 p95 叠在一起绘制。选一个已经过去的时刻(比现在早 1 小时以上,在 6 小时之内),测出那一刻每个处理器的 p95,并在 /root/gfd-table/02-rank.txt 中用 at=、rank1=、rank2=、rank3=、rank4=、gap= 六行写下来。at 是 epoch 秒,rank1 到 rank4 是按 p95 从大到小依次写下的处理器名称,gap 是第 1 名与第 4 名的 p95 之差(秒)。

某一时刻的值,用 /api/v1/query 并附带 time= 即可。必须把时刻固定下来,以后重新量时才会得到相同的值。把响应中的 metric.handler 和 value[1] 一起提取出来,用 sort -rn 排序,排名马上就出来了。看看这四个值有多接近,想一想在折线图上能否靠眼睛分辨它们。

把同样的问题迁移到表格中

先量一量表格会有多少行。选一个已经过去的区间(1 小时以上,结束时间早于现在),把 1 号面板的查询在该区间上以 step 60 发出,并在 /root/gfd-table/03-table.txt 中写 start=、end=、series=、points=、rows= 五行——series 是把同一个查询按 end 时刻的瞬时值发出时返回的序列数,points 是区间查询为一个序列返回的点的数量,rows 是两者相乘的值。然后新建 5 号面板:type 为 table,查询与 1 号面板相同,把 format 设为 table,并以区间查询发出(不要把 instant 设为真)。

区间查询是 /api/v1/query_range,需要同时发送 start、end、step。返回的 JSON 中 data.result[0].values 的长度就是一个序列的点的数量。新建面板时,下载仪表板,往 panels 数组里再添加一个,然后原样重新保存即可。请把 y 往下调,让 gridPos 不要重叠。

通过转换,每个序列只保留一个数字

给 5 号面板添加 reduce 转换。在 transformations 数组中放入一个 id 为 reduce 的条目,options.mode 为 seriesToRows,options.reducers 只放入 lastNotNull 一项。查询仍保持为区间查询——因为要看的是由转换来完成压缩。保存修改后的仪表板。

reduce 会把一个序列压缩成一个数字。seriesToRows 会为每个序列生成一行,这一行中会有装有序列名称的 Field 列,以及所选计算值的列。lastNotNull 的列名是 Last *——下一步会用到这个名称。转换按顺序放在仪表板 JSON 中每个面板都有的 transformations 数组里。

让列名和顺序变得人能读懂

在 5 号面板的 transformations 数组中,于 reduce 之后再追加一个 id 为 organize 的条目。通过 options.renameByName,把 Field 和 Last * 两列的名称分别改成不同的韩文名称,并通过 options.indexByName 把这两列的顺序固定为 0 和 1。保存修改后的仪表板。

organize 是一个转换,同时完成隐藏列(excludeByName)、确定顺序(indexByName)和重命名(renameByName)。这三个选项都是以列名为键的对象。转换按数组中写下的顺序依次应用,所以前一个转换产生的列名会被后一个转换接收。Field 是序列名称列,Last * 是最后一个值的列。

按标签拼接两个查询,放进一个表格

新建 6 号面板:type 为 table,查询有两个——refId 为 A 的是 sum by (handler) (rate(http_requests_total{job="shop-api"}[5m])),refId 为 B 的是与 1 号面板相同的 p95 查询。两个查询都把 instant 设为真,把 format 设为 table。然后在 transformations 中放入一个 id 为 joinByField 的条目,并把 options.byField 设为 handler,把 options.mode 设为 outer。

瞬时值查询为每个序列给出一行,所以不用转换,表格也很短——因此这里不需要 reduce。两个结果中共有的列,就是标签名称本身,即 handler。拼接之后,值列用 Value #A、Value #B 来区分。outer 会保留只存在于一侧的行,inner 则只保留两侧都有的行。

颜色只加在带有阈值的一个列上

把 6 号面板的默认单元格显示(fieldConfig.defaults.custom.cellOptions.type)保持为 auto,并在 fieldConfig.overrides 中追加一个只指明 p95 值列这一个列的覆盖。覆盖的 matcher.id 为 byName,properties 中放入 custom.cellOptions(把 type 设为 color-background)和 thresholds(2 个以上的档位,最后一个档位是数字阈值)。然后在 /root/gfd-table/07-cells.txt 中用 rule1=、rule2=、rule3= 三行,写下给表格单元格添加颜色时应遵守的规则,每条不少于 40 个字符,且互不相同。

如果把颜色加在面板默认值上,连名称列也会被涂上颜色。颜色要有意义,就得传达越过了阈值这句话,而要做到这一点,它只能加在带有阈值的列上。覆盖是 fieldConfig.overrides 数组中的条目,加在哪一列由 matcher 决定,改什么由 properties 决定。请先确认连接之后 p95 值列的名称。

把仍然是折线的排名面板改成表格

2 号面板(핸들러별 5xx 비율,韩文,意为“按处理器的 5xx 比率”)也是询问排名的面板。把 type 改为 table,查询保持不变,把 instant 设为真、format 设为 table,并通过 organize 转换隐藏 Time 列(在 excludeByName 中把 Time 设为真),再给剩下的两列重命名。请不要动 4 号面板(전체 요청률,韩文,意为“总请求率”)——它不是询问排名的面板。然后把当前已上传的仪表板原样下载,保存到 /root/gfd-table/fixed.json(只取 .dashboard 正文),并在 /root/gfd-table/08-review.md 中用 R1= 到 R4= 四行,分别用不少于 30 个字符写明改了什么、为什么改。

以 format: table 接收瞬时值查询时,列有 Time、handler、Value 三个。对于某一时刻的列表,时间列不提供任何信息,所以把它隐藏。要让文件与界面不分道扬镳,修改之后必须重新下载。记录的四行,依次是为什么改成表格、为什么用瞬时值查询、加了哪些转换以及为什么、哪个面板为什么保留为折线图。