TT Lab
はじめる
学ぶ 学習パス コース

Grafana — ダッシュボードは問いだ

このダッシュボードは1分に何回投げているのか

TT Labで続きを見る

目標

ダッシュボード1つのクエリ回数・系列数・点の数を実際に測って値段を付け、バジェットをファイルと検査ツールで書いたうえで、そのバジェットに収まる版を作って提出します。

なぜ重要なのか

ダッシュボードはタダのように見えます。パネルを1つ追加する作業はクリック3回で、コストは別のチームのPrometheusで発生するからです。そのため、ダッシュボードはいつも、重くなる方向にしか育ちません。値段を付ける計算そのものは、難しくありません。パネルごとに付いているクエリを足し、一覧をサーバーに問い合わせる変数を足すと、1回開くときのクエリ数になり、ここに自動リフレッシュの間隔を掛けると、毎分のクエリ数になります。クエリ10個で10秒リフレッシュなら、1分に60回、1日に8万回で、その画面を誰も見ていない夜にも、同じように飛んでいきます。この数字を一度数えてみたチームと、数えてみていないチームでは、ダッシュボードへの向き合い方が変わります。

ステップ

  1. lab-start-grafanaでGrafanaを起動し、/opt/lab/gfd/gfd-perf/heavy.jsonを、修正せず、そのままGrafanaにアップロードしてください(uidはファイルに書かれたgfd-perf、パネルは8つ)。curlで/api/dashboards/dbにPOSTすればよいです。このダッシュボードは、このラボが終わるまで原本のまま残しておきます。縮小した版は、最後のステップで別のuidとして別に保存します。
  2. Grafanaにアップロードされたダッシュボードをダウンロードして、数えてください。/root/gfd-perf/02-count.txtに、panels=、targets=、var_queries=、open=の4行を書きます。panelsは行(row)を除いたパネルの数、targetsはすべてのパネルのクエリの数、var_queriesはtemplating.listのうちtypeがqueryの変数の数、openはtargetsとvar_queriesを足した値で、ダッシュボードを1回開くときのクエリ数です。
  3. パネルごとに付いているクエリを実際に投げて、返ってくる系列の数を測ってください。過ぎた時刻を1つ選び(今より5分以上前、6時間以内)、すべてのクエリをその時刻の瞬間値として投げます。/root/gfd-perf/03-series.txtに、at=、total=、top_id=、top_series=の4行を書きます。atは選んだ時刻のエポック秒、totalはダッシュボードのすべてのクエリが返す系列の数の合計、top_idは系列を最も多く返すパネルのid、top_seriesはそのパネルの系列の数です。いちばん重いパネルのグラフに、線が何本描かれるかを考えてみてください。
  4. 過ぎた区間を1つ選び(2時間以上、終わりは今より前)、パネル3のp95のクエリを、同じ区間でstepだけを変えて2回投げてください。1回はstep=15、もう1回はstep=300です。/root/gfd-perf/04-points.txtに、start=、end=、points_fine=、points_coarse=の4行を書きます。points_fineはstep 15のとき、points_coarseはstep 300のときに、系列1つに返ってきた点の数です。
  5. ダッシュボードJSONの自動リフレッシュ間隔を読んで、/root/gfd-perf/05-rate.txtに、refresh=、refresh_sec=、targets=、per_min=の4行を書いてください。refreshはJSONに書かれた文字列そのまま、refresh_secはそれを秒に換算した整数、targetsはステップ2で数えたクエリの数、per_minはtargetsに60 / refresh_secを掛けた値です。このラボの計算では、自動リフレッシュはパネルのクエリだけを再び投げ、変数クエリは再び投げないものとして扱います。
  6. パネル3のp95のクエリは、バケット系列48個を読んで、毎回分位数を計算します。/etc/prometheus/rules/gfd-perf.ymlに、グループ名gfd-perf、interval15秒、レコーディングルール1つを置いてください。recordはhandler:http_request_duration_seconds:p95、exprはパネル3のp95のクエリそのままです。promtool check rules /etc/prometheus/rules/gfd-perf.ymlで確認したあと、curl -X POST http://127.0.0.1:9090/-/reloadで反映し、20秒以上待ってから、promq 'handler:http_request_duration_seconds:p95'が値を返すかを確認して、次のステップに進んでください。
  7. /root/gfd-perf/budget.txtに、バジェットを5行で書いてください。max_panels=6、max_targets=6、max_var_queries=0、min_refresh_sec=60、max_queries_per_min=6です。そして、/root/gfd-perf/budget.pyを作成してください。バジェットファイルのパスとダッシュボードJSONのパスを引数に受け取って、違反を1行に1つずつ(B1からB5までで始まる形で)出力し、違反が1つでもあれば、終了コード1で終わる必要があります。B1はパネル数の超過、B2はクエリ数の超過、B3は変数クエリ数の超過、B4はリフレッシュがオンなのに間隔が短すぎること、B5は毎分のクエリ数の超過です。原本の/opt/lab/gfd/gfd-perf/heavy.jsonに対して実行して、5つの違反がすべて検出されるかを確認してください。
  8. 原本のgfd-perfはそのままにして、バジェットに収まる新しいダッシュボードを、uidgfd-perf-slimで保存してください。減らし方は自由ですが、次の3つは必ず入れてください。どのパネルも使っていない変数クエリをなくす、自動リフレッシュ間隔を60秒以上に変える、そしてp95パネルのクエリを、ステップ6で作ったhandler:http_request_duration_seconds:p95に変える、の3つです。保存したあと、そのダッシュボードをそのままダウンロードして、/root/gfd-perf/fixed.jsonに保存し(.dashboardの本体だけ)、ステップ7の検査ツールを実行して、違反が0で、終了コードが0であることを確認してください。そして/root/gfd-perf/08-review.mdに、B1=からB5=までの5行で、各項目をどのようにバジェットに収めたかを、それぞれ30文字以上で書いてください。

参考

値段を付けるダッシュボードをアップロードする

lab-start-grafanaでGrafanaを起動し、/opt/lab/gfd/gfd-perf/heavy.jsonを、修正せず、そのままGrafanaにアップロードしてください(uidはファイルに書かれたgfd-perf、パネルは8つ)。curlで/api/dashboards/dbにPOSTすればよいです。このダッシュボードは、このラボが終わるまで原本のまま残しておきます。縮小した版は、最後のステップで別のuidとして別に保存します。

保存APIは、ダッシュボードの本体をdashboardキーの中に入れ、overwriteを一緒に送ります。ファイルからその形を作るには、jq -n --slurpfileが便利です。Grafanaが起動するのに数十秒かかるので、/api/healthが応答するかどうかを、先に見てください。

1回開くのに、クエリが何回飛ぶのか

Grafanaにアップロードされたダッシュボードをダウンロードして、数えてください。/root/gfd-perf/02-count.txtに、panels=、targets=、var_queries=、open=の4行を書きます。panelsは行(row)を除いたパネルの数、targetsはすべてのパネルのクエリの数、var_queriesはtemplating.listのうちtypeがqueryの変数の数、openはtargetsとvar_queriesを足した値で、ダッシュボードを1回開くときのクエリ数です。

行の中に畳まれているパネルも、パネルです。jqで展開するには、.panels[] | (., (.panels[]?))のあとに、typeがrowのものを除外すればよいです。クエリはパネルごとにtargets配列に入っていて、パネル1つに2つ以上あることもあります。変数は、一覧をサーバーに問い合わせるものだけを数えます。手で書いておいた定数の一覧は、クエリを作りません。

どのパネルがいちばん重いのか

パネルごとに付いているクエリを実際に投げて、返ってくる系列の数を測ってください。過ぎた時刻を1つ選び(今より5分以上前、6時間以内)、すべてのクエリをその時刻の瞬間値として投げます。/root/gfd-perf/03-series.txtに、at=、total=、top_id=、top_series=の4行を書きます。atは選んだ時刻のエポック秒、totalはダッシュボードのすべてのクエリが返す系列の数の合計、top_idは系列を最も多く返すパネルのid、top_seriesはそのパネルの系列の数です。いちばん重いパネルのグラフに、線が何本描かれるかを考えてみてください。

系列の数は、/api/v1/queryの応答のdata.resultの長さです。時刻を固定するには、time=を一緒に送ってください。そうすれば、あとで数え直しても同じ答えが出ます。クエリをダッシュボードJSONから取り出して、ループで回せば、手で書き写さなくて済みます。パネル1つにクエリが2つあるなら、その2つを足したものが、そのパネルの系列の数です。

解像度を変えると、返ってくる点が何倍になるのか

過ぎた区間を1つ選び(2時間以上、終わりは今より前)、パネル3のp95のクエリを、同じ区間でstepだけを変えて2回投げてください。1回はstep=15、もう1回はstep=300です。/root/gfd-perf/04-points.txtに、start=、end=、points_fine=、points_coarse=の4行を書きます。points_fineはstep 15のとき、points_coarseはstep 300のときに、系列1つに返ってきた点の数です。

区間クエリは/api/v1/query_rangeで、start・end・stepを一緒に送ります。返ってきたJSONのdata.result[0].valuesの長さが、系列1つの点の数です。2つの数字の比を出してみると、解像度がコストにそのまま掛け算されることがわかります。区間を過ぎた絶対時刻で固定しておけば、あとでもう一度測っても同じ値が出ます。

自動リフレッシュを掛けて、毎分のクエリ数を出す

ダッシュボードJSONの自動リフレッシュ間隔を読んで、/root/gfd-perf/05-rate.txtに、refresh=、refresh_sec=、targets=、per_min=の4行を書いてください。refreshはJSONに書かれた文字列そのまま、refresh_secはそれを秒に換算した整数、targetsはステップ2で数えたクエリの数、per_minはtargetsに60 / refresh_secを掛けた値です。このラボの計算では、自動リフレッシュはパネルのクエリだけを再び投げ、変数クエリは再び投げないものとして扱います。

自動リフレッシュ間隔は、ダッシュボードJSONの最上部のrefreshキーに、10s・1mのような文字列として入っています。末尾の文字が単位で、その前が数字です。この数字がなぜ重要なのかピンと来なければ、24を掛けて1日分を出してみてください。誰も見ていない夜にも、同じ数が飛んでいます。

重い計算をレコーディングルールに移す

パネル3のp95のクエリは、バケット系列48個を読んで、毎回分位数を計算します。/etc/prometheus/rules/gfd-perf.ymlに、グループ名gfd-perf、interval15秒、レコーディングルール1つを置いてください。recordはhandler:http_request_duration_seconds:p95、exprはパネル3のp95のクエリそのままです。promtool check rules /etc/prometheus/rules/gfd-perf.ymlで確認したあと、curl -X POST http://127.0.0.1:9090/-/reloadで反映し、20秒以上待ってから、promq 'handler:http_request_duration_seconds:p95'が値を返すかを確認して、次のステップに進んでください。

レコーディングルールの名前には慣例があり、수준:지표:연산の形で、コロンを2つ使います(プレースホルダーは順に、集計レベル、指標、演算です)。グループのintervalが計算間隔で、反映の直後には、まだ1度も計算していないので、その間隔が1回過ぎて、初めて値ができます。curl -s http://127.0.0.1:9090/api/v1/rules | jqで、ルールが読み込まれたかどうかを見られます。

バジェットを書き、そのバジェットを検査する道具を作る

/root/gfd-perf/budget.txtに、バジェットを5行で書いてください。max_panels=6、max_targets=6、max_var_queries=0、min_refresh_sec=60、max_queries_per_min=6です。そして、/root/gfd-perf/budget.pyを作成してください。バジェットファイルのパスとダッシュボードJSONのパスを引数に受け取って、違反を1行に1つずつ(B1からB5までで始まる形で)出力し、違反が1つでもあれば、終了コード1で終わる必要があります。B1はパネル数の超過、B2はクエリ数の超過、B3は変数クエリ数の超過、B4はリフレッシュがオンなのに間隔が短すぎること、B5は毎分のクエリ数の超過です。原本の/opt/lab/gfd/gfd-perf/heavy.jsonに対して実行して、5つの違反がすべて検出されるかを確認してください。

バジェットを文章で書いておいても、誰も守りません。動くコードで書いてください。行(row)の中に畳まれたパネルもパネルであること、ファイルが{"dashboard": ...}で包まれている場合もあることを、忘れないでください。refreshがないか、オフになっていれば、自動リフレッシュがないということなので、B4とB5は検出されてはいけません。

バジェットの中に収めて、新しい版として提出する

原本のgfd-perfはそのままにして、バジェットに収まる新しいダッシュボードを、uidgfd-perf-slimで保存してください。減らし方は自由ですが、次の3つは必ず入れてください。どのパネルも使っていない変数クエリをなくす、自動リフレッシュ間隔を60秒以上に変える、そしてp95パネルのクエリを、ステップ6で作ったhandler:http_request_duration_seconds:p95に変える、の3つです。保存したあと、そのダッシュボードをそのままダウンロードして、/root/gfd-perf/fixed.jsonに保存し(.dashboardの本体だけ)、ステップ7の検査ツールを実行して、違反が0で、終了コードが0であることを確認してください。そして/root/gfd-perf/08-review.mdに、B1=からB5=までの5行で、各項目をどのようにバジェットに収めたかを、それぞれ30文字以上で書いてください。

新しいuidで保存するときは、本文からidを取り除く必要があります。残しておくと、Grafanaがそのidの既存のダッシュボードを書き換えてしまいます。クエリ数を減らす方法は、パネルを削除することだけではありません。2つのクエリを1つの式にまとめられるパネルがないかを見てください。件数2つを別々に受け取って、目で割り算するパネルがそうです。系列を48個返すパネルは、その画面から読み取れるものがないのではないかを、先に問いかけてみてください。