Docker は 1.6 から Logging Drivers というのが追加され、コンテナからのログというものをどのように扱うかをドライバとして差し替えできるようになったとのこと。
そして Docker 1.8 からは Fluentd logging driver が追加され、ログ自体を fluentd を通した状態で扱えるようになりました。ログに fluentd の tag が付与された状態になるので、fluentd 集約サーバへ転送することであとは自由に操作することができそうです。
Fluentd 集約サーバ
Docker の Fluend logging driver によって転送される先となる Fluentd 集約サーバを用意します。今回はこの Fluentd サーバも Docker コンテナとして動作させることにします。
Docker Hub に Fluent コミュニティから配布されている fluentd コンテナイメージがあるので、それを利用します。
FROM fluent/fluentd
この Fluentd Docker イメージの Dockerfile を見てみると、fluent.conf
というファイル名で設定ファイルをコンテナへコピーする段取りになっているようなので、あわせて準備します。driver のドキュメントに書いてあるサンプルとほとんど同じで、流れてきたログのうち docker タグが付いているものをそのまま stdout へ出力するだけです。
<source>
type forward
port 24224
bind 0.0.0.0
</source>
<match docker.**>
type stdout
</match>
集約サーバコンテナを起動します。
$ 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 で集約することにします。
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 にて それぞれ Nginx access.log, error.log を stdout, stderr からのシンボリックリンクとなるように処置されていますので、それぞれこのログファイルへ出力するよう Web サーバを用意すれば Logging driver でそのまま扱われるようになります。この辺りの方法は任意のソフトウェア、アプリケーションでのログ出力でも採用できそうです。
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 を指定して起動します。
$ 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 ログとして表示されることになります。
$ docker logs -f logger
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 にはそれ以外にコンテナ名や識別子も含んでいるので扱いやすそうです。
サンプルソース
今回利用したサンプルのソース。
備考
Docker 1.8 と共にリリースされた Docker Compose 1.4 ではまだ新しい Logging driver はサポートされていないようで、Fluentd を利用することができませんでした。Compose から Engine へ渡す情報としてドライバとそのオプションの定義が追加されれば使えるようになると思われます。