NaviPlus Engineers' Blog

Kibanaのダッシュボード設定を動的生成してみる

インフラ担当の池田(@mikeda)です。

最近、Kibanaでアクセスログの可視化をやってみてるのですが、手でダッシュボードいじるのがダルいです。
例えばうちの場合はサーバクラスタ、顧客アカウントごとのアクセスを見たい場合が多く、その条件を毎回指定するのがめんどくさい。
ここを動的に生成できないかな、と思って調べてみました。

イメージとしては、
内部管理画面にクラスタ/顧客アカウントの一覧があるのでそこにKibanaのリンクを設置して、

クリックすると適切に設定されたKibanaに飛ぶ、

という感じです。

Kibanaのソースコードやドキュメントはぜんぜん読んでないのですが(すませんw)、UIをポチポチ触ってるととりあえず以下の2つの方法でなんとかできそうでした。

Kibanaサーバに設定ファイル配置

KibanaのconfigはJSONとしてダウンロードしたり、読み込んだりすることができます。

このJSONをKibanaサーバ上の
app/dashboards/XXX.json
に配置すれば、
こういうURLでアクセスして読み込むことができそうです。
http://kibana.example.jp/index.html#/dashboard/file/XXX.json

バッチ処理でJSONファイルをまとめて作成すれば、今回の要件は比較的簡単に満たせそうです。

ElasticSearchに設定突っ込んでリダイレクト

KibanaのUI上から『Save』を使って設定を保存すると、固定URLを指定してアクセスできるようになります。
http://kibana.example.jp/index.html#/dashboard/elasticsearch/XXX
※『Load』で指定して読み込むこともできます。

このデータはどこにあるかというと、ElasticSearchのkibana-intというIndexに保存されています。
※『Share』を使って設定を一時URLで共有する場合も基本的に同じ仕組です

JSON形式のコンフィグが入っているdashboardの他、いくつかのフィールドがあります。
というわけで、同じような設定を動的生成してElasticSearchに突っ込んでから、それを指定したKibanaのURLにリダイレクトしてやれば今回の要件は満たせそうです。

例えばRailsだとこんな感じでできます。

class KibanaTestController < ApplicationController
  ## Kibanaにリダイレクトテスト
  def redirect_to_kibana
    kibana_config = Kibana::KibanaConfig.new(
      'cluster_mikeda.jp',
      [
        { "field" => "cluster", "query" => '"mikeda.jp"' }
      ],
      [ 
        { "alias" => "aaa", "color" => "#7EB26D", "query" => 'access_type:"aaa"' },
        { "alias" => "bbb", "color" => "#EAB839", "query" => 'access_type:"bbb"' },
        { "alias" => "ccc", "color" => "#6ED0E0", "query" => 'access_type:"ccc"' },
        { "alias" => "ddd", "color" => "#E24D42", "query" => 'access_type:"ddd"' },
      ]
    )
    kibana_config.save
    redirect_to kibana_config.url
  end
end

module Kibana
class KibanaConfig
  KIBANA_URL = 'http://192.168.1.100/kibana/index.html'

  QUERY_DEFAULT = { 
    "alias" => "", 
    "pin" => false,
    "type" => "lucene"
  }
  FILTER_DEFAULT = { 
    "type" => "field",
    "mandate" => "must",
    "active" => true,
    "alias" => "", 
  }
  TIMESAMP_FILTER = { 
    "type" => "time",
    "field" => "@timestamp",
    "from" => "now-24h",
    "to" => "now",
  }

  def initialize(title, filters, queries)
    @config = default_config
    @title = title

    set_title(@title)
    set_filters([ TIMESAMP_FILTER ] + filters)
    set_queries(queries)
  end 

  def set_title(title)
    @config['title'] = title
  end

  def set_filters(filters)
    filters.each.with_index do |filter, i|
      @config['services']['filter']['list'][i.to_s] = FILTER_DEFAULT.merge(filter)
      @config['services']['filter']['ids'] << i
    end
  end

  def set_queries(queries)
    queries.each.with_index do |query, i|
      @config['services']['query']['list'][i.to_s] = QUERY_DEFAULT.merge(query)
      @config['services']['query']['ids'] << i
    end
  end

  def save
    es = Elasticsearch::Client.new hosts: Settings.elasticsearch_logstash.hosts
    es.index index: 'kibana-int',
             type:  'dashboard',
             id: @title,
             body: {
               title: @title,
               user: 'guest',
               group: 'guest',
               dashboard: @config.to_json
             }
  end

  def url
    "#{KIBANA_URL}#/dashboard/elasticsearch/#{URI.encode(@title)}"
  end

  def default_config
    JSON.parse(File.read('lib/kibana/cluster_default.json'))
  end
end
end

コンフィグはいったん手動でいじって『Export schema』でダウンロード、そこから不要なものを削除したものをデフォルトとしてRails側で調整しています。

// lib/kibana/cluster_default.json
{
  "services": {
    "query": {
      "idQueue": [],
      "list": {},
      "ids": []
    },
    "filter": {
      "idQueue": [],
      "list": {},
      "ids": []
    }
  },
  "rows": [
    {
      "title": "Graph",
      "height": "350px",
      "editable": true,
      "collapse": false,
      "collapsable": true,
      "panels": [
        {
          "span": 12,
          "editable": true,
          "group": [
            "default"
          ],
          "type": "histogram",
          "mode": "count",
          "time_field": "@timestamp",
...
  "refresh": false
}

ちょっと大げさな感じですが、ファイル生成よりは完全なリアルタイムでコンフィグ生成がやりやすそう。

まとめ

Kibanaのconfigを動的に生成できないかな、と2つの方法を試してみました。

今のところ『ElasticSearchに突っ込んでリダイレクト』でいこうと思っています。
もっと簡単な方法あればぜひ教えて下さい!