Docker 1.8 の Fluentd logging driver でコンテナログをフォワードする



Docker は 1.6 から [Logging Drivers](https://docs.docker.com/reference/logging/overview/) というのが追加され、コンテナからのログというものをどのように扱うかをドライバとして差し替えできるようになったとのこと。

そして Docker 1.8 からは [Fluentd logging driver](https://docs.docker.com/reference/logging/fluentd/) が追加され、ログ自体を fluentd を通した状態で扱えるようになりました。ログに fluentd の tag が付与された状態になるので、fluentd 集約サーバへ転送することであとは自由に操作することができそうです。


## Fluentd 集約サーバ

Docker の Fluend logging driver によって転送される先となる Fluentd 集約サーバを用意します。今回はこの Fluentd サーバも Docker コンテナとして動作させることにします。
Docker Hub に Fluent コミュニティから配布されている fluentd コンテナイメージがあるので、それを利用します。

```plain:Dockerfile
FROM fluent/fluentd
```

この [Fluentd Docker イメージの Dockerfile](https://github.com/fluent/fluentd-docker-image/blob/master/Dockerfile) を見てみると、`fluent.conf` というファイル名で設定ファイルをコンテナへコピーする段取りになっているようなので、あわせて準備します。driver のドキュメントに書いてあるサンプルとほとんど同じで、流れてきたログのうち docker タグが付いているものをそのまま stdout へ出力するだけです。

```fluentd.conf
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>
<match docker.**>
  type stdout
</match>
```

集約サーバコンテナを起動します。

```bash:fluentd集約サーバコンテナの起動
$ docker build -t fluentd .
$ docker run -d -p 24224:24224 --name logger fluentd
```


## コンテナログを Fluentd logging driver で扱う

コンテナからの任意のログを Fluentd logging driver で取り扱います。logging driver でログとして扱われるソースは stdout, stderr なので、ロギングしたい内容をこれらへ出力するようコンテナ内で振舞えば良さそうです。

今回はシンプルに Nginx のアクセスログ、エラーログを Fluentd で集約することにします。

```plain:Dockerfile
FROM nginx
ADD server.conf /etc/nginx/conf.d/server.conf

RUN mkdir -p /var/www/html
ADD index.html  /var/www/html/index.html
```

Docker オフィシャルの Nginx コンテナイメージは [Dockerfile](https://github.com/nginxinc/docker-nginx/blob/master/Dockerfile#L15) にて それぞれ Nginx access.log, error.log を stdout, stderr からのシンボリックリンクとなるように処置されていますので、それぞれこのログファイルへ出力するよう Web サーバを用意すれば Logging driver でそのまま扱われるようになります。この辺りの方法は任意のソフトウェア、アプリケーションでのログ出力でも採用できそうです。


```server.conf
server {
    listen 80 default;
    server_name _;
    root /var/www/html;
    index index.html;
    charset utf-8;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
}
```


Web サーバコンテナを Fluentd logging driver を指定して起動します。

```bash:nginxコンテナの起動
$ docker build -t web .
$ docker run -d --name w1 -p 80:80 \
  --log-driver=fluentd \
  --log-opt=fluentd-address=localhost:24224 \
  --log-opt=fluentd-tag=docker.{{.FullID}} \
  web
```

適当にアクセスしつつ、Fluentd 集約サーバコンテナのログを `docker logs` コマンドで確認します。集約サーバの設定で、Logging driver によって流れてきたログをそのまま標準出力するようにしていますので、Docker ログとして表示されることになります。

```bash
$ docker logs -f logger
```

```plain:FluentdコンテナのDockerログ
2015-08-19 13:38:13 +0000 docker.caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55: {"container_name":"/w1","source":"stdout","log":"192.168.99.1 - - [19/Aug/2015:13:38:13 +0000] \"GET / HTTP/1.1\" 200 4 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0\"","container_id":"caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55"}
2015-08-19 13:38:16 +0000 docker.caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55: {"log":"192.168.99.1 - - [19/Aug/2015:13:38:16 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0\"","container_id":"caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55","container_name":"/w1","source":"stdout"}
2015-08-19 13:38:52 +0000 docker.caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55: {"source":"stderr","log":"2015/08/19 13:38:52 [error] 6#6: *1 open() \"/var/www/html/invlaid.html\" failed (2: No such file or directory), client: 192.168.99.1, server: _, request: \"GET /invlaid.html HTTP/1.1\", host: \"192.168.99.101\"","container_id":"caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55","container_name":"/w1"}
2015-08-19 13:38:52 +0000 docker.caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55: {"source":"stdout","log":"192.168.99.1 - - [19/Aug/2015:13:38:52 +0000] \"GET /invlaid.html HTTP/1.1\" 404 168 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0\"","container_id":"caba8efd75ea1b2fec0f44422ec774e96506c86aab29eed2c0e517acacaf7d55","container_name":"/w1"}
```

アクセスログ、エラーログの実体は JSON 内の log 要素のようです。JSON にはそれ以外にコンテナ名や識別子も含んでいるので扱いやすそうです。

## サンプルソース

今回利用したサンプルのソース。

* https://github.com/kumatch/sandbox-docker-fluentd-logging



## 備考

Docker 1.8 と共にリリースされた Docker Compose 1.4 ではまだ新しい Logging driver はサポートされていないようで、Fluentd を利用することができませんでした。Compose から Engine へ渡す情報としてドライバとそのオプションの定義が追加されれば使えるようになると思われます。