1クール続けるブログ

とりあえず1クール続けるソフトウェアエンジニアの備忘録

Grafanaのダッシュボードをgrafonnetを使って管理する

記事一覧はこちら

要約

Grafanaはすごくパワフルな可視化ツールですよね。
例えば、kuberntesやECSのクラスタ1つごとにダッシュボードを作成することも多いかと思います。そうなると生のjsonでコード管理するのもかなり厳しくなってきます。
jsonnet + grafonnetを利用すれば、コード管理が楽になりそうという話です。

参考にしたのはこちら

grafana.com

jsonnetとは

jsonnetの存在自体はArgoCDがサポートしていたり、Spinnakerのパイプラインを管理するのに用いられているから知ってはいました。
ただ実際には使ったことはなく今回初めて勉強します。

jsonnetjsonのsupersetになっています。実装としてはC++Goの2種類があるようですが、コミュニティは最終的にはGoにすべて移行させたいようなので、Goの実装を利用するのが良いかと思います(参考)。C++での実装の方が少しパフォーマンスが良いらしいのですが、おそらく殆どのjsonnetの利用用途から見ると、その差はそこまで重要ではないはずです。

f:id:jrywm121:20210207104407p:plain

標準的なユースケースについては公式サイトのReferenceにて言及されています。設定ファイルを独立して作成するとたくさんの重複が発生し、保守が難しくなる。それをプログマティックに宣言できるようにしてくれるよとあります。

The standard use case is integrating multiple services which do not know about each other. Writing the configuration for each independently would result in massive duplication and most likely would be difficult to maintain. Jsonnet allows you to specify the configuration on your terms and programmatically set up all individual services.

構文はJsonnet公式のチュートリアルが分かりやすいです。
特徴としては下記かなあとは思います。レベル感の合っていない抜き出し方ですが、自分としてはこの辺がイメージ掴むのに良かったです。

  • Jsonnet is a purely functional language (with OO features).
  • Jsonnetのプログラムはすべて式で構成される → 文は存在しない
  • Jsonnetはオブジェクトの概念を持ち、+キーワードで継承することができる
  • Field Visiblityという概念があり、例えば::を利用したフィールド宣言の場合に最終的なJSONで出力されない

grafonnetとは

grafana.github.io

jsonnet用のライブラリが公式から提供されています。ダッシュボードの作成を用意された便利な関数で行うことができます。これによって大幅にコード行数を少なくすることが出来ます。
grafonnetのようなjsonnetのライブラリであるlibsonnetを導入する際には、単純にgit cloneする方法とjsonnet-bundlerを利用する方法があるそうです。せっかくなので後者の方法でgrafonnetを利用したいと思います。
jsonnet-bundlerのインストール方法は下記のようになります。

# Macの場合
brew install jsonnet-bundler

# amd64の場合
VERSION=v0.4.0
ARCH=amd64
wget -o jb https://github.com/jsonnet-bundler/jsonnet-bundler/releases/download/${VERSION}/jb-linux-${ARCH}
chmod +x jb
sudo mv ./jb /usr/local/bin/jb

ここからgrafonnetを利用できるようにするためには下記のようになる。jb initで依存するパッケージを定義するjsonnetfile.jsonが作成されます。

jb init
jb install https://github.com/grafana/grafonnet-lib   # jsonnetfile.jsonの設定が追記され、jsonnetfile.lock.jsonが作成される

jsonnnetのファイル側からは以下のように定義することで関数などを利用することが可能です。

local grafana = import 'vendor/grafonnet-lib/grafonnet/grafana.libsonnet';

# jsonnet実行時に -J vendor/grafonnet-lib を渡してあげれば下記のような宣言も可能
local grafana = import 'grafonnet/grafana.libsonnet';

jsonnet+grafonnet でダッシュボードを作成してみる

github.com

共通するファイルは下記のように書いてあげる。コメントはgrafonnetのコードを参考にしたが、もうちょっとJSDocよりに書いたほうが型とかが分かりやすかった気がする。

local grafana = import 'vendor/grafonnet-lib/grafonnet/grafana.libsonnet';
local graphPanel = grafana.graphPanel;
local prometheus = grafana.prometheus;

local dashboard = {
    /**
     * create dashboard
     *
     * @name dashboard.new
     *
     * @param title The title of the dashboard
     * @param tags (optional) Array of tags associated to the dashboard, e.g.`['tag1','tag2']`
     * @param description The description of the dashboard
    */
    new(title, tags, description):: grafana.dashboard.new(
        title,
        tags=tags,
        schemaVersion=18,
        editable=true,
        time_from='now-1h',
        refresh='1m',
        description=description,
    )
};

local podCountsPanel = {
    /**
     * create pod counts graph panel
     *
     * @name podCountsPanel.new
     *
     * @param title The title of the podCountsPanel
     * @param description The description of the panel
     * @param datasource Datasource
    */
    new(title, description, datasource):: graphPanel.new(
        title,
        decimals = 0,
        min = 0,
        datasource = datasource,
    )
    .addTarget(
        prometheus.target(
            'sum(kube_pod_status_ready{namespace="default"}))'
        )
    )
};

{
    dashboard: dashboard,
    podCountsPanel: podCountsPanel,
}

そして、呼び出す側からは下記のように宣言する

local tmpl = import "k8s-cluster-summary.libsonnet";
local dashboard = tmpl.dashboard;
local podCountsPanel = tmpl.podCountsPanel;

dashboard.new("booinfo-dev", ['kubernetes', 'dev'], 'summary of bookinfo k8s cluster')
  .addPanels(
    [
      podCountsPanel.new('pod count', 'クラスタpodの総数', 'prometheus') + {
        gridPos: { h: 8, w: 7, x: 0, y: 4 }
      },
    ]
  )

かなり良さげなのですが、既存のダッシュボードを移行しようと思うとかなり労力が必要そうですよね。
新しくダッシュボードを作成するときにはぜひ取り入れてみたい方法だとは思います。以上!