DockerコンテナのロギングにGrafana＋Lokiを試してみた

# 概要
最近急にクラウドネイティブ界隈で話題になっているらしいロギングツールの[Loki](https://grafana.com/oss/loki)をDockerで試してみた。
コンテナのロギングと言えば今のところEFKスタック(Elasticsearch + Fluentd + Kibana)が一般的だが、個人的な開発環境や遊び環境だとElasticsearchが重いせいでEFKを使うことに抵抗があった。しかし、Lokiはちょっと使ってみた感じだと全然リソース消費が気にならず、シンプルでお手軽に導入できることからDockerの開発環境にはとりあえず入れておいた方がいいレベルのものになるかもしれない。
まだバージョン1.0ではなく、今後はPrometheusとも連携できるようになるようだが、個人的に使う分には現状でも十分役立ちそう。

![image.png](https://grafana.com/dist/471f7527a8a8d06eb4381e54343199d7.png)

# 構築

* DockerにLokiのプラグインをインストールする
* 各コンテナがログをLokiへ転送するように設定する
* ログ収集用のLokiコンテナと可視化用のGrafanaコンテナを起動する
* GrafanaからLokiに接続する設定をする

## 事前準備
コンテナが一つもあがっていないことを確認
あがっていたら消しておく。

```
docker container ls -a
```

## DockerにLokiのプラグインをインストールする

コマンド1つ実行するだけ。

```shell
docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
```

インストール確認は``docker plugin ls``でできる。

```shell
$ docker plugin ls
ID                  NAME                DESCRIPTION           ENABLED
bb97061da4a3        loki:latest         Loki Logging Driver   true
```

## 各コンテナがログをLokiへ転送するように設定する
Logging Driverの設定を変更することでLokiへログを転送させるようにする。
基本的には[公式のdocker-driverのREADME](https://github.com/grafana/loki/blob/master/cmd/docker-driver/README.md)に書かれている通りに設定を行えばよい。log-optsに記載できるオプションがたくさんあるようなので詳しく知りたい場合も同ページを参照。

同一ホスト上でデフォルトのポート番号でLokiを起動すれば``localhost:3100``でアクセスできるようになるため、``daemon.json``を変更する場合は以下のように記載すればよい。
コンテナごとに指定したい場合は、各コンテナを起動する``docker-compose.yaml``の各サービスごとにloggingのオプションを記載する必要があるので結構手間

```json:daemon.json
{
    "debug" : true,
    "log-driver": "loki",
    "log-opts": {
        "loki-url": "http://localhost:3100/api/prom/push",
        "loki-batch-size": "400"
    }
}
```

``daemon.json``の変更を反映させるためにDockerを再起動する。

```shell
systemctl restart docker.service 
```

まだLokiを起動していないが、とりあえずこれで今後起動したコンテナのログがLokiに転送されるようになる。
次に肝心のLokiとGrafanaを起動する。

## ログ収集用のLokiコンテナと可視化用のGrafanaコンテナを起動する
### コード取得

[公式のGitHub](https://github.com/grafana/loki)からコードを取得する。
https://github.com/grafana/loki/blob/master/production/docker-compose.yaml のみあれば十分なので、``git clone``せずに直接``docker-compose.yaml``を作成してしまっても全然問題無い。

```shell
git clone https://github.com/grafana/loki.git
cd loki/production/
```

### docker-compose.yamlの編集

デフォルトではpromtailを使ってホストの``/var/log/*log``も収集するようになっているが、Dockerコンテナのログのみ収集したい場合は不要なのでpromtail部分をコメントアウトor削除する。
ホストのログも収集したい場合はそのままでよい。
Lokiのホスト側のポート番号を変更する場合は``daemon.json``の方も合わせるのを忘れずに。
Grafanaの方のポートは外部からブラウザアクセスで使うだけなので好きな番号にしておそらく問題無い。

いきなりpromtailというものが出てきたが、イメージ的にはおそらく以下のように思っていいと思う。

```
Loki = Elasticsearch みたいなやつ（ログが集まる場所）
promtail = Fluentd みたいなやつ（ログ集めるやつ）
Grafana = Kibana みたいなやつ（集まったログを見やすくするやつ）
```

直接Lokiにログを送れてしまうのでpromtailの存在意義がわかりにくいが、おそらく標準出力に出てくるタイプの一般的なコンテナのログはLokiに直接送って、ファイルに書き出されていくタイプのログ（主にサーバのログ）はpromtail経由でLokiに送る感じなのではないかと思う。

```yaml:docker-compose.yaml
version: "3"

networks:
  loki:

services:
  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - loki

#  promtail:
#    image: grafana/promtail:latest
#    volumes:
#      - /var/log:/var/log
#    command: -config.file=/etc/promtail/docker-config.yaml
#    networks:
#      - loki

  grafana:
    image: grafana/grafana:master
    ports:
      - "3000:3000"
    networks:
      - loki
```

編集が終わったらLokiとGrafanaのコンテナを起動
ちゃんと使うならLokiのログが溜め込まれていくディレクトリを永続化しておくべきだと思う。
Grafanaはどちらでも。

```shell
docker-compose up -d
```

## GrafanaでLokiに接続する設定をする

※ここの設定は画面上で手動で行わなくても、事前に設定ファイル作っておいてコンテナ起動時に送る方法があると思う。

* ブラウザでGrafana（デフォルトでは「[http://＜サーバアドレス＞:3000](http://＜サーバアドレス＞:3000)」）にアクセスし、admin/adminでログインする。
* adminユーザのパスワード変更を求められるので適当に変更
* 「Add data source」をクリックし、Lokiをクリック
* URLに「[http://loki:3100](http://loki:3100)」と入力し、「Save & Test」をクリック

``Data source connected and labels found.``と表示されたら成功
なお、接続に成功してもLoki起動後に追加されたログが1つも無い場合は``Data source connected, but no labels received. Verify that Loki and Promtail is configured properly.``というエラーが出る。とりあえず何かしらのログを発生させればこのエラーは表示されなくなる。
``daemon.json``を変更した場合は既にLokiとGrafanaのログが入っているはずなのでエラーになることはないと思うが、``docker-compose.yaml``等で個別に変更するつもりの場合は注意

<img width="700px" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116209/5a92c9fb-d298-e9c6-3dc9-a1eef3d47423.png">

## 使う

あとは適当にコンテナを起動した後に、サイドバーからExploreをクリックし、左上のLog labelsで適当に見たいコンテナ名などをクリックすればそのコンテナのログが表示される。ホスト単位で見たり複数のコンテナを指定して見たりもできる。
デフォルトでは自動で更新されないため、右上のRefleshボタンで適当に更新間隔を変更しておくと便利
最初の画面にサンプルで記載されているが、``{app="cassandra"} (duration|latency)\s*(=|is|of)\s*[\d\.]+``のように正規表現でフィルタリングして表示も可能なのでdockerコマンドで頑張ってログを見るよりもだいぶ楽ができる。
ただダッシュボードがLokiに未対応のようで、画面を開くたびにQueryの入力が必要になるところが面倒

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116209/f4b3b9a4-d2b6-36c5-a004-df4ce769240a.png)

## その他

### ダメだったこと
そもそもコンテナのログは``/var/lib/docker/containers/＜コンテナID＞/＜コンテナID＞-json.log``に書き込まれているわけで、最初はpromtailに``/var/lib/docker/containers/``をマウントさせてjsonファイルのみ取り込む方式でいけるのではないかと思った。
この方法だと``daemon.json``などを変更する必要もなく、既に起動中のコンテナのログを後から収集対象にできるから便利に使える。
が、同じことをしようとしていた人がいて、過去のバージョンでは強引にやれないこともなかったが今はやれなくなってしまっている様子。https://github.com/grafana/loki/issues/333

### サーバ上でもログを見たい場合
Lokiへログを転送する設定にしてもホストの``/var/lib/docker/containers/＜コンテナID＞/＜コンテナID＞-json.log``には通常通りログが書き込まれているらしく、``docker container logs``コマンドで普通にログを見ることができる。
ただし、なぜか``docker-compose logs``コマンドでは``WARNING: no logs are available with the 'loki' log driver``と表示されてしまってログが見れない。
サーバ上でログを見たい場合は注意