为了加一个标签,改的是文件
目标
读取仪表板的版本和保存消息,把两个版本的差异提取成人能读懂的形式,把数据源提取成变量使其可以迁移,然后改成由文件作为源头,并制作能自己检查文件与界面是否一致的工具。
为什么重要
如果只是把仪表板放进仓库,半年后界面和文件就已经完全不同了。因为没有人有理由去改文件,也没有任何东西阻止人在界面上修改。让文件来提供仪表板之后,界面保存就会被阻止,修改仪表板的唯一途径就成了文件——这个看起来不方便的约束,消除了界面与文件分道扬镳的途径。而且,版本保留着这个事实,与知道改了什么这个事实并不相同。如果不去掉每次保存都会变的字段,diff 就会整个变红,什么也告诉不了你。
步骤
- 用
lab-start-grafana启动 Grafana,并上传/opt/lab/gfd/gfd-as-code/start.json(uid 为gfd-code)。然后把 1 号面板的标题改为지금 요청률(韩文,意为“当前请求率”),并附上不少于 10 个字符的保存消息再次保存。 - 获取
http://127.0.0.1:3000/api/dashboards/uid/gfd-code/versions,在/root/gfd-as-code/02-versions.txt中写三行。latest=是最新版本号,message=是该版本的保存消息,first_message=是版本 1 的保存消息。三个值都按响应中写的原样抄写。 - 创建
/root/gfd-as-code/normalize.py。它接收仪表板 JSON 文件路径作为参数,只保留仪表板正文(如果被{"dashboard": ...}包着,就剥掉),去掉顶层的version、id、iteration,对键排序后输出到标准输出。保留面板的id。 - 创建
/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 个字符写明哪个面板的什么发生了怎样的变化。 - 用一个不存在的数据源 uid 发出查询,并在
/root/gfd-as-code/05-uid.txt中用missing_status=和ds_uid=两行写下返回的 HTTP 状态码,以及这个 Pod 中真实的 prometheus 数据源 uid。接着创建一个名为DS的datasource类型模板变量(目标是prometheus),并把两个面板都改为指向该变量(${DS})后保存。 - 把当前的仪表板规范化后保存到
/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会变为真。 - 把当前仪表板原样再保存一次,并在
/root/gfd-as-code/07-blocked.txt中用status=和message=写下返回的 HTTP 状态码和响应正文中的message。然后在procedure=行中,用不少于 40 个字符写明今后要修改这个仪表板,应该改什么、再做什么。 - 给仪表板添加
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。从第 6 步起,要更改 provisioning 路径并自己重新启动。 - 起始仪表板在
/opt/lab/gfd/gfd-as-code/start.json中。只读取它——第 8 步会把它作为反例重新使用。 - 版本列表响应的每一项中,该版本的正文都包含在
data里。 - provisioning 配置只在启动时读取一次。如果改了文件而界面没有变化,就是没有重新启动。
- 常见错误 ① 重新启动时漏掉
GF_PATHS_DATA。版本历史会整个消失。 - 常见错误 ② 在界面上修改却不改文件。第 6 步之后界面保存会被阻止,所以马上就会暴露。
- provisioning · 仪表板 JSON 模型 · 仪表板 HTTP API · 变量
不带保存消息地修改,就什么也不会留下
用 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)应当失败。无论给什么都通过的检查器比没有还糟。