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

Grafana — 仪表盘是一个问题

为了加一个标签,改的是文件

在 TT Lab 中继续学习

目标

读取仪表板的版本和保存消息,把两个版本的差异提取成人能读懂的形式,把数据源提取成变量使其可以迁移,然后改成由文件作为源头,并制作能自己检查文件与界面是否一致的工具。

为什么重要

如果只是把仪表板放进仓库,半年后界面和文件就已经完全不同了。因为没有人有理由去改文件,也没有任何东西阻止人在界面上修改。让文件来提供仪表板之后,界面保存就会被阻止,修改仪表板的唯一途径就成了文件——这个看起来不方便的约束,消除了界面与文件分道扬镳的途径。而且,版本保留着这个事实,与知道改了什么这个事实并不相同。如果不去掉每次保存都会变的字段,diff 就会整个变红,什么也告诉不了你。

步骤

  1. 用 lab-start-grafana 启动 Grafana,并上传 /opt/lab/gfd/gfd-as-code/start.json(uid 为 gfd-code)。然后把 1 号面板的标题改为 지금 요청률(韩文,意为“当前请求率”),并附上不少于 10 个字符的保存消息再次保存。
  2. 获取 http://127.0.0.1:3000/api/dashboards/uid/gfd-code/versions,在 /root/gfd-as-code/02-versions.txt 中写三行。latest= 是最新版本号,message= 是该版本的保存消息,first_message= 是版本 1 的保存消息。三个值都按响应中写的原样抄写。
  3. 创建 /root/gfd-as-code/normalize.py。它接收仪表板 JSON 文件路径作为参数,只保留仪表板正文(如果被 {"dashboard": ...} 包着,就剥掉),去掉顶层的 version、id、iteration,对键排序后输出到标准输出。保留面板的 id。
  4. 创建 /root/gfd-as-code/diff.sh。它接收两个版本号作为参数,把两个版本规范化后进行比较,相同则不输出任何内容并以 0 结束,不同则输出差异并以非 0 的值结束。然后查看 bash /root/gfd-as-code/diff.sh 1 2 的结果,在 /root/gfd-as-code/04-diff.txt 的 changed= 行中,用不少于 20 个字符写明哪个面板的什么发生了怎样的变化。
  5. 用一个不存在的数据源 uid 发出查询,并在 /root/gfd-as-code/05-uid.txt 中用 missing_status= 和 ds_uid= 两行写下返回的 HTTP 状态码,以及这个 Pod 中真实的 prometheus 数据源 uid。接着创建一个名为 DS 的 datasource 类型模板变量(目标是 prometheus),并把两个面板都改为指向该变量(${DS})后保存。
  6. 把当前的仪表板规范化后保存到 /root/gfd-as-code/dash/gfd-code.json,在 /root/gfd-as-code/provisioning/dashboards/lab.yml 中写入读取该文件夹的 provisioning 配置,然后用 GF_PATHS_PROVISIONING=/root/gfd-as-code/provisioning 重新启动 Grafana。仪表板改为来自文件后,查询响应中的 meta.provisioned 会变为真。
  7. 把当前仪表板原样再保存一次,并在 /root/gfd-as-code/07-blocked.txt 中用 status= 和 message= 写下返回的 HTTP 状态码和响应正文中的 message。然后在 procedure= 行中,用不少于 40 个字符写明今后要修改这个仪表板,应该改什么、再做什么。
  8. 给仪表板添加 gitops 标签。但必须通过修改文件来完成。然后创建 /root/gfd-as-code/verify.sh——它接收仪表板 JSON 文件路径作为参数(默认值为 /root/gfd-as-code/dash/gfd-code.json),把该文件与当前界面规范化后进行比较,相同则以 0 结束,不同则以非 0 的值结束。最后在 /root/gfd-as-code/08-bundle.md 中写两行:files=(要放进仓库的文件列表)和 revert=(回滚方法,不少于 40 个字符)。

参考

不带保存消息地修改,就什么也不会留下

用 lab-start-grafana 启动 Grafana,并上传 /opt/lab/gfd/gfd-as-code/start.json(uid 为 gfd-code)。然后把 1 号面板的标题改为 지금 요청률(韩文,意为“当前请求率”),并附上不少于 10 个字符的保存消息再次保存。

保存 API 的正文是 {"dashboard": ..., "overwrite": true, "message": "..."}。如果省略消息,版本列表里就只剩下时间——半年后判断是否要回滚到这个版本时,就没有依据了。

版本列表实际留下的内容

获取 http://127.0.0.1:3000/api/dashboards/uid/gfd-code/versions,在 /root/gfd-as-code/02-versions.txt 中写三行。latest= 是最新版本号,message= 是该版本的保存消息,first_message= 是版本 1 的保存消息。三个值都按响应中写的原样抄写。

看到版本 1 的消息,你可能会吃惊——它与你亲自写下并发送的不一样。这正是这一步要确认的事实。用 jq 分别提取 max_by(.version) 和 select(.version == 1) 即可。

让两个版本可以比较

创建 /root/gfd-as-code/normalize.py。它接收仪表板 JSON 文件路径作为参数,只保留仪表板正文(如果被 {"dashboard": ...} 包着,就剥掉),去掉顶层的 version、id、iteration,对键排序后输出到标准输出。保留面板的 id。

如果混有每次保存都会变的字段,diff 就会整个变红。相同的输入必须总是得到相同的输出,所以请固定键的顺序。Python 的 json.dumps 提供这个选项。

改了什么——双向都能工作的工具

创建 /root/gfd-as-code/diff.sh。它接收两个版本号作为参数,把两个版本规范化后进行比较,相同则不输出任何内容并以 0 结束,不同则输出差异并以非 0 的值结束。然后查看 bash /root/gfd-as-code/diff.sh 1 2 的结果,在 /root/gfd-as-code/04-diff.txt 的 changed= 行中,用不少于 20 个字符写明哪个面板的什么发生了怎样的变化。

各版本的正文包含在版本列表响应的 data 中。diff 命令在有差异时以 1 结束,所以直接使用这个退出码即可。务必测试给出两个相同版本时是否也能正常工作——只有一个方向有效的工具,无法区分“没有差异”和“工具坏了”。

迁移后面板为什么会变空

用一个不存在的数据源 uid 发出查询,并在 /root/gfd-as-code/05-uid.txt 中用 missing_status= 和 ds_uid= 两行写下返回的 HTTP 状态码,以及这个 Pod 中真实的 prometheus 数据源 uid。接着创建一个名为 DS 的 datasource 类型模板变量(目标是 prometheus),并把两个面板都改为指向该变量(${DS})后保存。

数据源代理路径是 /api/datasources/proxy/uid/<uid>/api/v1/query。即使填入不存在的 uid,界面上显示的也不是错误,而是空图表——所以迁移后很难找到原因。变量放在 templating.list 中,并把面板的 datasource.uid 改成变量引用字符串。

让文件成为源头

把当前的仪表板规范化后保存到 /root/gfd-as-code/dash/gfd-code.json,在 /root/gfd-as-code/provisioning/dashboards/lab.yml 中写入读取该文件夹的 provisioning 配置,然后用 GF_PATHS_PROVISIONING=/root/gfd-as-code/provisioning 重新启动 Grafana。仪表板改为来自文件后,查询响应中的 meta.provisioned 会变为真。

provisioning 配置只在 Grafana 启动时读取一次——只写了文件而不重新启动,什么也不会发生。配置中包含 apiVersion、providers,options.path 指向存放仪表板 JSON 的文件夹。重新启动时,数据文件夹(GF_PATHS_DATA)必须保持不变,版本历史才会保留下来。

现在无法在界面上保存了

把当前仪表板原样再保存一次,并在 /root/gfd-as-code/07-blocked.txt 中用 status= 和 message= 写下返回的 HTTP 状态码和响应正文中的 message。然后在 procedure= 行中,用不少于 40 个字符写明今后要修改这个仪表板,应该改什么、再做什么。

这是会被拒绝的请求,所以什么都不会改变。放心地发送就行。响应码可以用 curl -w '%{http_code}' 获得。这个看起来不方便的约束,正是防止界面与文件分道扬镳的装置。

应用——修改文件来改变界面,并检查两者是否一致

给仪表板添加 gitops 标签。但必须通过修改文件来完成。然后创建 /root/gfd-as-code/verify.sh——它接收仪表板 JSON 文件路径作为参数(默认值为 /root/gfd-as-code/dash/gfd-code.json),把该文件与当前界面规范化后进行比较,相同则以 0 结束,不同则以非 0 的值结束。最后在 /root/gfd-as-code/08-bundle.md 中写两行:files=(要放进仓库的文件列表)和 revert=(回滚方法,不少于 40 个字符)。

修改文件之后,必须让 Grafana 重新读取,才会反映到界面上(与前面的步骤中做过的一样)。请对检查器做双向测试——给它仓库中的副本应当通过,给它另一个仪表板文件(例如 /opt/lab/gfd/gfd-as-code/start.json)应当失败。无论给什么都通过的检查器比没有还糟。