Docker LoggingにFluentdを使いforwardでNginxのログを送信する

過去の記事

実現したいこと

以前、NginxのログをFluentdを用いてElasticsearchに送信する際は、アクセスログをデータボリュームに書き出しtailで読み込んでElasticsearchに送信する方法で実現していた。
その際、オフィシャルのNginxコンテナのアクセスログの仕様について触れた。

  • docker container logsやdocker-compose upをフォアグラウンドで実行していると見ることのできるログに、各コンテナで起きたログを表示するには標準出力、標準エラー出力に内容を書く必要がある。
  • Nginxはアクセスログをログファイルに書きつつ標準出力に書き出す方法がないので、アクセスログを標準出力にシンボリックリンクすることで実現している。
  • そのため/var/log/nginx/access.logにいくらログを書いてもコンテナ内にログは残らなくなっている。
https://chulip.org/entry/2019/08/18/233205

上記仕様を回避するため、ログファイルをわざわざ別名で書き出すようにする必要があった。

しかし、そもそもデフォルトで標準出力にアクセスログが流れてくるのであればそれをそのままElasticsearchに渡してやればよいのでは?
そうすれば、ログファイルに別名を付ける必要もないし、ログを読み込むためだけにデータボリュームを用意する必要もない。

そこで、Docker Loggingを使用してNginxコンテナのログをFluentdに渡してそのままElasticsearchに送信できるようにする。

なお、過去の記事で使用した環境に変更を加えていくのでdiffを記載していく。

Fluentd

参考
--- a/fluentd/fluent.conf
+++ b/fluentd/fluent.conf
@@ -1,9 +1,7 @@
 <source>
-  @type tail
-  format ltsv
-  path /var/log/nginx/myapp_access.log
-  tag nginx
-  pos_file /var/log/nginx/myapp_access.log.pos
+  @type forward
+  port 24224
+  bind 0.0.0.0
 </source>
 
 <match nginx>

typeをforwardに変更。

Nginx

--- a/nginx.conf
+++ b/nginx.conf
@@ -24,8 +24,8 @@ server {
   listen 80;
   server_name localhost;
 
-  access_log /var/log/nginx/myapp_access.log ltsv;
-  error_log  /var/log/nginx/myapp_error.log;
+  access_log /var/log/nginx/access.log ltsv;
+  error_log  /var/log/nginx/error.log;
 
   root /myapp/public;

ログファイルのファイル名を変更して標準出力、標準エラー出力にアクセスログが出力されるように。

docker-compose.yml

参考
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -26,11 +26,16 @@ services:
     volumes:
       - .:/myapp
       - ./nginx.conf:/etc/nginx/conf.d/default.conf
-      - http-log:/var/log/nginx
     ports:
       - 80:80
     depends_on:
       - web
+      - fluentd
+    logging:
+      driver: fluentd
+      options:
+        tag: "nginx"
+        fluentd-async-connect: "true"
   elasticsearch:
     image: elasticsearch:7.3.0
     environment:
@@ -48,7 +53,6 @@ services:
   fluentd:
     build: ./fluentd
     volumes:
-      - http-log:/var/log/nginx
       - ./fluentd/fluent.conf:/fluentd/etc/fluent.conf
     ports:
       - 24224:24224
@@ -57,4 +61,3 @@ services:
 volumes:
   db-data:
   elastic-data:
-  http-log:

まず、loggingの設定を追加。
次に、ログファイルを参照する必要がなくなったのでログファイル用のデータボリューム関連を削除。

次に、fluentd-async-connect。
docker-compose upを行うと下記WARNINGが表示される。

WARNING: no logs are available with the 'fluentd' log driver

docker-compose.ymlのdepends_onでnginxは依存していることを記述しているが、
ただ単にコンテナの作成順序が保証されるだけで、サービスが起動するまで待ってくれるわけではない。
その為か、Nginxのコンテナ起動時にFluentdがavailableではないためWARNINGが表示される。

これは、FluentdとElasticsearchにも同様のことが言える。
Elasticsearchはコンテナ起動からサービスがavailableになるまでの時間が結構かかるのでFluendはすぐに接続することができない。

#0 Remaining retry: 14. Retry to communicate after 2 second(s).

logにもリトライをしている表示が出ていて、自分の環境だとだいたい16 secondsになるとようやく接続できている。
リトライの間隔は指数関数的に増えていく(1s,2s,4s,8s,16s)

上記の事から、Nginxコンテナ立ち上げ時にFluentdに接続できることは保証されていないので、
fluentd-async-connectをtrueにしておく。
ドキュメントによるとtrueだと、接続が確立できるまでメッセージをバッファリングしてくれるようである。

これで、docker-compose upしてlocalhostにアクセスすると、NginxのアクセスログがElasticsearchに送信されてKibanaで参照できる。
tailでアクセスログを読み込んでいた場合と異なり、containerのログなのでcontainer_idやcontainer_nameも一緒に送られる。
このあたりのフォーマットの調整は必要に応じてやればよいだろう。