kakakakakku blog

Weekly Tech Blog: Keep on Learning!

mkr dashboards コマンドを活用して最高のダッシュボードを生成した

はじめに

監視サービスでアラートが鳴った場合,それは閾値を超えた異常値が検知されているので,すぐに対応が必要だけど,もしかしたら通常時にも何か起きてるかもしれないし,通常時の傾向が特定のリリース以降で変わってしまっているかもしれないし,監視設定が漏れているかもしれない.逆にリソースが余りすぎていて過剰な投資をしてしまっているかもしれない.だからこそ,日々モニタリングすることは重要で,そんなモニタリングに絶対に欠かせない機能は「ダッシュボード」だと思ってる.

今回は mkr dashboards コマンドを使って Mackerel のカスタムダッシュボードを生成した話をまとめてみようと思う.

カスタムダッシュボードとは?

Mackerel には「カスタムダッシュボード」という機能がある.ただそんなに複雑なものではなくて,一言で言ってしまうと「Markdown 記法が使える汎用的なテキストエリア」という感じ.だから,Zabbix Screen や AWS CloudWatch Dashboard とは違って,ダッシュボードを作る場合,Mackerel のメトリックグラフから iframe をコピーして,ダッシュボードに貼り付ける作業を繰り返す必要がある.結構大変!

mackerel.io

mkr dashboards コマンドとは?

そこで mkr dashboards を使って自動化することを考えた.

ダッシュボードの期待値を YAML に定義して,mkr dashboards コマンドを実行することで自動でダッシュボードを生成することができる.さらに YAML を GitHub にコミットすることで,バージョン管理をすることができる.便利!

もしかしたら mkr monitors コマンドと似てる?と思った人もいるかもしれないけど,残念ながら push / pull / diff といったサブコマンドは用意されていなくて,実際には mkr dashboards generate しかない.ダッシュボードの反映を抑制する --print オプションはあるけど,デフォルトの動作だと,強制的にダッシュボードを反映するので「generate だから試してみよう!」みたいに気軽に実行するとアレなので注意が必要だよ!

# ダッシュボードを反映する
$ mkr dashboards generate xxx.yml

# Markdown を標準出力に表示する
$ mkr dashboards generate xxx.yml --print

mkr dashboards コマンドで生成できるダッシュボードの種類

ドキュメントにも書いてある通り,mkr dashboards コマンドを使うと,2種類のダッシュボードを作ることができる.

名前が直感的ではなくて微妙な気がするけど,簡単に覚えるなら「ホストごとのダッシュボード」と「ロールごとのダッシュボード」と覚えておけば良さそう.

  • ホストグラフ(YAML では host_graphs で定義する)
    • ホストごとに指定したグラフ(例えば loadavg5 など)を表示する
  • 個別グラフ指定(YAML では graphs で定義する)
    • ロールごとに指定したグラフ(例えば loadavg5 など)を表示する
    • その他として,サービスメトリックや式グラフを表示することもできる

詳細な YAML の項目説明は公式ドキュメントに書いてある.

mackerel.io

ロールダッシュボードを作った

今回 mkr dashboards コマンドを使って,ロールごとに重要な監視項目をモニタリングするためのダッシュボード(今回は "ロールダッシュボード" と呼ぶ)を作った.

今回は「ロールダッシュボード (web)」を例にして,YAML を作るのに工夫したところを書く.

  • ロールダッシュボード (web)
  • ロールダッシュボード (db)
  • ロールダッシュボード (aggregator)
  • などなど...

工夫点 1 : 期間ごとに short と long に分割した

Mackerel のダッシュボードでは,Zabbix Screen や AWS CloudWatch で提供されているような期間選択の機能がないという欠点がある.よって,期間ごとにグラフを配置していく必要がある.

最初に「6種類の期間」と「6種類の監視項目」を YAML に書いてみた.単純計算で36個のグラフをダッシュボードに配置することができた.ただ予想通り,グラフの描画があまりにも遅く,全てのグラフが描画されるまでに30秒程度掛かっていた.これじゃダメだ!運用に耐えない!

  • 期間
    • 1h
    • 1d
    • 1w
    • 1mo
    • 3mo
    • 6mo
  • 監視項目
    • loadavg5
    • cpu.{user,iowait,system}
    • memory.used
    • custom.nginx.requests.requests
    • custom.php-fpm.processes.total_processes
    • custom.fluentd.buffer_total_queued_size.out_nginx_access

どう工夫したかと言うと,期間を分割してダッシュボードを2個にした.これでグラフ数を半分にすることができて,描画のパフォーマンスも改善できた.

  • short(一時的にスパイクしたときなど,短期的な傾向を見るときに使う)
    • 1h
    • 1d
    • 1w
  • long(中長期的に傾向を見るときに使う)
    • 1mo
    • 3mo
    • 6mo

工夫点 2 : headline に期間を記載した

グラフが期間ごとに3個並んでいると,一瞬「このグラフの期間ってなんだっけ?」とわからなくなることがある.特にアプリケーション開発者など,たまにダッシュボードを見るメンバーのことを考えると,明記しておきたいと思った.ただ mkr dashboards コマンドではテーブルに任意の文字を書き出すことができないため,headline に書いた.

graphs:
  (中略)
  - headline: loadavg5 ( 1h / 1d / 1w )
  (中略)

これで少しは改善できた.

f:id:kakku22:20161025222608p:plain

工夫点 3 : システムメトリック名に記号が使われていてもそのまま YAML に書く

例えば,システムメトリックにある cpu.{user,iowait,system} のグラフを描画したいと思ったときに,管理画面から取得できる iframe のタグを見ると以下のようになっていて,手動でカスタムダッシュボードを作る場合は当然 URL エンコードしたまま貼り付ける必要がある.ただ YAML の graph_name に書くときに cpu.%7Buser%2Ciowait%2Csystem%7D と書くのは嫌だなーと思っていた.

<iframe src="https://mackerel.io/embed/orgs/my-org/services/my-service/web?graph=cpu.%7Buser%2Ciowait%2Csystem%7D&amp;stacked=true&amp;simplified=false&amp;period=1h" height="200" width="400" frameborder="0"></iframe>

実際に試したら問題なくて,そのまま cpu.{user,iowait,system} と書くことができた.良かった.ちなみにグラフを「積み上げ」で表示する場合は stacked: true と書く必要がある(デフォルトは false).

graphs:
  - headline: cpu.{user,iowait,system} ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: cpu.{user,iowait,system}
        stacked: true
        period: 1h
  (中略)

最終的にできた YAML の例 : web_short.yml

こんな感じになった.うーん記述量が多いなぁ...!と思う.

config_version: 0.9
url_path: web_short
title: web_short
format: iframe
graphs:
  - headline: loadavg5 ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1w
  - headline: cpu.{user,iowait,system} ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: cpu.{user,iowait,system}
        stacked: true
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: cpu.{user,iowait,system}
        stacked: true
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: cpu.{user,iowait,system}
        stacked: true
        period: 1w
  - headline: memory.used ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: memory.used
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: memory.used
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: memory.used
        period: 1w
  - headline: custom.nginx.requests.requests ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: custom.nginx.requests.requests
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: custom.nginx.requests.requests
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: custom.nginx.requests.requests
        period: 1w
  - headline: custom.php-fpm.processes.total_processes ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: custom.php-fpm.processes.total_processes
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: custom.php-fpm.processes.total_processes
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: custom.php-fpm.processes.total_processes
        period: 1w
  - headline: custom.fluentd.buffer_total_queued_size.out_nginx_access ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: custom.fluentd.buffer_total_queued_size.out_nginx_access
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: custom.fluentd.buffer_total_queued_size.out_nginx_access
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: custom.fluentd.buffer_total_queued_size.out_nginx_access
        period: 1w

最終的にできたダッシュボードの例

こんな感じになった!

f:id:kakku22:20161025223155p:plain

mkr dashboards コマンドの進化に期待すること

次に mkr dashboards コマンドで改善してもらえると最高に便利なのに!という個人的な期待を書いてみたいと思う.

期待 1 : periods をサポートして欲しい

現在の仕様だと,期間 period は単一項目でしか定義できないため,上に載せた例のように YAML が無駄に大きくなってしまう問題がある.そこで host_idsgraph_names と同様に periods として配列形式で定義したい.今回の例で言うと,以下のように period のためだけに graph_def を3回繰り返して定義する必要があった.

graphs:
  - headline: loadavg5 ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1h
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1d
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        period: 1w

もし periods が使えれば,以下のようにスッキリと定義することができて最高なのでは?と思う.もっと言うと column_count の値も自動判別してくれるとさらに嬉しい.

graphs:
  - headline: loadavg5 ( 1h / 1d / 1w )
    column_count: 3
    graph_def:
      - service_name: my-service
        role_name: web
        graph_name: loadavg5
        periods:
          - 1h
          - 1d
          - 1w

期待 2 : mkr dashboards コマンドで任意のテキストをサポートして欲しい

ダッシュボードに任意のテキストを書くことができると表現の幅がグッと広がるし,インフラエンジニアの暗黙知のようなものを補足することもできるし便利だと思ってる.手動でダッシュボードを作るなら,書けば良いだけなんだけど,mkr dashboards コマンドだとできない.ただ mkr dashboards コマンドでダッシュボードを生成した後に手動で書き換える的なツライ運用もしたくないから,サポートして欲しい.

ちなみにダッシュボードに任意のテキストを書ける機能は珍しくなく,Zabbix Screen なら「プレーンテキスト」として書けるし,AWS CloudWatch なら「テキストウィンドウ」に書ける.Kibana にも「Markdown Widget」がある.個人的にもよく使っている.

期待 3 : filesystem をロールで見れるようにして欲しい

システムメトリックの filesystem グラフはホスト画面でしか見れず,ロールだと見れない.ロールごとの filesystem グラフをダッシュボードに表示したいと思っていて,見れるようにして欲しい.

期待 4 : サブコマンドを mkr monitors コマンドと合わせて欲しい

個人的に mkr dashboards generate コマンドでダッシュボードが更新されてしまうのは少し違和感があるのと,ある意味ユースケースは似ている mkr monitors コマンドとサブコマンドが違うのも気になっている.mkr dashboards generate コマンドは残すとして,例えば以下のようなサブコマンドはどうだろう?と考えていた.まぁ現実的に考えると,pulldiff は難しそうな気がするので,せめて generatepush になってくれると良いのかもしれない.

  • mkr dashboards generate
    • 標準出力に表示する
  • mkr dashboards push
    • Mackerel 管理画面に反映する
  • mkr dashboards pull
    • Mackerel 管理画面からエクスポートする
  • mkr dashboards diff
    • Mackerel 管理画面と YAML を比較する

次世代ダッシュボードに期待すること

本音で言うと Mackerel のダッシュボードに期間選択の機能がないことに困っている.Zabbix Screen なら 1h / 2h / 3h / 6h / 12h / 1d / ... / 1y と選択できるし,AWS CloudWatch でも任意の期間(14日以内)を選択することができる.ちなみに Mackerel もサービス画面なら 30min / 1h / 6h / 1d / 1W / 1M / 1Y で選べるのに,なぜかダッシュボードでは選べず「Markdown 記法が使える汎用的なテキストエリア」になってしまっている.

急にスパイクしたときだったり,過去の傾向を調査するときだったり,特定の期間で複数のグラフを見るというシチュエーションは多いのではないかと思う.少なくとも僕はよくあって,Zabbix Screen をよく使っている(まぁ Zabbix だとロールでグラフを見れない欠点はあるんだけど).次世代ダッシュボードの可能性があるなら是非検討してもらいたい.

実は先週の Mackerel Meetup に参加したときに「どうダッシュボードを使ってますか?」と質問したら「期間ごとに並べてますねー」という回答だったけど「確かに期間指定で絞り込めたら便利ですねー」という声もあった.

kakakakakku.hatenablog.com

まとめ

今回は Mackerel のダッシュボードを便利に運用したいと思って mkr dashboards コマンドを頑張って使ってみた!現在は計6個のロールダッシュボードを運用していて,結構便利に使えているので,満足している.

最近 Mackerel User Group の Slack に JOIN したので「皆さんはダッシュボードをどうやって活用してますか?」みたいな雑談をしてみたいなー!と思ったりしている(発言するのちょっと恥ずかしい).もっと良い活用方法があるなら是非聞いてみたい.

mackerel-ug.hatenablog.com

関連記事

「ホストグラフ」と「個別グラフ指定」のサンプルが載っていて,YAML とダッシュボードの比較が凄くわかりやすかった.

htnosm.hatenablog.com

mkr monitors コマンドを使って監視ルールをバージョン管理 & CI できるようにした話を最近書いた.

kakakakakku.hatenablog.com

Mackerel を本格的に導入する前に Zabbix を使って每日楽しくモニタリングをしていた話を社内勉強会でしたときの資料も以前公開している.

kakakakakku.hatenablog.com