CentOS7.4にMetabaseを構築してみた

BIツールで"Metabase"がいいと聞いて環境を構築してみました。
Metabaseはサービスとして動作させます。

[Running Metabase on Debian as a service with nginx](https://github.com/metabase/metabase/blob/master/docs/operations-guide/running-metabase-on-debian.md)を参考にCentOs版で実施しました。

# 環境
OS: CentOs7.4
DB: h2(※ Metabase用のDB)

# ステップ
1. jarファイルのダウンロード
2. serviceファイルの作成
3. Metabaseのログ出力設定
4. nginx/JDKのインストール
5. Metabase起動

## 1. jarファイルのダウンロード
オンプレ環境で使うということ、dockerを本番運用したことがないため
[Metabase for Other Platforms](https://www.metabase.com/start/other.html)からjarファイルをダウンロードします。

ダウンロードしたjarファイルはCentOSの任意ディレクトリに置きます。
　※私は/opt/metabaseというディレクトリを作って、そこに置きました。

## 2. serviceファイルの作成
CentOSではサービスとして運用するためにserviceファイルを作成し、
以下のようにしました。

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

[Service]
User=metabaseroot
Type=simple
WorkingDirectory=/opt/metabase
ExecStart=/usr/bin/java -Dlog4j.configuration=file:/opt/metabase/log4j.properties -jar /opt/metabase/metabase.jar
ExecStop=/bin/kill -SIGTERM $MAINPID

[Install]
WantedBy=multi-user.target
```

`ExecStart=...`のところで
metabase.jarの起動時にログ設定ファイルを指定しています。
ログ設定ファイルの中身は後述します。

## 3. Metabaseのログ出力設定
Metabaseはlog4jというJavaプログラム用のログAPIを使用してログ出力しています。
デフォルトの設定ではログがファイルに出力されなかったので、
以下のように変更しました。

```log4j.properties(変更前)
log4j.rootLogger=WARN, console, metabase
log4j.appender.file.File=${logfile.path}/metabase.log
```

```log4j.properties(変更後)
log4j.rootLogger=WARN, console, metabase, file
log4j.appender.file.File=/opt/metabase/metabase.log
```

## 4. nginx/JDKのインストール

```
# yum install -y java-1.8.0-openjdk-devel nginx
# systemctl enable nginx
# systemctl start nginx
# firewall-cmd --add-service=http --permanent
# firewall-cmd --reload
```

インストールついでに起動設定やファイヤーウォールの設定もしておきます。

MetabaseはjarファイルなのでJDKをインストールし、
Metabaseだけでもlocalhostでブラウザアクセスできるのですが
外部公開も踏まえてnginxをプロキシサーバとして設置します。

nginxの設定は以下を参考にすると良いと思います。
[Ensure nginx is setup to proxy requests to Metabase](https://github.com/metabase/metabase/blob/master/docs/operations-guide/running-metabase-on-debian.md#ensure-nginx-is-setup-to-proxy-requests-to-metabase)


## 5. Metabase起動
以下のコマンド実行で起動します。

```
# systemctl enable metabase
# systemctl start metabase
```

起動できたら、localhost:3000にアクセスしてMetabaseを使用できます。
※PCのスペックによりますが、Metabaseの起動に数分掛かります。

## 注意
実際はAnsibleを使って環境構築しました。
　※jarファイル/serviceファイルに関しては、Metabase構築サーバーに対するファイルコピーを実施
その場合はplaybookさえ書けば、一発でMetabase起動まで出来ます。

# 使ってみた感じ
## 良いと思った点
1. 通知にメールとslackが使える<br>
    うちの会社ではslackはエンジニアは使っていますが企画とか他部署では使っていない場合があるので<br>
    メールでも通知出来るのは良いですね。
2. SQLが書けなくても、GUIから可視化したいデータの抽出が可能<br>
    クエリビルダーみたいなやつで、どのデータソースのどのテーブルのどのデータを抽出するかを<br>
    リストから選択していくかで可視化することが出来ます。<br>
    これも非エンジニアに対して優しいかなと思います。
3. ユーザーごとに権限の細かな設定が可能<br>
    この人はこのquestion(クエリみたいなもの)は閲覧のみ可能など。
4. グラフの種類が豊富<br>
    パイチャート、時系列、表、マップなど。
5. ダッシュボードに置いたquestionのサイズ変更が自由<br>
    ダッシュボードに方眼紙みたいなマス目が書かれていて<br>
    そのマス目単位で自由に縦・横にサイズ変更が可能です。

## イマイチな点
1. 日本語対応していない<br>
    やっぱり英語だととっつきにくいと感じる人が多いです。<br>
    数年前からローカライズのIssueはあるのですが、進んでないですね...<br>
[Help us provide Metabase in your language!](https://www.metabase.com/blog/Metabase-in-your-language/)からローカライズのプロジェクトに参加したのですが、特になんのアナウンスもなく...<br>
    参加頻度、低くしすぎたからかな...？  
2. question作成時にデータソースの一覧が表示されない<br>
    キャッシュのリフレッシュをするとデータソース一覧が表示され選択出来るようになります。<br>[Issues #6997](https://github.com/metabase/metabase/issues/6997)と同じ現象です。
3. Chromeでダッシュボード上のquestionが削除出来ない
    WindowsでChromeを使ってMetabaseのダッシュボードを編集してて
    questionを削除したいと思って右上の❌マークをクリックしても削除出来ないことがあります。
    ※「ことがある」なんで常に削除出来ないわけじゃないです。

    [Issues #5301](https://github.com/metabase/metabase/issues/5301)に同じようなことが書かれています。
    2と同様にキャッシュリフレッシュすると削除出来るようになります。
    ※リフレッシュしなくても❌マーク連打で削除出来たりもします。

# まとめ
まだまだこれから成熟していくサービスかなと感じました。
 ※バージョンもまだ0.28.3と0始まりですし。

個人的にはClojureという言語を知れたのが良かったです。
関数型言語は一度も見たことも触ったことがなかったので
関数型言語を触っていく取っ掛かりになりました。

もっとMetabaseが日本に広まればいいなーと思います。


# 番外編
今回初めてlog4jなるものがあると知ったので少し調べてみました。

## log4j
主にLogger/Appender/Layoutの3つのコンポーネントで構成されています。
各コンポーネントについて簡単に書いていきます。

### Logger
ロギングのグループ化を行います。グループ単位でログ出力を制限したり、出力方法を指定することが可能です。

```log4j.properties
log4j.rootLogger=WARN, console, metabase

<中略>

# customizations to logging by package
log4j.logger.metabase.driver=INFO
log4j.logger.metabase.middleware=DEBUG
log4j.logger.metabase.models.permissions=INFO
log4j.logger.metabase.query-processor.permissions=INFO
log4j.logger.metabase.query-processor=INFO
log4j.logger.metabase.sync=DEBUG
log4j.logger.metabase.models.field-values=INFO
log4j.logger.metabase=INFO
# c3p0 connection pools tend to log useless warnings way too often; only log actual errors
log4j.logger.com.mchange=ERROR
```

Loggerは以下のような書き方で設定します。

```
log4j.rootLogger=<ログ出力レベル>,<Appender1>,<Appender2>,...
log4j.logger.<Logger名>=<ログ出力レベル(省略可)>,<Appender1(省略可)>,<Appender2(省略可)>,...
```

親となるrootLoggerのログレベルは"WARN”、出力するAppenderは"console"と"metabase"。

Logger"metabase"はいくつかのパッケージ単位(driver,middleware...etc.)でログレベルを出力しています。

ちなみにログレベルは以下のようになっています。

```
DEBUG < INFO < WARN < ERROR < FATAL
```

検証運用だとログレベルがINFOやDEBUGでも問題ないと思いますが
本番運用する場合はWARN以上にした方がログが煩雑にならなくていいと思います。


### Appender
ログの出力先を指定します。
出力先にはファイル、コンソールなどが指定できます。

```log4j.properties
<中略>
# log to the console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{MM-dd HH:mm:ss} \u001b[1m%p %c{2}\u001b[0m :: %m%n

# log to a file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=${logfile.path}/metabase.log
log4j.appender.file.MaxFileSize=500MB
log4j.appender.file.MaxBackupIndex=2
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d [%t] %-5p%c - %m%n

log4j.appender.metabase=metabase.logger.Appender
<中略>
```

console/file/metabaseの3つのAppenderが設定してされています。

書き方のフォーマットは以下の感じです。
```
log4j.appender.<Appender名>=<Appenderクラス名>
log4j.appender.<Appender名>.<オプション>=<設定内容>
```

<Appenderクラス>には出力先を指定します。
出力先がコンソールである`org.apache.log4j.ConsoleAppender`、
出力先はファイルである`org.apache.log4j.RollingFileAppender`、
`log4j.appender.metabase=metabase.logger.Appender`では
Metabaseで定義されているクラスを指定しています。

`org.apache.log4j.RollingFileAppender`では
ログファイルが一定サイズを超えた場合、何世代かまでバックアップファイルを作成します。

Metabase独自の`metabase.logger.Appender`では
`org.apache.log4j.AppenderSkeleton`を拡張しているようですが
Clojureをイマイチ理解できていないため深いところまでは理解できませんでした...

ファイルにログ出力するAppender"file"の設定をみていきます。

| オプション| 設定内容 |
| :-:     |  :-:    |
| ファイル出力先<br>(File) |  ${logfile.path}/metabase.log  |
| ファイル最大サイズ<br>(MaxFileSize) |  500MB  |
| バックアップインデックス<br>(MaxBackupIndex) |  2インデックス |
| 出力クラス<br>(layout) |  org.apache.log4j.PatternLayout |
| 出力フォーマット<br>(layout.ConversionPattern) |  %d [%t] %-5p%c - %m%n |

RollingFileAppenderクラスではバックアップしたファイルにインデックスをつけます。
sample.logというファイルをバックアップする時は、sample.log.1/sample.log.2などとなります。
MaxBackupIndexで指定した数だけバックアップファイルが作成され
指定した数を超える場合はインデックス番号が大きいものが一つ削除されることになります。

MaxBackupIndexが2で、
バックアップ時にsample.log.1、sample.log.2があった場合、
sample.log.2の内容が削除され、新たにバックアップ内容がストアされます。

出力クラス、出力フォーマットに関しては次の「Layoutの設定」で記載します。

### Layout
ログの出力フォーマットを指定します。

Appender"file"では`org.apache.log4j.PatternLayout`クラスを指定しています。
このクラスを使用することで様々なフォーマットでログを出力することが出来ます。

そしてログは変換パターン記号を用いて以下のフォーマットで出力しています。

```%d [%t] %-5p%c - %m%n```

| 変換パターン記号 | 内容 |
| :-: | :-: |
| %d | ログイベントの日時を出力 |
| %t | ログ出力スレッド名を出力 |
| %p | ログレベルを出力<br>"-5"では左詰で5文字以下の場合、空白を挿入 |
| %c | ログイベントのカテゴリー名を出力 |
| %m | メッセージを出力 |
| %n | 改行文字(プラットフォーム依存)を出力 |

Appender"file"では

```<日時> [ログ出力関数名] <ログレベル><ログ出力カテゴリ名> - <ログメッセージ>```

とログファイルにログが保存されるということです。