Grafana lokiを利用してコンテナログの収集をしてみた


この記事は[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware) 5日目の記事です。

# はじめに
NTTコムウェアの寺島です。私は、開発チームの技術的なサポートであったり、技術ナレッジの蓄積やそれを組織に展開する役割を担ったチームに今年の10月から所属しています。
今回の記事は、私が異動前に担当していたシステムで採用したGrafana loki（Grafanaと分けるため、以降Lokiと記載）が元ネタとなります。

# Lokiを採用した背景とその理由
担当したシステムは、dockerコンテナでのアプリ実行とコンテナオーケストレータとしてNomad/サービスディスカバリとしてConsulを採用したシステムになります。当たり前ですがオーケストレータを利用した場合、コンテナが実行されるホストは固定されないため、コンテナのログをサーバにSSHでログインして確認することは困難です。また、NomadのCLIを利用して、特定ホストから各コンテナログを確認できますが、CLIベースであるためログ確認はしづらいものです。

このような背景から、今回Lokiを採用した理由は以下の3つになります。

- CLIベースでのログ検索ではなく、より人間が検索しやすいものにしたかった。
- サーバメトリクスのモニタリングとしてGrafana/Promethusを採用しており、親和性が高かった。
- よく使われるログ基盤であるEFKスタック（Elasticsearch/Fluentd/Kibana）の採用も考えたが、トラブル時のデバック用途としてはオーバースペックだった。

# Lokiとは
LokiはGrafana labsが公開しているログの検索や収集ができるOSSツールです。
プロジェクトとしては2018年にスタートしており、2019年に安定版が出たもので、比較的最近のものです。
特徴は他のロギングシステムと異なり、ログ自体をインデックス化せず（Promethusラベルと同様に）、ログに関連するメタデータにインデックスをつけるという考えの元に構成されています。そのため、ログ自体の大きいインデックスを持たなくてもよく、低コストでの導入が可能です。他のロギングシステムように長期間のログ情報に対して傾向を分析するような使い方に向いていませんが、トラブル時のログ解析だけに的を絞るのであれば十分な機能を備えています。また、公式ではマルチテナントの機能を持っており、スケーラブルと記載がありますが、今回はそこは利用しておりません。

参考：[Grafana Loki](https://grafana.com/oss/loki/)

# 構成
Loki自体はログの集約と検索が行えるツールであるため、ログの収集は別のツールを利用する必要があります。今回、ログの収集はGrafana Labsが公開しているPromtailを採用しています。その他にもFluentd、Fluentbitなどが利用できます([クライアント一覧](https://grafana.com/docs/loki/latest/clients/))。仕組みとしては、コンテナ実行サーバにPromtailのコンテナを配置して、標準出力されているアプリのログをコンテナ実行サーバのjournaldに送り、それをPromtailが収集してLokiに送るようにしています。またPromtailを採用した理由の一つとして、Promethusを組み合わせることでログデータからメトリクスを作成することが可能なためです（イメージとしてPromtailをPromethusのexporterとして動作するイメージですが、本記事では設定方法は割愛します）。ログデータはS3/GCSなどにも保存可能ですが、今回はローカルに保存する形式にしています。

![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303038/4fe04f86-d083-3934-9328-bfc46c0d891f.png)

## Loki設定
LokiはNomadから起動しており、コンテナ設定（抜粋）は以下の通りです。永続化データは/loki配下に出力されるため、ホスト側にマウントしています。

```
    task "loki"{
      driver = "docker"
      config {
        image= "grafana/loki:2.2.0"
        ports = ["loki"]

        volumes = [
          "/hostvolume/loki:/loki"        
        ]
      }

    }
```

lokiの設定（local-config.yaml）はログデータの保存期間などをデフォルトから変更して利用しています。

```yaml
#  local-config.yamlを一部抜粋
table_manager:
  retention_deletes_enabled: true
  retention_period: 8760h
```


## 各コンテナ設定
Nomadのコンフィグの抜粋になりますが、dockerのlogging driverをjournaldに設定します。これによって、このコンテナのログがjouraldへ送信されます。

```
  task "AAABBB" {
      driver = "docker"

     config {
        image = "AAAABBB"
        ports = ["http"]
        logging {
          type = "journald"
        }
      }
        resources {
          cpu    = 1000 
          memory = 1024 
        }
    }
  }

```
## Promtail設定
Promtailのコンテナ設定は、ホスト側のjarnaldファイルが読めるようにNomad設定ファイルを記述します。以下のようにホストの`/var/log/journal`及び`/run/log/journal`をマウントしてあげます。

```
  task "promtail"{
      driver = "docker"
      config{
        image = "grafana/promtail:2.1.0-amd64"
        volumes = [
          "/var/log/journal:/var/log/journal:ro",
          "/run/log/journal:/run/log/journal:ro",
          "local/:/etc/promtail/",
          "/tmp:/tmp"
        ]
        ports=["promtail"]
      }
    }

```


以下がpromtailのログ収集設定になります。clientsに記載されているurlは、Lokiのサーバを指定しています（各サーバにてDNSでconsulに登録されているサービスの名前解決ができるようにしています）。relabel_configsにて、ラベルを扱いやすいものに変更しています。

```yaml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml
clients:
- url: http://loki.service.consul:3100/loki/api/v1/push

scrape_configs:
  - job_name: journal
    journal:
      max_age: 12h
      labels:
        job: systemd-journal
      path: /var/log/journal
    relabel_configs:
      - source_labels:
        - '__journal__systemd_unit'
        target_label: 'unit'
      - source_labels:
        - '__journal__hostname'
        target_label: 'host'
      - source_labels:
        - __journal_container_name
        target_label: container_name
```
## Grafana設定
GrafanaのDataSourceとしてLokiを選択し、LokiのURLを指定してSave&Testをクリックして問題なければ設定完了です。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303038/3b7e94b1-dd8a-3dce-3714-c608b1f15a5d.png)


# コンテナのログを検索してみる
GrafanaのExploreからDataSourceとしてLokiを選択します。ログが収集されているとLoglabelsのプルダウン上に、relabel_configsで設定したラベルが出てきます。以下の図は、container_nameとしてnginxコンテナとhostを指定した場合のログ結果です。
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303038/e22a68ce-374e-d1f6-75c2-2a07ef40e05e.png)

次にログをgrepすることも可能なので、ステータスコードが404のログを検索してみます。`|~ "HTTP/1.1\" 404"`の文字列を追加して検索してみると、以下のようにステータスコード404のログがgrepできます。
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303038/2bd67c99-0e0b-e6df-df31-34ccedaf738a.png)

# まとめ
Lokiを利用してdockerコンテナのログ収集及び検索を実現しました。2019年に安定版となったツールであるため、導入に際しては日本語ドキュメントなどの情報が少ないというのが辛いところでした。導入してみての感想は、Lokiのコンセプトのように用途を絞った使い方であればログ基盤としては十分かと感じています。今回紹介していませんが、Promtailにてログをメトリクス化も取り入れています。
GrafanaのプロジェクトとしてLokiと同様のコンセプトでトレースに関するツールである[Grafana Tempo](https://grafana.com/oss/tempo/)が2020年に発表されました。まだ安定版に至っていないですがこの辺りの動きも今後注視していきたいと考えています。