順位を問うパネルをテーブルに移す
目標
折れ線グラフ4本が重なったダッシュボードを受け取り、順位を問うパネルをテーブルに移して、reduce・organize・joinByFieldの変換で、読める形にします。
なぜ重要なのか
ダッシュボードが答えるべき質問は、大きく2つあります。「いつからこうなったのか」と「どれがいちばん悪いのか」です。前者は時間に沿った動きなので折れ線グラフが合っていますが、後者はある瞬間のソートされた一覧なので、折れ線グラフでは答えられません。系列4つの値が互いに近いと、線は絡み合い、順位を知るには、1つの時刻を選んで4つの値を読み、頭の中で並べ替える必要があります。8個になると、誰もその作業をしません。テーブルは、その並べ替えを画面が代わりに行う形で、変換は、時系列をテーブルが読める形に変える道具です。ただし、すべてのパネルをテーブルに変えると、かえって悪くなります。移すのは、順位を問うパネルだけです。
ステップ
lab-start-grafanaでGrafanaを起動し、/opt/lab/gfd/gfd-table/ranking.jsonを、修正せず、そのままGrafanaにアップロードしてください(uidはファイルに書かれたgfd-table、パネルは4つ)。curlで/api/dashboards/dbにPOSTすればよいです。アップロードしたあと、Webプレビューの3000番ポートで一度開いて、4つのパネルがどう見えるかを、目で確認しておいてください。- パネル1は、4つのハンドラーのp95を重ねて描きます。過ぎた時刻を1つ選び(今より1時間以上前、6時間以内)、その瞬間のp95をハンドラーごとに測って、
/root/gfd-table/02-rank.txtに、at=、rank1=、rank2=、rank3=、rank4=、gap=の6行で書いてください。atはエポック秒、rank1からrank4までは、p95が大きいものから順に書いたハンドラー名、gapは1位と4位のp95の差(秒)です。 - まず、テーブルが何行になるかを測ってください。過ぎた区間を1つ選び(1時間以上、終わりは今より前)、パネル1のクエリをその区間にstep 60で投げて、
/root/gfd-table/03-table.txtに、start=、end=、series=、points=、rows=の5行を書いてください。seriesは、同じクエリをendの時刻の瞬間値として投げたときに返ってきた系列の数、pointsは、区間クエリが系列1つに返した点の数、rowsは、その2つを掛けた値です。そして、パネル5を新しく作成してください。typeはtable、クエリはパネル1と同じものを、formatをtableにして、区間クエリで(instantは真にしないでください)投げます。 - パネル5に
reduce変換を付けてください。transformations配列に、idがreduceの項目を1つ置き、options.modeはseriesToRows、options.reducersはlastNotNullだけを入れます。クエリは、そのまま区間クエリにしておきます。行を減らす作業を変換が行うかどうかを確かめるためです。直したダッシュボードを保存してください。 - パネル5の
transformations配列の、reduceの後ろに、idがorganizeの項目をもう1つ付けてください。options.renameByNameで、FieldとLast *の2つの列の名前を、それぞれ別の韓国語の名前に変え、options.indexByNameで、その2つの列の順序を0と1に固定してください。直したダッシュボードを保存してください。 - パネル6を新しく作成してください。
typeはtable、クエリは2つです。refIdがAのものはsum by (handler) (rate(http_requests_total{job="shop-api"}[5m]))、refIdがBのものはパネル1と同じp95のクエリです。2つのクエリとも、instantを真に、formatをtableにしてください。そして、transformationsに、idがjoinByFieldの項目を1つ置き、options.byFieldをhandler、options.modeをouterにしてください。 - パネル6の既定のセル表示(
fieldConfig.defaults.custom.cellOptions.type)はautoにしておき、fieldConfig.overridesに、p95の値の列1つだけを指名するオーバーライドを1つ追加してください。オーバーライドのmatcher.idはbyName、propertiesには、custom.cellOptions(typeをcolor-backgroundに)とthresholds(段階を2つ以上、最後の段階は数値のしきい値)を入れます。そして/root/gfd-table/07-cells.txtに、rule1=、rule2=、rule3=の3行で、テーブルのセルに色を付けるときに守るべきルールを、それぞれ40文字以上で、互いに異なる内容で書いてください。 - パネル2(
핸들러별 5xx 비율、韓国語のタイトルは「ハンドラー別の5xx割合」という意味です)も、順位を問うパネルです。typeをtableに変えて、クエリはそのままにしたまま、instantを真に、formatをtableにし、organize変換でTime列を隠して(excludeByNameでTimeを真に)、残る2つの列の名前を変えてください。パネル4(전체 요청률)(韓国語のタイトルは「全体のリクエストレート」という意味です)には、手を付けないでください。順位を問うパネルではありません。そして、今アップロードされているダッシュボードをそのままダウンロードして、/root/gfd-table/fixed.jsonに保存し(.dashboardの本体だけ)、/root/gfd-table/08-review.mdに、R1=からR4=までの4行で、何をなぜ変えたかを、それぞれ30文字以上で書いてください。
参考
- Grafanaは
lab-start-grafanaで起動します(数十秒かかります)。Webプレビューの3000番ポートで、目でも確認してみてください。 - 元のダッシュボードは
/opt/lab/gfd/gfd-table/ranking.jsonにあります。このファイルは修正せず、読むだけにしてください。 - ダッシュボードを保存するAPIは
POST /api/dashboards/dbで、本文は{"dashboard": ..., "overwrite": true}です。 - ある瞬間の値は
/api/v1/queryにtime=を、区間は/api/v1/query_rangeにstart・end・stepを、一緒に送ります。どちらの経路も、/api/datasources/proxy/uid/<uid>/の後ろに付けて、Grafana経由で投げられます。 - 変換はブラウザーで計算されます。そのため、採点は、ダッシュボードJSONの
transformations・type・optionsとクエリのinstant・format、そして、そのクエリを実際に投げて得られた値で行います。テーブルが画面にどう描かれるかは、自分の目で見てください。 - よくある間違い①は、直したあとに保存しないことです。画面で変えても、APIで保存しなければ、採点ツールが見るのは古い版です。
- よくある間違い②は、時刻を
지금(韓国語で「いま」を意味する語です)基準で取ることです。過ぎた絶対時刻で固定しておけば、もう一度測っても同じ値が出ます。 - 変換の一覧 · テーブル可視化 · Prometheusクエリエディター · ダッシュボードJSONモデル · PrometheusクエリAPI
折れ線グラフ4本のダッシュボードを、そのままアップロードする
lab-start-grafanaでGrafanaを起動し、/opt/lab/gfd/gfd-table/ranking.jsonを、修正せず、そのままGrafanaにアップロードしてください(uidはファイルに書かれたgfd-table、パネルは4つ)。curlで/api/dashboards/dbにPOSTすればよいです。アップロードしたあと、Webプレビューの3000番ポートで一度開いて、4つのパネルがどう見えるかを、目で確認しておいてください。
保存APIは、ダッシュボードの本体をdashboardキーの中に入れ、overwriteを一緒に送ります。ファイルからその形を作るには、jq -n --slurpfileが便利です。Grafanaが起動するのに数十秒かかるので、/api/healthが応答するかどうかを、先に見てください。
折れ線グラフから順位を読んでみる
パネル1は、4つのハンドラーのp95を重ねて描きます。過ぎた時刻を1つ選び(今より1時間以上前、6時間以内)、その瞬間のp95をハンドラーごとに測って、/root/gfd-table/02-rank.txtに、at=、rank1=、rank2=、rank3=、rank4=、gap=の6行で書いてください。atはエポック秒、rank1からrank4までは、p95が大きいものから順に書いたハンドラー名、gapは1位と4位のp95の差(秒)です。
ある瞬間の値は、/api/v1/queryにtime=を一緒に送れば得られます。時刻を固定しておくと、あとでもう一度測っても同じ値が出ます。応答のmetric.handlerとvalue[1]を一緒に取り出して、sort -rnで並べれば、順位がすぐに出ます。4つの値がどれだけ近いかを見て、それを折れ線グラフで目で見分けられるかどうかを、考えてみてください。
同じ質問をテーブルに移す
まず、テーブルが何行になるかを測ってください。過ぎた区間を1つ選び(1時間以上、終わりは今より前)、パネル1のクエリをその区間にstep 60で投げて、/root/gfd-table/03-table.txtに、start=、end=、series=、points=、rows=の5行を書いてください。seriesは、同じクエリをendの時刻の瞬間値として投げたときに返ってきた系列の数、pointsは、区間クエリが系列1つに返した点の数、rowsは、その2つを掛けた値です。そして、パネル5を新しく作成してください。typeはtable、クエリはパネル1と同じものを、formatをtableにして、区間クエリで(instantは真にしないでください)投げます。
区間クエリは/api/v1/query_rangeで、start・end・stepを一緒に送ります。返ってきたJSONのdata.result[0].valuesの長さが、系列1つの点の数です。パネルを新しく作るときは、ダッシュボードをダウンロードして、panels配列に1つ加え、そのままもう一度保存すれば済みます。gridPosが重ならないように、yを下にずらしておいてください。
変換で、系列ごとに数字を1つだけ残す
パネル5にreduce変換を付けてください。transformations配列に、idがreduceの項目を1つ置き、options.modeはseriesToRows、options.reducersはlastNotNullだけを入れます。クエリは、そのまま区間クエリにしておきます。行を減らす作業を変換が行うかどうかを確かめるためです。直したダッシュボードを保存してください。
reduceは、系列1つを数字1つに減らします。seriesToRowsは、系列ごとに1行を作り、その行には、系列名が入ったField列と、選んだ計算値の列ができます。lastNotNullの列名はLast *です。次のステップで、この名前を使います。変換は、ダッシュボードJSONのパネルごとにあるtransformations配列に、順に入ります。
列の名前と順序を、人が読めるようにする
パネル5のtransformations配列の、reduceの後ろに、idがorganizeの項目をもう1つ付けてください。options.renameByNameで、FieldとLast *の2つの列の名前を、それぞれ別の韓国語の名前に変え、options.indexByNameで、その2つの列の順序を0と1に固定してください。直したダッシュボードを保存してください。
organizeは、列を隠し(excludeByName)、順序を決め(indexByName)、名前を変える(renameByName)、1つの変換です。3つのオプションとも、列名をキーにしたオブジェクトです。変換は、配列に書かれた順に、順番に適用されるので、前の変換が作り出した列名を、後ろの変換が受け取ることになります。Fieldは系列名の列、Last *は最後の値の列です。
2つのクエリをラベルで結合して、1つのテーブルに載せる
パネル6を新しく作成してください。typeはtable、クエリは2つです。refIdがAのものはsum by (handler) (rate(http_requests_total{job="shop-api"}[5m]))、refIdがBのものはパネル1と同じp95のクエリです。2つのクエリとも、instantを真に、formatをtableにしてください。そして、transformationsに、idがjoinByFieldの項目を1つ置き、options.byFieldをhandler、options.modeをouterにしてください。
瞬間値クエリは、系列ごとに1行を返すので、変換なしでもテーブルが短くなります。そのため、ここではreduceが必要ありません。2つの結果に共通してある列は、ラベル名そのままのhandlerです。結合したあと、値の列はValue #A・Value #Bで区別されます。outerは、片方にしかない行も残し、innerは、両方にある行だけを残します。
色は、しきい値のある列1つにだけ付ける
パネル6の既定のセル表示(fieldConfig.defaults.custom.cellOptions.type)はautoにしておき、fieldConfig.overridesに、p95の値の列1つだけを指名するオーバーライドを1つ追加してください。オーバーライドのmatcher.idはbyName、propertiesには、custom.cellOptions(typeをcolor-backgroundに)とthresholds(段階を2つ以上、最後の段階は数値のしきい値)を入れます。そして/root/gfd-table/07-cells.txtに、rule1=、rule2=、rule3=の3行で、テーブルのセルに色を付けるときに守るべきルールを、それぞれ40文字以上で、互いに異なる内容で書いてください。
パネルの既定値に色を設定すると、名前の列まで塗られます。色が意味を持つには、その色がしきい値を超えたということを伝える必要があり、そのためには、しきい値のある列にだけ付ける必要があります。オーバーライドは、fieldConfig.overrides配列の項目で、どの列に設定するかはmatcherが、何を変えるかはpropertiesが決めます。結合したあとのp95の値の列の名前を、先に確認してください。
まだ線のまま残っている順位のパネルを、テーブルに変える
パネル2(핸들러별 5xx 비율、韓国語のタイトルは「ハンドラー別の5xx割合」という意味です)も、順位を問うパネルです。typeをtableに変えて、クエリはそのままにしたまま、instantを真に、formatをtableにし、organize変換でTime列を隠して(excludeByNameでTimeを真に)、残る2つの列の名前を変えてください。パネル4(전체 요청률)(韓国語のタイトルは「全体のリクエストレート」という意味です)には、手を付けないでください。順位を問うパネルではありません。そして、今アップロードされているダッシュボードをそのままダウンロードして、/root/gfd-table/fixed.jsonに保存し(.dashboardの本体だけ)、/root/gfd-table/08-review.mdに、R1=からR4=までの4行で、何をなぜ変えたかを、それぞれ30文字以上で書いてください。
瞬間値クエリをformat: tableで受け取ると、列はTime・handler・Valueの3つです。ある瞬間の一覧に、時刻の列は何の情報も与えないので、隠します。ファイルと画面が食い違わないようにするには、直したあとにもう一度ダウンロードする必要があります。記録の4行は、それぞれ、なぜテーブルに変えたのか、なぜ瞬間値クエリなのか、どの変換をなぜ付けたのか、どのパネルをなぜ折れ線グラフのまま残したのかを書く場所です。