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


概要

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

image.png

構築

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

事前準備

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

docker container ls -a

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

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

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

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

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

各コンテナがログをLokiへ転送するように設定する

Logging Driverの設定を変更することでLokiへログを転送させるようにする。
基本的には公式のdocker-driverのREADMEに書かれている通りに設定を行えばよい。log-optsに記載できるオプションがたくさんあるようなので詳しく知りたい場合も同ページを参照。

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

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

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

systemctl restart docker.service 

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

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

コード取得

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

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に送る感じなのではないかと思う。

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はどちらでも。

docker-compose up -d

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

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

  • ブラウザでGrafana(デフォルトでは「http://<サーバアドレス>:3000」)にアクセスし、admin/adminでログインする。
  • adminユーザのパスワード変更を求められるので適当に変更
  • 「Add data source」をクリックし、Lokiをクリック
  • URLに「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等で個別に変更するつもりの場合は注意

使う

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

その他

ダメだったこと

そもそもコンテナのログは/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と表示されてしまってログが見れない。
サーバ上でログを見たい場合は注意