📚

OSSのBIツール「Metabase」の使い方

10 min read

更新履歴

2021年3月19日: 初回投稿
2021年3月20日: 【随時更新】Tips に下記内容を追加
- Jarファイルによる実行
- APIの活用
2021年5月3日: Jarファイルによる実行 を更新(nohup → サービス化に内容変更)

概要

Metabaseは自分でホストすることを厭わなければ無料で使うことができるオープンソースのBIツールです。

Metabaseイメージ

2021年3月19日現在、データソースはDBMSに限られるという欠点はありますが、接続したデータソース内のデータから上の画像のようなダッシュボードを簡単に作成することができます。

なお有名なOSSのBIツールにはRedashというものもあります。
こちらはデータベースだけでなく

  • Googleスプレッドシート
  • Salesforce

などもデータソースにすることができるのですが、Redashを使用するにはSQLを使用できることという前提条件があります。
そのため、データ可視化を行う人がデータサイエンティスト/アナリストやエンジニアならRedashを、そうでないならMetabaseを使用するのが良いのではないかと思います。

インストール

インストール方法はこちらに記載のとおり、

  • Jarファイル
  • Docker
  • AWS Elastic Beanstalk
  • Heroku
  • Debian as a Service
  • Kubernetes

などがありますが、Dockerの場合、以下のコマンドを打つだけですので「一番簡単なのはDockerでは」と思っております(Windows 10 ProにDocker Desktop for Windowsがインストールされている前提です)。

docker run -d -p 3000:3000 -v %USERPROFILE%\metabase.db:/metabase.db --name metabase metabase/metabase

補足:-vオプション

Metabaseでは /metabase.db というディレクトリにMetabase自体のデータベースが格納されております。
公式ドキュメントには-vオプションの記載はありませんが、同オプションでデータベースの永続化をしておかないとコンテナ終了時にデータベースも消えてしまいます。
そのため同オプションでファイルの永続化をしております。

セットアップ

セットアップ方法はこちらに記載のとおりですが、英語で「うっ」となる方もいらっしゃると思いますので簡単に説明します。

  1. http://localhost:3000にアクセスするとsetup画面が表示されます。

setup画面

  1. 画面の指示に従い、管理者アカウントを登録します。

管理者アカウント登録画面

  1. データソースに接続します。

データソース接続画面

  1. 匿名での使用状況データ収集に同意するかどうか決め、[次へ]ボタンをクリックします。

使用状況データ収集同意画面

データの可視化

データ可視化の方法も公式ドキュメントに記載されておりますが、データ可視化方法簡単に説明します。
なおMetabaseによるデータ可視化方法には

  1. 簡単な質問
  2. カスタム質問
  3. ネイティブクエリ(SQL)

の三種類がありますが、カスタム質問を主に使用し、複雑なものはネイティブクエリを使用するという使い方に落ち着くと思います。
ネイティブクエリはSQLを書くだけなので、ここではカスタム質問について説明します。

  1. ログインするとホーム画面が表示されます。

ホーム画面

  1. 画面上部の[質問する]ボタンをクリックすると質問方法選択画面になるのでカスタム質問を選択します。

質問方法選択画面

  1. 遷移後の画面でデータソースを選択するとノートブックエディタ画面になります。

ノートブックエディタ画面

  1. [フィルター]でデータソースのフィルタリング条件を指定、[要約]でグルーピング条件などを指定します。また必要に応じてテーブルを結合したり、カスタム列を追加したり、ソートしたりします。
  2. [ビジュアライズ]ボタンをクリックします。
  3. 可視化された結果を自由にカスタマイズし、最後に[保存]ボタンをクリックします。

可視化結果

ざっくりとしたデータ可視化手順は以上です。
可視化した結果(これを「カード」といいます)を配置することでダッシュボードを作成することができますが、詳しくは公式ドキュメントをご覧ください。

補足:ネイティブクエリ

当然ですが接続先のデータソースによって使用方法は異なります(データソースがMySQLならネイティブクエリで使用するSQLはMySQLとなる)。
一応お伝えしておきます。

【随時更新】Tips

ここから先は私が経験から学んだMetabase使用方法を思い思いに記載したものとなります。

あなたの質問には時間がかかりすぎました

私は普段、MetabaseのデータソースにMySQLデータベースを使用しているのですが、Metabase使用開始当初、副問い合わせを使用した際、「あなたの質問には時間がかかりすぎました」と表示されて質問結果が表示されない事象に悩まされました。「そんなに重い処理をDBMSにさせているのだろうか」とDBMSに直接接続してクエリを投げるも応答時間は0.03秒と非常に速くて問題なし。その時は「全社にBIツールのMetabaseを導入すると言ってしまったのに使い物にならないとかヤバイ・・・!」と気が気じゃありませんでした。

色々調べた結果、MB_ASYNC_QUERY_THREAD_POOL_SIZEという環境変数をデフォルト値の50から増やすことで解決できました。どの程度増やせば良いかは使用しているマシンスペックにもよると思いますが、私の場合、AWS EC2インスタンス(t3a.large)をMetabaseのインストール先にしていたので、Cloud WatchでCPU使用率やCPUクレジット使用数などを見ながら決めました。現在は1,000にしています。

Jarファイルによる実行(2021年5月3日更新)

本記事に「Dockerによる実行が一番楽だと思う」と投稿しておりますが、私の会社では諸々の事情により仕方なくJarファイルによる実行を選択しております。
なのでJarファイルによる実行の際、行ったことも記載しておこうと思います。

前提条件

ハード: Amazon EC2
OS: AmazonLinux2
インストール済み: wget, vim

1. Javaのインストール

最初にAdoptOpenJDKからOpenJDK 11(LTS)のJREをwgetコマンドでダウンロードしてインストールしました。
インストール方法はこちらに記載されております。

2. ログファイルの作成

Metabaseのログを出力するためのログファイルを作成しました。

touch /var/log/metabase.log

3. ユニット定義ファイルの作成

Metabase運用開始当初はnohup java -jar metabase.jarでMetabaseを実行しておりましたが、毎晩再起動を行うためにサービス化(systemdにMetabaseを登録)することにしました。
手順は以下のとおりです。

cd /etc/systemd/system
sudo vim /etc/systemd/system/metabase.service

ユニット定義ファイル(metabase.service)の中身は以下のとおりです。

metabase.service
[Unit]
Description=Metabase server
After=syslog.target
After=network.target

[Service]
User=root
# Metabaseのjarファイルをダウンロード・実行するディレクトリ
WorkingDirectory=/lib/ext
# 環境変数の設定(後述)
Environment="MB_ASYNC_QUERY_THREAD_POOL_SIZE=1000"
# Metabase実行コマンド
ExecStart=/usr/bin/java -jar /lib/ext/metabase.jar
# フォアグラウンドで実行継続するコマンドなのでsimpleを指定
Type=simple
# ログ・エラーの出力定義
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=metabase
# サービスのメインプロセス停止時の動作は再起動
Restart=always

[Install]
WantedBy=multi-user.target

環境変数について補足

上述の「あなたの質問には時間がかかりすぎました」対策のため、環境変数MB_ASYNC_QUERY_THREAD_POOL_SIZEを1000に設定しております。

なおMetabaseをサービスとして実行するのではなく、nohup java -jar metabase.jarで実行する場合、設定ファイル最下部に下記内容を追記すればOKです(一時的に環境変数を追加する場合はexport 変数名=値で追加しますが、exportコマンドで追加した環境変数はログアウトすると消えてしまいますので設定ファイル最下部に追記します)。

設定ファイル
MB_ASYNC_QUERY_THREAD_POOL_SIZE=1000
export MB_ASYNC_QUERY_THREAD_POOL_SIZE

また環境変数を追加する設定ファイルですが

  • 全ユーザに環境変数を設定したいのであれば、/etc/profile
  • 特定ユーザに環境変数を設定したいのであれば、~/.bash_profile

となります(こちらの記事が参考になりました)。

4. metabase.confの作成

systemdがログを適切に処理できるようmetabase.confを作成しました。

sudo touch /etc/rsyslog.d/metabase.conf
sudo vim /etc/rsyslog.d/metabase.conf

metabase.confの中身は以下のとおりです。

metabase.conf
if $programname == 'metabase' then /var/log/metabase.log
& stop

5. Metabase.jarのダウンロードおよびサービス実行

/lib/ext にJarファイルをダウンロードして実行しました。
ダウンロードURLはこちらのページのセンテンス "Your download should begin automatically. If it doesn't you can start it manually." のリンクからコピーしました。

なおSSH接続を切断してもMetabaseが実行され続けるようnohupコマンドを使用しました(本当はサービス化するべきですが面倒くさかったので・・・)。 サービス化しました。

cd /lib/ext
wget https://downloads.metabase.com/v0.39.1/metabase.jar
sudo systemctl daemon-reload
sudo systemctl start metabase.service
sudo systemctl enable metabase.service

6. cronの設定

前述のとおり、Metabase運用開始当初は nohup java -jar metabase.jar で実行しておりました。
しかし運用している中でJetty Active threadsが段々増え、環境変数MB_ASYNC_QUERY_THREAD_POOL_SIZEに達したら新たな質問が作成できなくなるという事象に悩まされ、色々と調べるも根本的な解決方法が不明なため、毎晩、cronでMetabaseを再起動することにしました。

crontab -e

crontabに設定した内容は以下のとおりです。

crontab
0 18 * * * sudo systemctl restart metabase.service

なおEC2のタイムゾーンがUTCのため毎日18時(=日本時間の午前3時)としておりますが、タイムゾーンをJSTにしている場合は日本時間でご入力ください。

補足:環境変数の反映

Metabaseを起動した状態で環境変数を追加した場合、再起動しないと反映されません。
サービス化している場合はsudo systemctl restart metabase.serviceで、nohup java -jar metabase.jarで実行している場合は以下の手順で再起動してください。

ps -ef | grep java  # MetabaseのPIDを調べる
kill PID  # Metabaseのプロセスを止める
nohup java -jar metabase.jar  # 再度実行する

補足:Metabaseのバージョンアップ

Metabaseのバージョンアップは最新のJarファイルをダウンロードして再起動するだけです。
Jarファイルで実行している場合、/metabase.db にMetabase自体のデータベースが残っているので特に気にする必要はありませんが、Dockerで起動している場合、-vオプションでファイルの永続化をしておく必要があるのでご注意ください。

APIの活用

MetabaseはAPIが公開されており、

  1. セッションIDの取得
  2. APIリクエスト

をするだけで簡単にカードを作成したり、質問結果を取得できます。
詳しくはAPIドキュメントをご覧頂ければと思いますが、使用方法を簡単に紹介致します。
なおここではPythonとJupyter Notebookを使用します。

1. セッションIDの取得

まずはAPIリクエストを行う際にheader情報に埋め込むセッションIDを取得します。
取得方法は以下のとおりです(metabase\endpointはご自身がMetabaseにログインする際のURLに置き換えてください)。

import requests

metabase_endpoint = "http://localhost:3000"
res = requests.post(url=f"{metabase_endpoint}/api/session",
                    headers={"Content-Type": "application/json"},
                    json={"username": "your_login_username",
                          "password": "your_login_password"}
                    )

assert res.ok, "error."
session_id = res.json()["id"]
session_id

なおセッションIDは14日間有効で、セキュリティ上の理由により一定期間に取得できる回数に制限があるようです。
APIリクエストの都度、セッションIDを取得するようなプログラムは書かない方が良いと思われます。

2. APIリクエスト

ここでは良く使いそうな内容をいくつかご紹介致します。

質問結果の取得(GET /api/card/:id

各カード(質問)には固有のIDがふられており、カードを開いた際のURL末尾を見ることで確認することができます(例: http://localhost:3000/question/1)。
このIDをリクエストURL末尾に含めてGETメソッドでリクエストを送ることでそのカードの質問結果を取得することができます。

import pandas as pd

# カード(質問)のID
quession_id = "1"

# セッションIDを忘れずに
headers = {"Content-Type": "application/json", "X-Metabase-Session": session_id}
res = requests.get(f"{metabase_endpoint}/api/card/{quession_id}/query/json", headers=headers)

# 質問結果の表示
pd.DataFrame(res.json()).head()

参考文献