ダッシュボードを書き、それを検査する道具を作る
目標
最初に4つの質問を書き、その質問に答える状態ダッシュボードをJSONで自分で書きます。 そのあと、そのダッシュボードを検査するツールを作り、パネルごとに答える質問が書かれているか、パネルが6個を超えていないか、5xxを割合で見ているか、レイテンシをパーセンタイルで見ているかを、機械に代わりに問いかけさせます。
なぜ重要なのか
ダッシュボードは一方向にしか育ちません。「これも見えるといい」と付け足す根拠は誰でも挙げられますが、消すには「これは誰も見ていない」を証明しなければならず、その方法がないからです。パネルごとに答える質問を書いておけば、その質問がないパネルを消す根拠ができ、検査ツールをCIに組み込めば、そのルールが人の手を離れます。
ダッシュボードJSONのdiffは人が読みにくいものです。座標とフィールドがたくさん動くので、レビューがそのまま通りやすくなります。機械が代わりに問いかけてくれる場所を作ることが、このラボの本当の目的です。
このラボではGrafanaを起動しません
ここで扱うのはダッシュボードのJSONそのものです。Grafanaを起動して画面にする作業は、このコースの後半のラボで行います。ここではファイルと検査ツールだけを使います。
ステップ
- このダッシュボードが答える4つの質問を書いてください(保存先:
/root/gfq/01-questions.md)。質問は疑問符で終わる1文にし、質問ごとにmetric:で始まる行へ、どの指標で答えるかを書きます。4つのゴールデンシグナル(レイテンシ・トラフィック・エラー・飽和)をカバーする必要があります。 - 状態ダッシュボードを書いてください(保存先:
/root/gfq/dashboard.json)。uidが必要で、タイトルは疑問符で終わり、パネルは4–6個です。パネルごとにdescriptionへ、そのパネルが答える質問を疑問符で終わるように書きます。タイトルに5xxを含むパネルと、지연(韓国語で「遅延」を意味する語です)またはlatencyを含むパネルが、1つずつ必要です。 - 検査ツールを作成してください(ファイル:
/root/gfq/lint.py)。python3 lint.py <JSON 경로>を実行すると、違反ごとにVIOLATION <규칙id> <패널 제목>の1行を出力し、最後にviolations=<개수>を出力するようにします(プレースホルダーは順に、JSONファイルのパス、ルールid、パネルのタイトル、違反の個数です)。最初のルールはno-descriptionです。自分のダッシュボードに対して実行した結果を保存してください(保存先:/root/gfq/03-lint-basic.txt)。 - ルールを3つ追加してください。
too-many-panels(パネルが6個を超える)、error-count-not-ratio(タイトルに5xxがあるのに、クエリに割り算がない)、latency-not-quantile(タイトルに지연やlatencyがあるのに、クエリにhistogram_quantileがない)です。各ルールをわざと破ったファイルで試した結果を保存してください(保存先:/root/gfq/04-lint-full.txt)。 - 4つのルールをすべて破るダッシュボードをわざと作成してください(保存先:
/root/gfq/bad-dashboard.json)。検査ツールを実行した結果も保存してください(保存先:/root/gfq/05-bad.txt)。 - 診断用のパネルを別ダッシュボードへ切り出してください(保存先:
/root/gfq/diagnosis.json。パネルは3つ以上、uidは別の値)。状態ダッシュボードのlinksがそのuidを指すようにします。パネル数とリンクを確認した結果を保存してください(保存先:/root/gfq/06-split.txt)。 - ルールをもう1つ追加します。
description-not-questionは、説明があるのに疑問符で終わっていなければ違反です。叙述文の説明を持つファイルで試した結果を保存してください(保存先:/root/gfq/07-lint-e.txt)。 - レビューを書いてください(保存先:
/root/gfq/08-review.md)。## 30초 시험、## 지운 패널、## CI 에 거는 이유の3つの節が必要で(韓国語の見出しは順に「30秒テスト」「削除したパネル」「CIに組み込む理由」という意味です)、状態・診断・容量という3種類の区別を含める必要があります。
参考
- 標準ライブラリの
jsonだけを使います。このPodは外に出られないので、pip installはできません。 - パネルのクエリは
panel["targets"][i]["expr"]にあります。1つのパネルにtargetが複数ある場合があるので、つなげて調べるほうが安全です。 - Grafana APIでアップロードするときは
{"dashboard": {…}}の殻をかぶせるため、検査ツールがdoc.get("dashboard", doc)で両方を受け付けるようにしておけば、あとでそのまま使えます。 - 5xxの割合は
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))の形です。件数はトラフィックが増えると一緒に増えますが、ユーザーが経験する確率は割合です。 - p95は
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))です。leはバケットの境界なので、それだけを残して合算します。 - 採点ツールは、皆さんの検査ツールをわざとルールを破ったファイルに対して実行します。常に
violations=0を出す検査ツールは、ないよりも悪く、その場で不合格になります。
質問を先に書く
質問、パネルの順に進みます。逆にすると「この指標があるから描いてみよう」になり、そうして付けたパネルは、あとから誰にも解釈できません。
4つのゴールデンシグナルが、そのまま4つの質問になります。
- エラー: いま、ユーザーに失敗を返していますか
- レイテンシ: 遅くなりましたか
- トラフィック: どれだけ入ってきていますか
- 飽和: リソースはまもなく尽きますか
質問の行は疑問符で終え、すぐ下のmetric:で始まる行に、どの指標で答えるかを書いてください。エラーは件数ではなく割合であることが、指標の行に表れている必要があります。
状態ダッシュボードをJSONで書く
状態ダッシュボードのパネルは4–6個です。6個を超えていたら、たいてい診断用が混ざっていて、30秒以内に答えを返せなくなります。
パネルごとにdescriptionへ、そのパネルが答える質問を書いてください。1文で書けないパネルは、自分でも何を見ているのかわかっていないので、消す候補です。
タイトルも質問にします。タイトルが質問なら、その質問に答えないパネルが目立ちます。
採点ツールは、タイトルに5xxを含むパネルのクエリに割り算があるか、지연(韓国語で「遅延」を意味する語です)やlatencyを含むパネルがhistogram_quantileを使っているかを調べます。
検査ツールを作り、ルールを1つ入れる
出力形式が契約です。違反ごとにVIOLATION <규칙id> <패널 제목>を1行、最後にviolations=<개수>を1行出力します(プレースホルダーは順に、ルールid、パネルのタイトル、違反の個数です)。採点ツールがこの2つの形式をそのまま探します。
最初のルールno-descriptionは、パネルのdescriptionがないか、空であれば違反です。
json.load()で読み込み、doc.get("dashboard", doc)でGrafana APIの殻まで受け付けるようにしておけば、あとでそのまま使えます。
自分のダッシュボードに実行して、違反が出てはいけません。出るなら、ステップ2へ戻って説明を埋めてください。
ルールを3つ追加し、検出できるか試す
検査ツールは、合格させるものだけを確かめても半分です。わざとルールを破ったファイルを入れて、検出できるかまで見る必要があります。常に合格する検査ツールは、ないよりも悪いものです。守られていないのに守られていると知らせ、誰も報告しません。
3つのルールは次のとおりです。
too-many-panels: パネルが6個を超えています。ダッシュボード単位で1回だけ数えます。error-count-not-ratio: タイトルに5xxがあるのに、クエリに/がありません。latency-not-quantile: タイトルに지연(韓国語で「遅延」を意味する語です)やlatencyがあるのに、クエリにhistogram_quantileがありません。
試験用のファイルは/root/gfq/fixtures/のような場所に作っておき、検査ツールを実行してください。その出力を結果ファイルに残さなければ、ルール名が残りません。
4つのルールをすべて破ったダッシュボードを作る
よく見る「グラフの壁」が、まさにこの形です。ユーザーが経験することの代わりにプロセス内部の指標がずらりと並び、5xxは件数で描かれ、レイテンシは平均で、説明は空です。
4つを1つのファイルにすべて盛り込んでください。
- パネルが7個以上
- タイトルに
5xxがあるのに、クエリに割り算がないパネル - タイトルに
지연(韓国語で「遅延」を意味する語です)があるのに、histogram_quantileがないパネル descriptionが空のパネル
わざと作るこのファイルが、検査ツールの回帰テストになります。ルールを直すたびに、これに対して実行すれば確かめられます。
診断用パネルを別のダッシュボードへ移す
状態ダッシュボードに診断用のパネルを混ぜた瞬間、その画面は30秒以内に答えを返せなくなります。消すのではなく移すのです。移してリンクでつなげば、必要なときに一度で移動できます。
診断ダッシュボードには、原因を探すパネルを入れます。CPU・GC・コネクションプールの待ち・ハンドラー別のエラー率などです。uidは状態ダッシュボードと異なる値にしてください。
状態ダッシュボードのlinksは、次のような形です(コード内のプレースホルダーは診断ダッシュボードのuidです)。
"links": [{"type": "dashboards", "title": "왜 아픈가 — 진단", "url": "/d/<진단 uid>"}]
結果ファイルには、2つのダッシュボードのパネル数とリンクを確認した出力を残してください。
説明が質問になっているかまで調べる
説明が叙述文だと、「何を描いたか」だけが残り、「なぜ見るのか」が消えます。「5xxリクエストの割合を表示する」は、パネルを見ればわかることなので、何も足してくれません。
疑問符を強制すると、質問を書けないパネルが浮かび上がり、そのパネルが消す候補になります。
ルール名はdescription-not-questionです。説明がまったくない場合はno-descriptionなので、2つのルールが重なって二重に数えられないように分けて書いてください。
自分のダッシュボードは、引き続き違反がない状態でなければなりません。
インシデントレビューを書く
ダッシュボードを作ったら、インシデントテストをしてみます。午前3時にページを受けたと想定して、このダッシュボードを開き、30秒以内に正常かどうかを言えるでしょうか。言えないなら、パネルが足りないのではなく多すぎるのです。
3つの節を書きます。
## 30초 시험: どの順序で見て、何で判断するのか## 지운 패널: 何をなぜ外したのか。状態・診断・容量という3種類の区別が、ここに表れます## CI 에 거는 이유: 検査ツールが何を防いでくれるのか
タイトルが質問なら、その質問に答えないパネルが目立つことも、あわせて書いておくとよいでしょう。