Grafana LokiでCaddyのアクセスログを扱う

- Webサーバにおけるアクセスログの記録や可視化は、「**トラフィックの分析によるパフォーマンス検証**」や「**セキュリティ上の脅威の検出**」などに役立つため非常に重要です。
- 今回はWebサーバー構築ソフトウェアの1つである[Caddy](https://caddyserver.com/)が出力するアクセスログを、データ可視化ツールである[Grafana](https://grafana.com/)及びログ集計システムである[Grafana Loki](https://grafana.com/oss/loki/)で扱う方法を記録いたします。

## 環境
- macOS 13.0
- Docker version 24.0.2
- Docker Compose version v2.18.1

## 手順

### コンテナでの環境構築
- 今回は以下の必要な環境をDockerで用意します。
    - **Caddy**: Webサーバー
    - **Grafana**: データ可視化
    - **Grafana Loki**: ログ集計
    - **Promtail**: ログ送信エージェント
- まず必要なディレクトリ及びファイルを作成します。

```shell
# 任意ディレクトリ作成及び移動
mkdir caddy-log-monitoring
cd caddy-log-monitoring

# 静的ファイル表示用
mkdir public
touch public/index.html

# Caddy設定ファイル作成
touch Caddyfile

# コンテナ起動のための設定ファイル作成
touch docker-compose.yml

# Promtail設定ファイル作成
touch promtail.yaml
```

- 次に以下の内容を`public/index.html`に記述します。
    - **※Webサーバー表示確認用なので内容は任意。**

```html
<h1>テスト</h1>
```

- 次に「**ログの出力先やサイズ**」等の設定のための以下の内容を`Caddyfile`に記述します。
    - [参考](https://caddyserver.com/docs/caddyfile/directives/log)

```conf
:80 {
	file_server {
		root /srv/public
	}
	log {
		output file /var/log/caddy/access.log {
			roll_size 100mb
			roll_keep 5
			roll_keep_for 720h
		}
	}
}
```

- 次にコンテナ設定のための以下の内容を`docker-compose.yml`に記述します。

```yaml
version: '3'
services:
  caddy:
    image: caddy:2.7-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./public:/srv/public
      - ./caddy/logs:/var/log/caddy
    networks:
      - monitoring
  grafana:
    image: grafana/grafana
    ports:
      - '3000:3000'
    volumes:
      - ./grafana:/var/lib/grafana
    networks:
      - monitoring
  loki:
    image: grafana/loki
    ports:
      - 3100:3100
    networks:
      - monitoring
  promtail:
    image: grafana/promtail
    volumes:
      - ./promtail.yaml:/etc/promtail/promtail-config.yaml
      - ./caddy/logs:/var/log/caddy
    command: -config.file=/etc/promtail/promtail-config.yaml
    depends_on:
      - loki
    networks:
      - monitoring
networks:
  monitoring:
```

- 次にLokiへのログ送信設定のための以下の内容を`promtail.yaml`に記述します。
    - **※試しなのでステータス(status)と処理時間(duration)の最低限必要そうな値を指定。**

```yaml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
- job_name: caddy
  static_configs:
  - targets:
      - localhost
    labels:
      job: caddy
      __path__: /var/log/caddy/*log
      agent: caddy-promtail
  pipeline_stages:
  - json:
      expressions:
        duration: duration
        status: status
  - labels:
      duration:
      status:
```

- 必要なファイル及び内容が揃ったので、以下のコマンドでコンテナを起動します。
    - **※起動後、Grafana関連のログが出力されるので、一通り終わるまで待つ。**

```shell
docker compose up
```

- 起動後、まず`localhost`にアクセスして以下のようなWebサーバー起動及び静的ファイル表示を確認します。

![static-file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/375e5045-2651-867a-78f9-856b8726f5d7.png)

- 次に「**Webサーバーのパフォーマンス検証**」及び「**アクセスログの一括追加**」のため、以下のように[ab](https://httpd.apache.org/docs/2.4/programs/ab.html)コマンドを実行します。

```shell
ab -c 10 -n 10 http://localhost/

＊＊＊＊＊＊＊
Requests per second:    371.54 [#/sec] (mean)
Time per request:       26.915 [ms] (mean)
Time per request:       2.692 [ms] (mean, across all concurrent requests)
Transfer rate:          85.27 [Kbytes/sec] received
```

- 次に`localhost:3000`にアクセスして以下のようにGrafanaログイン画面が表示されることを確認します。

![grafana-login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/2e12a782-14fb-2431-0ae5-4b912235f918.png)

- 表示後、以下のデフォルト認証情報でログインします。
    - username: admin
    - password: admin
- ログイン後、以下のように新規パスワードを求められるので任意の値で設定してログインしてホーム画面表示を確認します。

![grafana-new-pass.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/fd45b727-8d85-d378-9862-77ad3a45bf8a.png)

![grafana-home.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/6df2c8ac-feaa-80b5-6923-bd428f925ada.png)

### データソース設定
- 必要な環境が構築できたので、GrafanaにCaddyのアクセスログを[データソース](https://grafana.com/docs/grafana/latest/datasources/)として設定していきます。
- 扱うアクセスログは上記の`Caddyfile`でも設定したように、`/var/log/caddy/access.log`が対象です。
    - **※ローカルとマウントしているので、ローカルでは`caddy/logs`内で確認できます。**
- まず上記のGrafanaのHome画面から「**DATA SOURCES**」をクリックしてください。
- 次に以下のように「Add data source」画面のフォームに「**Loki**」と入力して表示されるものを選択してください。

![grafana-datasource.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/3e930b5b-1b1d-5436-a7b5-0de7057c6d18.png)

- 以下のように「HTTP」欄に`http://loki:3100`を入力して「**Save & Test**」をクリックして接続成功を確認してください。

![loki-conf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/74f445a9-cc81-70bb-3f8a-d13c13157a96.png)

![grafana-data-success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/3b2e4f97-5fb8-8b71-9723-353f2885c41d.png)

### クエリ実行
- データソースの登録が完了したので、[LogQL](https://grafana.com/docs/loki/latest/logql/)というLokiで扱えるログ集約クエリ言語を利用してみます。
- まず左サイドメニューから「**Explore**」画面へ遷移します。

![grafana-exploer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/ba73d9d1-ebbe-53dd-9f94-a0781b7f36e2.png)

- 遷移後、以下のように左上のデータソースが「**Loki**」となっていることを確認して、右端の「**Code**」ボタンをクリックして、真ん中のクエリ入力欄を開きます。

![loki-query.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/257635cc-0175-8224-94e4-3756ba9285d5.png)

- クエリ入力欄に以下のLogQLを入力して、右上の「**Run query**」ボタンをクリックして実行してください。

```
{filename="/var/log/caddy/access.log"} | json | line_format "duration is {{.duration}}\nstatus is {{.status}}"
```

- 実行後、以下のように画面下に可視化された**簡易的なグラフ**と**ログ**が出力されることを確認します。

![loki-query-result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336499/ef72abe9-4432-abf8-1142-2230c528235c.png)

- 以上です。

## 補足
- 今回はダッシュボード作成までは執筆していませんが、Grafana + Lokiを利用すればアクセスログの「**IP情報・処理時間・ステータス**」等を利用した解析や分析が捗ります。

## 参考
- https://caddyserver.com/docs/caddyfile/directives/log
- https://grafana.com/
- https://grafana.com/oss/loki/
- https://grafana.com/docs/loki/latest/clients/promtail/
- https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
- https://grafana.com/docs/loki/latest/clients/promtail/stages/json/