コンテナ上のProxyサーバのログをログサーバに転送する

## はじめに

Dockerのコンテナなどを用いてProxyサーバを立てることがありますが、そこで生成される`access.log`はそのままではログサーバに送ることが出来ません。この記事ではこの課題に対する1つの解決方法を提示します。

## 解決方法

内容は以下の通りです。

1. ログ保管場所を永続化させるようにした Proxyサーバコンテナの作成する。
2. ホスト側のrsyslogdの設定で上記の`access.log`をリモートのsyslogサーバに転送する。

### Proxyサーバコンテナの構築

ここではProxyサーバはSquidで構築します。コンテナ内の`/var/log/squid`をホスト側の`/home/user/containers/proxy/logs/`にバインドします。`Dockerfile`と`docker-compose.yml`は以下の通りです。ディレクトリの位置は各自の都合で変更してください。

/home/user/containers/proxy/Dockerfile
```Dockerfile
FROM ubuntu:latest

# Installing squid
RUN apt update && \
    apt install -y squid

# Squid settings
RUN sed -ri 's/^#http_access allow localnet/http_access allow localnet/' /etc/squid/squid.conf && \
    sed -ri 's/#cache_dir/cache_dir/' /etc/squid/squid.conf && \
    sed -ri 's/^cache_dir/#cache_dir/' /etc/squid/squid.conf && \
    echo 'cache_dir ufs /var/spool/squid 100 16 256' >> /etc/squid/squid.conf

# Initialize Squid cache directory
RUN squid -z

EXPOSE 3128/tcp

CMD [ "squid", "-N" ]
```

/home/user/containers/proxy/docker-compose.yml
```yml
version: '3'

services:
  squid:
    build: .
    ports:
      - "3128:3128"
    volumes:
      - ./logs:/var/log/squid
      - ./cache:/var/spool/squid
    logging:
      driver: syslog
      options:
         syslog-address: "tcp://192.168.***.***:514"
         tag: proxy

    restart: always
```

コンテナ内のSquidはDockerのログドライバを利用してくれないのですが、Dockerコンテナ自身のエラーを集約するために`docker-compose.yml`の中にsyslogサーバへの転送を記述しています。IPアドレスを各自の環境に合わせて修正してください。

次に必要なディレクトリを作成します。

```bash
$ cd /home/user/containers/proxy
$ mkdir cache logs
$ sudo usermod -aG user proxy
$ sudo usermod -aG syslog proxy
$ sudo chown proxy.proxy cache logs
$ sudo chmod 775 cache logs
```

3行目の`user`は管理者がユーザ権限でログを調査したい場合などのためにあった方が良い設定です。`user`は各自のユーザ名に置き換えてください。

設定が出来たらコンテナを起動させます。

```bash
$ docker compose up -d
```

### rsyslog設定ファイルの変更

コンテナ内のログをログサーバに転送したいという要求は他のコンテナでも起こるかもしれませんので、コンテナごとに設定ファイルを分けます。以下のファイルを`/etc/rsyslog.d/`に追加します。

/etc/rsyslog.d/30-proxy_container.conf
```conf
module(load="imfile")
input(type="imfile"
      file="/home/user/containers/proxy/logs/access.log"
      tag="proxy"
      facility="local0"
      severity="notice")
:syslogtag, isequal, "proxy" @@192.168.***.***:514
```

この設定のポイントは`imfile`のモジュールを使ってファイルを監視するところで、ファイルの更新があるとその更新分をrsyslogdで処理することになります。監視するファイルと`tag`を書き換えた設定ファイルを増やすことで他のコンテナのログ転送もすることが出来ます。

最後の行のIPアドレスはリモートのログサーバのIPアドレスとします。たいていの場合は、`docker-compose.yml`に記述したログサーバのIPアドレスと同じになるでしょう。

設定が出来たらrsyslogdを再起動します。

```bash
$ sudo service rsyslog restart
$ sudo service rsyslog status
● rsyslog.service - System Logging Service
     Loaded: loaded (/lib/systemd/system/rsyslog.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-09-08 08:06:09 UTC; 31s ago
(以下略)
```

ステータス確認でエラーが起きている場合は設定ファイルに誤りがある場合や、ログファイルアクセスのためのパーミッションが足りない場合があります。エラーメッセージを読んで確認してください。

### 動作確認

ウェブブラウザなどでProxyサーバの設定をして適当なページをアクセスしてください。ログサーバに`access.log`の内容が転送されていれば成功です。

## その他

### はまりポイント

私の場合は`/home/user/containers/proxy`以下にコンテナ関連ファイルを置きましたが、最初は以下のエラーが発生しました。

```
imfile: on startup file '/home/user/containers/proxy/log/access.log' does not exist 
but is configured in static file monitor - this may indicate a misconfiguration.
```

これは、`/home/user`のパーミッションが`700`だったためでしたので、`sudo chmod 755 ~`などとしてアクセス許可をしました。解決方法は他にもありますので、皆さんの都合の良い方法を適用していただければと思います。

### 残作業

1. ホスト上のログファイルの取り扱い

   `/var/log/squid`を永続化したのは良いのですが、このままではホストのストレージが溢れてしまいます。`logrotate`の設定などが必要です。リモートのログサーバへの転送が確実に行われていることを確認出来たら、ここでのログ保存は数時間程度分だけでも良いかもしれません。

2. 転送用syslogサーバのコンテナ化

   今回はコンテナホスト側でsyslogサーバを動かしていたのでそのまま使いましたが、軽量ホストを使う場合は`rsyslogd`もコンテナ化することになるでしょう。また、上記ログ管理のために`logrotate`のコンテナも作ることになります。機能ごとに`rsyslogd`コンテナや`logrotate`コンテナを立てるか、ホスト上にある全てのコンテナのログを管理するための`rsyslogd`コンテナ、`logrotate`コンテナを立てるかは思案のしどころです。

## 考察

この作業をするために検討していたことを記しておきます。

1. コンテナ上にsquidとrsyslogdを同居させる

   [この記事](https://qiita.com/bashaway/items/9e8538aa340e591231cb)など、いくつかの事例がありますが、どれも2つの機能を実行させるために`init`や`startup.sh`などでコンテナの最後のコマンドを実行する必要があります。ダメというわけではありませんが、以下の事を考慮して見送りました。

   1. シンプルでなく、メンテナンス上の問題を抱える可能性がある。
   2. `squid`から`rsyslogd`にログを送る際に`journald`などを使う場合はさらに設定が複雑になる(のかな？)。

   もっと良い方法がありましたら教えてください。

2. Dockerのログドライバ

   sshサーバコンテナを作った時は`sshd_config`に特別な設定をしなくてもDockerのログドライバでリモートログサーバにログを転送できました。Squidでは`squid.conf`に`access.log syslog:local0.info combined`を追加してみても、Dockerのログドライバからリモートログサーバに転送してくれませんでした。何か勘違いがあったら教えてください。

以上です。