Grafana Lokiがメジャーリリースされたのでログ収集からダッシュボード化まで試してみた

## はじめに
現在の案件でPrometheus + Grafana(+ Alertmanager + Consul)を使って監視環境を運用しているのですが、ログ監視については現場内製のツールを使用しています。

しかしせっかくなので今使用しているCNCF公認のアプリケーションと連携して、よりモダンなログ監視ができないかを調査していました。

パッと思いつくのはElasticSearch + Fluentd + KibanaのEFKスタックですが、ゼロからその環境を構築するのは結構コストがかかります。

他に代替策がないかなと考えながらCNCF Landscapeを見ていたところ、Grafana Lokiが目にとまりました。

このGrafana LokiのGitHubの概要の部分には、

> Like Prometheus, but for logs. 

と書いてあり、今のGrafanaとの相性が良さそうだと感じ試してみることにしました。本記事はその際のメモを記載したものです。


## 各関連プロジェクト、概念について解説
### Grafana Lokiとは
GitHubのdescriptionに書かれている様に、Prometheusのメトリクスのログバージョンのプロジェクトです。

後述のPromtailから送られるログを特定のストレージに保存し、Grafanaなどに連携して利用します。

### Promtail
PromtailはLokiに対してログを送るエージェントです。

取得したいログがあるアプリケーションのインスタンスに対してデプロイし、ログを取得・送信させます。

Promtailの流れとしては、

1. ログ収集対象をdiscoveryし
1. ログのストリームに対してラベルを付与し、
1. Lokiインスタンスへ上記ログを送信する

というものになっています。

### Grafanaとは
本記事の本題であるGrafana Lokiの親元のプロジェクトです。

Grafana LokiやPrometheusに格納されているログ・メトリクスをデータソースとして、ダッシュボードと呼ばれる画面で各情報を可視化します。

Promtail, Loki, Prometheus等で取得するデータのは人間にとって可読性の低いテキストデータでマシンリーダブルなので、Grafanaで可視化することでヒューマンリーダブルになります。

### Promtail + Grafana Loki + Grafanaを使った基本構成
ここまでの構成をまとめ、Grafana Lokiを使ってログを取得し、Grafanaで可視化するための大まかなフローをまとめると以下の様になります。

1. Promtailで特定のログをLokiに送信
1. LokiがPromtailから送られたログを保存・処理し、
1. Grafanaでログを取得・表示する
という感じですね。


## ハンズオン
それでは実際に手を動かして試してみましょう。

先に今回試した内容のdocker-composeファイルを記載しておきます。

順序としては、

1. flogアプリケーションでダミーログを`/var/log/flog.log`へ出力し、
1. Promtailの設定を追加し、上記flogのログを取得、Lokiへ送信するよう設定
1. Loki側でPromtailから送信されるログを受け取り、
1. GrafanaでLokiをデータソースとして追加し、ダッシュボードとして可視化する

というものになっています。

```yaml
    version: '3.7'
    networks:
        grafana:
    services:
        grafana:
            image: grafana/grafana
            environment:
                GF_EXPLORE_ENABLED=true
            ports:
                3000:3000
            networks:
                grafana
        loki:
            image: grafana/loki
            ports:
                3100:3100
            networks:
                grafana
        promtail:
            image: grafana/promtail
            ports:
                9080:9080
            command: -config.file=/etc/promtail/flog-config.yaml
            volumes:
                ./etc/promtail:/etc/promtail
                ./var/log:/var/log
            networks:
                grafana
        flog-logger:
            image: mingrammer/flog
            command: -n 5 -d 5 -t log -w -o /var/log/flog.log -l
            volumes:
                ./var/log:/var/log
            networks:
                grafana
```

### ダミーログ生成アプリケーションを立ち上げて、ログを生成する
まずはLokiで管理するためのログの生成から行なっていきます。

Docker imageでランダムなログを出力してくれる`mingrammer/flog`というimageがあったので、こちらを利用することにしました。

単純にdocker runを行うと標準出力へログを送信してくれますが、Promtailで取得しやすくするために`/var/log/flog.log`というファイルへ出力するようオプションを調節し、docker-composeの起動時に実行するように設定を行いました。

例として下記に`docker run`したときの出力を記載しておきます。いい感じの出力になっていますね。

```
$ docker run mingrammer/flog
160.42.94.201 hettinger6406 [08/12/2019:08:06:47 +0000] "DELETE /bricks-and-clicks" 501 8708
216.202.164.184 schumm2546 [08/12/2019:08:06:47 +0000] "DELETE /harness" 502 8144
127.193.172.88 [08/12/2019:08:06:47 +0000] "DELETE /cross-media" 501 10036
35.157.101.153 [08/12/2019:08:06:47 +0000] "DELETE /facilitate" 404 26412
70.73.208.218 [08/12/2019:08:06:47 +0000] "HEAD /communities/b2c/morph" 500 6639
189.108.176.231 erdman6222 [08/12/2019:08:06:47 +0000] "HEAD /best-of-breed/real-time/implement" 301 9151
100.181.144.136 hane3425 [08/12/2019:08:06:47 +0000] "DELETE /orchestrate/transform" 205 25930
(...)
```
### Promtailを立ち上げログを収集する
ログの生成ができたので、次にPromtailにログを取得させ、かつLokiへ送信させます。

Promtail containerからflog containerへ取得を行い、Loki containerのAPIへリクエストする設定を記述し、起動時に利用することを明示します。

```yaml
        server:
            http_listen_port: 9080
            grpc_listen_port: 0
        positions:
            filename: /tmp/positions.yaml
        clients:
            url: http://loki:3100/loki/api/v1/push
        scrape_configs:
            job_name: flog-logger
                static_configs:
                    targets:
                        flog-logger
                        labels:
                            job: flog
                            __path__: /var/log/flog.log
```

起動ができたら http://localhost:9080/targets をブラウザで開くことでPromtailの起動と、ログの取得ができていることが確認できます。

<img width="882" alt="Screenshot 2019-12-08 17.14.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/6daa2a53-1f8e-beac-ea8d-0561036f8a06.png">

### Grafana Lokiを立ち上げPromtailからのpushを受ける
Promtail側からLokiへログを送信する設定が適切に行えていれば、Loki側ではログが自動で保存されます。

それを確認するためには、Lokiに対してREST APIでリクエストを送ることで、格納されているログを取得することができます。

例えば`/loki/api/v1/query`へGETリクエストを送信すると、下記の様な結果を受け取ることができます。

```bash
    $ curl -G -s  "http://localhost:3100/loki/api/v1/query" --data-urlencode 'query={job="flog"}' | jq
    {
        "status": "success",
        "data": {
            "resultType": "streams",
            "result": [
                {
                    "stream": {
                        "filename": "/var/log/flog.log",
                        "job": "flog"
                    },
                    "values": [
                        [
                            "1575790467377181386",
                            "15.152.22.86 - pacocha1603 [08/12/2019:07:33:47 +0000] \"POST /mission-critical\" 203 12036"
                        ],
                        [
                            "1575790462309234273",
                            "132.100.60.192 - white7155 [08/12/2019:07:33:47 +0000] \"HEAD /envisioneer/morph/matrix/compelling\" 501 19202"
                        ],
                        (...)
                    ]
                }
            ]
        }
    }
```

これで生成されたログがPromtailから取得され、Lokiに送信されていることが分かりました。

では次にGrafanaを使ってこのログデータを可視化することにしましょう。

### GrafanaでLokiをデータソースに設定し、Explorerで可視化する

`localhost:3000`をブラウザで開くことで、Grafanaを開くことができます。

ログインの認証は`admin/admin`で可能です。

Grafanaが起動、usernameとpasswordは両方adminを入力することでログインが可能です。

<img width="1233" alt="Screenshot 2019-12-08 17.23.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/dc2ebdf4-9aad-a532-aa80-73a74f950f65.png">


ログインができたら次に可視化を行うデータ元である、データソースを指定する必要があります。

下記の様な画面がログイン後に表示されると思いますので、"Add data source"を選択しましょう。

次にどのアプリケーションをデータソースとして使うか選択する画面に移動します。

"Logging & document databases"の項目にあるLokiを選びましょう。

<img width="1230" alt="Screenshot 2019-12-08 16.41.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/ddccf168-f4ac-fe90-9389-a4825416523a.png">

そうすると接続情報を入力する画面に移動します。

今の場合はローカルのLokiを接続先として設定するので、URLのinputに`http://loki:3100`を入力し、"Save & Test"をクリックして緑色のアラートが出れば接続完了です。

<img width="1230" alt="Screenshot 2019-12-08 16.44.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/36b5e37e-dbb2-6e2c-a38d-893a1b0cc21b.png">

設定が完了したら、今度は左メニューのExplorerを選択します。

<img width="455" alt="Screenshot 2019-12-08 16.45.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/e818f952-839b-4ccf-47a1-baa32ad75a95.png">

ここでは簡易的なダッシュボードを作成することができるので、画面中の"Log Labels"からfilenameを選択すれば、下記の様なグラフとログの一覧が表示されます。

<img width="1229" alt="Screenshot 2019-12-08 16.46.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79990/03af70a5-cdae-8edf-ae80-085273577050.png">

以上で簡単ではありますが、アプリケーションによるログ出力、Promtailによるログ取得、Lokiでのログ管理、そしてGrafanaでのログデータの可視化ができました！