4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Dockerのlogdriverを全て試してログの保存方法を考える

Last updated at Posted at 2024-03-27

はじめに

dockerを運用していると、docker logコマンドでトラブルシューティングをする機会が多いと思います。開発環境や、少人数の環境ではdocker logコマンドで十分ですが、

  • 複数のコンテナで構成されたアプリケーション全体の流れを追いたい
  • 複数のホストでコンテナを利用している
  • 法令、コンプライアンスでログの保存期間が決まっている場合

などのケースでは、課題が残ります。

dockerのログはlog driverを変更することで出力先を指定できます。 想像しやすい例だとsyslog driverでしょうか。

公式ドキュメントには、サポートされているlog driverの一覧があります。

今回は公式サポートの ドライバーすべてを試して 設定方法、使用感をお伝えしたいと思います。

ドライバ 説明
none コンテナに対するログを記録せず、 docker logs は利用できなくなる
local 独自のファイル方式で保存され、ドライバの中で一番オーバーヘッドが少ない
json-file JSON 形式で保存され、デフォルトのドライバー
syslog Syslogサーバへ転送する
journald ホストマシンのjournaldに保存する
gelf Graylog、 Logstach などのような Graylog Extended Log Format (GELF) エンドポイントに記録
fluentd ログメッセージを fluentd に記録(forward input)する
awslogs ログメッセージを Amazon CloudWatch Logs に記録する
splunk HTTP Event Collector を使い、 splunk にログメッセージを記録する
etwlogs Event Tracing for Windows (ETW) events としてログメッセージを記録する(Windows)
gcplogs Google Cloud Platform (GCP) ロギングにログメッセージを記録する

ドライバーの制限

これから紹介するdriverですが、一部を除き変更するとdocker logsコマンドでのログが確認できなくなります。Docker Enterprise のユーザは "dual logging" を利用できるため、docker logsコマンドでの確認と、ドライバーへの送信両方使えるドライバーが多いです。

表. ドライバ変更とdocker logsが両立できるドライバー

Docker Community Engine(無料) Docker Enterprise(有償)
local
json-file
journald
syslog ×→〇 ※2023/10/27検証
gelf ×→〇 ※2023/10/27検証
fluentd ×→〇 ※2023/10/27検証
awslogs ×→〇 ※2023/10/27検証
splunk ×→〇 ※2023/10/27検証
etwlogs × ※2023/10/27未検証
gcplogs ×→〇 ※2023/10/27検証

ドライバーの設定方法

log driverは以下の通り、個別に設定する方法と、システム全体で指定する方法があります。

  1. docker composeファイルに記述(個別)
  2. docker runコマンドで起動時に指定(個別)
  3. docker daemon.json設定ファイルに記述(システム全体)

以下に指定方法をそれぞれ示します。それぞれ単語の省略、単数複数など若干変換が必要ですが、基本的にはすべて同じように指定ができます。

指定方法(compose)

version: "3"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - 0.0.0.0:30080:80
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      # - ./conf.d:/etc/nginx/conf.d
    logging:
      driver: json-file
      options:
        max-file: '1'
        max-size: 3m

指定方法(docker run)

$ docker run --rm  \
  -p 0.0.0.0:30080:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx

指定方法(システム全体)

ファイルを編集 sudo vi /etc/docker/daemon.json

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

記事の以降ではcomposeのloggingセクションのみ記載します。

ドライバーの優先順位

それぞれの設定が優先順位は以下の通りです。

  1. runやcomposeの個別の指定
  2. docker daemon.jsonによる全体の指定
  3. デフォルト値 (現在は: json-file)

daemon.jsonで指定したシステム全体の設定をrunやcomposeの個別指定は上書きできます。

同じドライバーを指定することもできるので、何も設定しない場合、全体設定でrsyslogのOOファシリティ、個別に設定してrsyslogのXXファシリティを指定するなどもできます。

検証環境

検証環境は、docker composeを用いて構築したNginx Webサーバです。そのNginxコンテナのログを様々な方法で取得し検証していきます。

dockerのインストールは公式ドキュメントに従いました。本記事では、Ubuntu22.04を用いており、Dockerのバージョンは以下の通りです。

$ docker -v
Docker version 24.0.7, build afdd53b
$ docker compose version
Docker Compose version v2.21.0

作業用フォルダに移動します。

$ mkdir nginx-log
$ cd nginx-log

compose.yamlを作成します。docker-compose.yamlから徐々に移行中です

version: "3"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - 0.0.0.0:30080:80
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      # - ./conf.d:/etc/nginx/conf.d

公式ドキュメントに従い、最新のNginxコンテナから初期状態の設定ファイルを取得します。

docker runコマンドで一時的にコンテナを起動し、その中からnginx.confを取得します。

$ docker run --name tmp-nginx-container -d nginx
$ docker cp tmp-nginx-container:/etc/nginx/nginx.conf ./nginx.conf
$ docker rm -f tmp-nginx-container

nginx.confを修正します。初期設定ではファイルにログを出力しているので標準出力、標準エラーに変更します。これによりdocker経由でログを取得することができます。

アクセスログを標準出力に流すことについては賛否があります。ファイルとして保存し、従来の方法で収集する方が好まれる印象がありますが、今回の検証では標準出力に流します。

user  nginx;
worker_processes  auto;

#標準エラーに出力
error_log /dev/stderr warn;
#error_log  /var/log/nginx/error.log notice;

pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
	#標準出力に出力
    access_log /dev/stdout main;
    #access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

コンテナを起動します。

$ docker compose up -d

コンテナの起動を確認します。

$ docker compose ps -a
NAME      IMAGE          COMMAND                                          SERVICE   CREATED          STATUS          PORTS
nginx     nginx:latest   "/docker-entrypoint.sh nginx -g 'daemon off;'"   nginx     58 seconds ago   Up 57 seconds   0.0.0.0:30080->80/tcp

ブラウザでコンテナにアクセスします。

image-20231028074935993.png

ログを確認してみます。

$ docker compose logs 
nginx  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
nginx  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
nginx  | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx  | /docker-entrypoint.sh: Configuration complete; ready for start up
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:02 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 2023/10/27 22:47:02 [error] 29#29: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 192.168.144.32, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.144.34:30080", referrer: "http://192.168.144.34:30080/"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:02 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://192.168.144.34:30080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:04 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:04 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:04 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:47:04 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:49:26 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 2023/10/27 22:49:26 [error] 31#31: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 192.168.144.32, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.144.34:30080", referrer: "http://192.168.144.34:30080/"
nginx  | 192.168.144.32 - - [27/Oct/2023:22:49:26 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://192.168.144.34:30080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"

現在利用されているlog driverを確認します。json-fileが用いられているようです。

$ docker info --format '{{.LoggingDriver}}'
json-file

none

コンテナに対するログを記録せず、 docker logs は何も出力しないそうです。使う機会があるかは不明ですが。試してみます。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: none

logsコマンドで確認すると、想定通り、ログが確認できません。

$ docker compose logs nginx 
WARN[0000] Can't retrieve logs for "nginx": Error response from daemon: configured logging driver does not support reading 

一方でdocker runの場合、起動したターミナルでログが確認できます。これはログではなく、docker runコマンドの標準出力のため、想定通りの動作となります。

$ docker run --rm  \
  -p 0.0.0.0:30080:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
  --log-driver none \
  nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
192.168.144.32 - - [28/Oct/2023:00:17:08 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
192.168.144.32 - - [28/Oct/2023:00:17:08 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
192.168.144.32 - - [28/Oct/2023:00:17:09 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"

別のターミナルから起動中のnginxコンテナを見てみると想定通り、ログが確認できません。

$ docker logs eager_burnell 
Error response from daemon: configured logging driver does not support reading

Local file logging driver

ロギングドライバーlocalは、コンテナーの stdout/stderr を保存します。デフォルトでは、localドライバーはコンテナーごとに 最大100MB のログ メッセージを保存し、自動圧縮を使用してディスク上のサイズを削減します。

dockerが管理するローカルディスクに独自形式で保存するためパフォーマンスが最も良いです。しかし、ほかのアプリケーションからファイル経由でのアクセスは避けるよう記載があります。

例では、最大サイズを10MBに、ローテーションを3に指定します。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: local
      options:
        max-size: 10m
        max-file: 3

動作確認をします。logsコマンドでログが取得できています。

ubuntu@test-nginx:~/nginx-log$ docker compose logs nginx 
nginx  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
nginx  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
nginx  | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx  | /docker-entrypoint.sh: Configuration complete; ready for start up

利用可能なオプションは以下の通りです。

オプション 説明 値の例
max-size ログの最大サイズ。正の整数に、測定単位を表す修飾子 ( km、またはg) を加えたもの。デフォルトは 20m です。 --log-opt max-size=10m
max-file ログのローテーション数で、超えると最も古いファイルから削除されます。正の整数。デフォルトは 5 です。 --log-opt max-file=3
compress ローテーションされたログ ファイルの圧縮を切り替えます。デフォルトで有効になっています。 --log-opt compress=false

JSON File logging driver

デフォルトの設定で、コンテナーの stdout/stderr をJSON形式のファイルに保存します。JSON形式は各行にその出所(stdoutまたはstderr)とタイムスタンプを注釈します。各ログファイルは1つのコンテナに関する情報のみを含みます。

例では、最大サイズを10MBに、ローテーションを3に指定します。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
  logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: 3

動作確認をします。logsコマンドでログが取得できています。

$ docker compose logs 
nginx  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
nginx  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
nginx  | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx  | /docker-entrypoint.sh: Configuration complete; ready for start up

ログの出力先を確認します。

$ docker inspect nginx |grep LogPath
        "LogPath": "/var/lib/docker/containers/264dd92738c22bc708d87cb19c2f859972af90ee4e1ae1ab6e60370d0e7f0720/264dd92738c22bc708d87cb19c2f859972af90ee4e1ae1ab6e60370d0e7f0720-json.log",

ログファイルを覗いてみます。確かに1行1行Jsonファイルとして保存されています。

$ sudo cat /var/lib/docker/containers/264dd92738c22bc708d87cb19c2f859972af90ee4e1ae1ab6e60370d0e7f0720/264dd92738c22bc708d87cb19c2f859972af90ee4e1ae1ab6e60370d0e7f0720-json.log
#省略
{"log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n","stream":"stdout","time":"2023-10-28T01:07:45.45693319Z"}
{"log":"/docker-entrypoint.sh: Configuration complete; ready for start up\n","stream":"stdout","time":"2023-10-28T01:07:45.46027774Z"}

Jsonファイルは{}のルート要素を1つのファイル1つとしているためこのファイルは正確にはJsonファイルとは言えません。ほかのアプリケーションでパースする際は注意してください。1行1行をJsonとして読み込みましょう。

1行のログは以下のようになっています。ログのほかにも、標準出力/エラー、時刻が記録されています。

{
    "log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n",
    "stream":"stdout",
    "time":"2023-10-28T01:07:45.45693319Z"
}

利用可能なオプションは以下の通りです。

オプション 説明 値の例
max-size ログの最大サイズ。正の整数に、修飾子 ( km、またはg) を加えたもの。デフォルトは -1 (無制限) です。 --log-opt max-size=10m
max-file ログのローテーション数で、超えると最も古いファイルから削除されます。デフォルトは 5 です。 --log-opt max-file=3
compress ローテーションされたログ ファイルの圧縮を切り替えます。デフォルトで有効になっています。 --log-opt compress=false
labels ログメッセージをタグで分類する際に使用します。詳細はドキュメントを参照してください。 --log-opt labels=production_status,geo
labels-regex --log-opt labels-regex=^(production_statusgeo)
env " --log-opt env=os,customer
env-regex " --log-opt env-regex=^(oscustomer)

Syslog logging driver

syslogドライバーはログをサーバーに転送します。オプションパラメータで、サーバアドレス、ファシリティを指定します。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: syslog
      options:
        syslog-address: udp://172.17.0.1:514
        syslog-facility: daemon
        tag: "{{.ID}}_{{.Name}}"
        syslog-format: rfc5424

サーバ側はrsyslogを用います。/etc/rsyslog.conf

# provides UDP syslog reception
module(load="imudp")
input(type="imudp" port="514")
$AllowedSender TCP, 127.0.0.1, 172.17.0.1/16

syslogを確認します。f60d2def811e_nginxはコンテナのIDと名前になっています。

$ sudo tail -f /var/log/syslog 
Oct 28 01:48:33 test-nginx f60d2def811e_nginx[9334] 192.168.144.32 - - [28/Oct/2023:01:48:33 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
Oct 28 01:48:35 test-nginx f60d2def811e_nginx[9334] 192.168.144.32 - - [28/Oct/2023:01:48:35 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"

コマンドでも確認してみます。以下の通り取得できています。

$ docker compose logs -f
nginx  | 192.168.144.32 - - [28/Oct/2023:01:48:33 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [28/Oct/2023:01:48:35 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"

利用可能なオプションは以下の通りです。

オプション 説明 値の例
syslog-address syslogサーバを指定、TCP/UDP/UNIX、TLS有無 --log-opt syslog-address=udp://192.168.1.3:514
--log-opt syslog-address=tcp+tls://192.168.1.3:514
--log-opt syslog-address=unix:///tmp/syslog.sock
syslog-facility syslogのファシリティを名称、または番号で指定 --log-opt syslog-facility=daemon
tag デフォルトはコンテナ IDの先頭12文字
使える特殊表現
--log-opt tag=test-app
--log-opt tag={{.Name}}
syslog-format 指定しない場合は、ローカルの Unix syslog 形式が使用されます。
rfc3164RFC-3164 互換形式
rfc5424RFC-5424 互換形式
rfc5424micro 形式
--log-opt syslog-format=rfc5424
syslog-tls-ca-cert CA によって署名された信頼証明書への絶対パス --log-opt syslog-tls-ca-cert=/ca.pem
syslog-tls-cert TLS 証明書ファイルへの絶対パス。 --log-opt syslog-tls-cert=/cert.pem
syslog-tls-key TLS キー ファイルへの絶対パス。 --log-opt syslog-tls-key=/key.pem
syslog-tls-skip-verify TLS 検証がスキップされます。 --log-opt syslog-tls-skip-verify=true

Journald logging driver

journaldに転送するdriverです。journaldは、Systemdシステム管理デーモンの一部として提供されるログ管理ツールです。システムとサービスのログ情報を効率的に収集、管理、クエリするために設計されています。

伝統的なテキストベースのログファイル(例:/var/log/syslog)とは異なり、バイナリフォーマットでログを保持し、メタデータを活用して高速に検索やフィルタリングが可能です。journaldはログのローテーションや保持期間の設定、自動圧縮もサポートしており、システムのログ管理を中心的に担当します。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: journald
      options:
        tag: "dev"

本ドライバーは以下のメタデータをログ本文と合わせてジャーナルに送信します。

フィールド 説明
CONTAINER_ID 先頭12文字のコンテナID
CONTAINER_ID_FULL 完全な64 文字のコンテナ ID
CONTAINER_NAME 起動時のコンテナ名
CONTAINER_TAG
SYSLOG_IDENTIFIER
コンテナタグ ( ログタグオプションのドキュメント)
ドライバーのオプションで指定したものが送信される
CONTAINER_PARTIAL_MESSAGE ログの整合性を示すフィールド。長いログ行のロギングを改善します。

ジャーナルから確認してみます。コンテナ名でも、オプションで指定したタグdevでも検索ができます。

$ sudo journalctl CONTAINER_NAME=nginx
Oct 28 02:02:53 test-nginx dev[9334]: 192.168.144.32 - - [28/Oct/2023:02:02:53 +0000] "GET / HTTP/>
Oct 28 02:02:53 test-nginx dev[9334]: 192.168.144.32 - - [28/Oct/2023:02:02:53 +0000] "GET / HTTP/>
Oct 28 02:02:53 test-nginx dev[9334]: 192.168.144.32 - - [28/Oct/2023:02:02:53 +0000] "GET / HTTP/>
Oct 28 02:02:54 test-nginx dev[9334]: 192.168.144.32 - - [28/Oct/2023:02:02:54 +0000] "GET / HTTP/>
Oct 28 02:02:54 test-nginx dev[9334]: 192.168.144.32 - - [28/Oct/2023:02:02:54 +0000] "GET / HTTP/>
$ sudo journalctl CONTAINER_TAG=dev
# 同様

journalctlはless互換のため、検索や移動が容易です。dockerからもログは確認できます。

$ docker compose logs
nginx  | 192.168.144.32 - - [28/Oct/2023:02:02:54 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"
nginx  | 192.168.144.32 - - [28/Oct/2023:02:02:54 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" "-"

Graylog Extended Format logging driver

本ドライバーはGELF形式での転送を行います。GEFLはGraylogやFluentdが有名です。

GELFのログメッセージはすべて次のフィールドを持つ辞書です。

  • バージョン
  • ホスト (最初にメッセージを送信した人)
  • タイムスタンプ
  • メッセージの短いバージョンと長いバージョン
  • 自分で設定したカスタムフィールド

ドライバーの指定方法

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: gelf
      options:
        gelf-address: "udp://172.17.0.1:12201"

Graylog構築

version: '3'
services:
  mongo:
    image: mongo:5
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
    environment:
      - http.host=0.0.0.0
      - transport.host=localhost
      - network.host=0.0.0.0
      - "ES_JAVA_OPTS=-Dlog4j2.formatMsgNoLookups=true -Xms512m -Xmx512m"
  graylog:
    image: graylog/graylog:5.1
    environment:
      - GRAYLOG_PASSWORD_SECRET=somepasswordpepper
      # Password: admin
      - GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
      - GRAYLOG_HTTP_EXTERNAL_URI=http://127.0.0.1:9000/
    entrypoint: /usr/bin/tini -- wait-for-it elasticsearch:9200 --  /docker-entrypoint.sh
    restart: always
    depends_on:
      - mongo
      - elasticsearch
    ports:
      - 9000:9000 # Graylog web interface and REST API
      - 1514:1514 # Syslog TCP
      - 1514:1514/udp # Syslog UDP
      - 12201:12201 # GELF TCP
      - 12201:12201/udp # GELF UDP

古いCPUではMongoDB 5.0系が動かないので注意です。

$ docker compose logs mongo  -f
graylog-mongo-1  | 
graylog-mongo-1  | WARNING: MongoDB 5.0+ requires a CPU with AVX support, and your current system does not appear to have that!
graylog-mongo-1  |   see https://jira.mongodb.org/browse/SERVER-54407
graylog-mongo-1  |   see also https://www.mongodb.com/community/forums/t/mongodb-5-0-cpu-intel-g4650-compatibility/116610/2
graylog-mongo-1  |   see also https://github.com/docker-library/mongo/issues/485#issuecomment-891991814
graylog-mongo-1  | 

起動後、Graylogダッシュボードにログインし、GELF inputを作成します。

image-20231028115355326.png

Nginxのアクセスログを確認します。

image-20231028115601330.png

コンテナのログとしては検索などが可能ですが、messageフィールドをNginxフォーマットで分析するにはGraylogでの追加設定が必要です。本記事では詳細は扱いません。

image-20231028115729157.png

Fluentd logging driver

Fluentdへログを転送するドライバーです。Fluentdはオープンソースのデータ収集ソフトウェアで、複数のデータソースからのログやイベントデータを効率的に収集、変換、送信することを目的としています。クラウド環境や大規模なシステムでのログ集約や分析のための中心的なツールとして広く利用されています。

  1. 多数のプラグインが利用可能で、データの収集、変換、出力先をカスタマイズ
  2. データの一時的な保存や再送をサポートし、データの紛失リスクを低減
  3. リソースの消費が少なく、大量のデータも効率的に処理
  • Input Plugins(Source)
    • ログの入力を定義します
    • tail: コマンドのtail -fと同様にファイルを監視し追記分を入力とします
    • forward: fluentd形式の入力を受け付けますDockerドライバは本形式で出力する
    • http: jsonのPOSTを受け付ける
    • syslog: syslogサーバとして機能する
  • Filter Plugins(filter)
    • 通過するログをフィルタリングします
    • grep: 正規表現などを用いてログをフィルタリングします
    • geoip: IPアドレスから地域を判別できます
  • Output Plugins(match)
    • フィルタリングやパースを行ったログを出力する
    • stdout: デバッグなどに用い、標準出力を利用します
    • forward: Fluentd形式で次のFluentd転送する
    • mongo: mongoDBに保存する
    • 他にもs3, elasticsearchなどがあります
  • その他
    • その他にもログ転送前に様々な処理を行うPluginが存在します。
    • Parser Plugins
    • Formatter Plugins
    • Buffer Plugins
    • Metrics Plugins

ドライバーの指定方法

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: fluentd
      options:
        fluentd-address: "172.17.0.1:24224"
        tag: "docker_{{.Name}}"

Fluent構築

本記事ではinputにドライバーの主力形式であるforward、処理は行わずoutputに標準出力を用います。

fluent.confを作成します。

<source>
  @type forward
</source>

<match docker_**>
  @type stdout
</match>

上記設定ファイルを組み込んだDockerイメージを作成するためのDockerfile

FROM fluent/fluentd:v1.16-1

COPY fluent.conf /fluentd/etc

compose.yamlを作成します。

version: '3'
services:
  fluentd:
    build: ./
    ports:
      - 24224:24224
      - 24224:24224/udp

Fluentdを起動します。起動後、NginxにアクセスするとFluentdを起動したターミナルの標準出力でログが確認できます。

$ docker compose up
[+] Running 2/2
 ✔ Network fluentd_default      Created                                                              0.1s 
 ✔ Container fluentd-fluentd-1  Created                                                              0.0s 
Attaching to fluentd-fluentd-1

~~ 省略 ~~

fluentd-fluentd-1  | 2023-10-28 03:43:30.000000000 +0000 docker_nginx: {"source":"stdout","log":"192.168.144.32 - - [28/Oct/2023:03:43:30 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36\" \"-\"","container_id":"3a8a296963b63a9b0f1d51dd3141749d0812662ace6188f027b9d708c0508c95","container_name":"/nginx"}
fluentd-fluentd-1  | 2023-10-28 03:43:30.000000000 +0000 docker_nginx: {"container_id":"3a8a296963b63a9b0f1d51dd3141749d0812662ace6188f027b9d708c0508c95","container_name":"/nginx","source":"stdout","log":"192.168.144.32 - - [28/Oct/2023:03:43:30 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36\" \"-\""}
fluentd-fluentd-1  | 2023-10-28 03:43:30.000000000 +0000 docker_nginx: {"container_id":"3a8a296963b63a9b0f1d51dd3141749d0812662ace6188f027b9d708c0508c95","container_name":"/nginx","source":"stdout","log":"192.168.144.32 - - [28/Oct/2023:03:43:30 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36\" \"-\""}

注意点としては、コンテナ起動時にFluentdが起動している必要があります。Fluentd側に問題があるとコンテナは起動できません。

$ docker compose up -d
[+] Running 2/2
 ✔ Network nginx-log_default  Created                                                     0.1s 
 ✔ Container nginx            Created                                                     0.2s 
Error response from daemon: failed to create task for container: failed to initialize logging driver: dial tcp 192.168.0.5:24224: connect: connection refused

Amazon CloudWatch Logs logging driver

CloudWatchへ転送するdriverです。本章では親和性を考慮してEC2サーバ上のコンテナからの転送を想定します。Amazon CloudWatchは、AWSリソースおよびアプリケーションをモニタリングするサービスです。

  1. リアルタイムモニタリング: AWSリソースのメトリクスをリアルタイムで収集し、表示
  2. アラーム: 予め設定したしきい値を超えると、通知や自動アクションをトリガーするアラーム
  3. ダッシュボード: 複数のメトリクスを一つのビューで見るためのカスタマイズ可能なダッシュボード
  4. ログファイルの統合: CloudWatch Logsを使用して、アプリケーションのログファイルをモニタリングし、保存、分析
  5. カスタムメトリクス: 独自のメトリクスをCloudWatchに送信し、モニタリング
  6. イベント管理: CloudWatch Eventsを使用して、AWSリソースの変更や状態遷移を検出し、応答
  7. トレース: CloudWatch ServiceLensとX-Rayの統合を通じて、アプリケーションのパフォーマンスボトルネックや問題点を追跡・分析する

ドライバーの指定方法

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: awslogs
      options:
        awslogs-region: ap-northeast-1
        awslogs-group: my-log-group
        awslogs-create-group: "true"

AWS CloudWatchでの設定

IAMからポリシーを作成し、本ポリシーをロール経由でEC2にアタッチします。

CloudWatchへのログ送信のみを許可した最低限のポリシーです。

image-20231028130956175.png

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": ["logs:CreateLogStream", "logs:PutLogEvents", "logs:CreateLogGroup"],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

EC2にログインし、Nginxを起動します。セキュリティグループには30080を適切に追加します。

[ec2-user@ip-XX nginx-log]$ sudo docker-compose up -d
[+] Running 1/1
 ⠿ Container nginx  Started   

起動前には、ドライバーがロググループを作成し、認証情報の確認などを行っています。

CloudWatchを確認すると、指定した名称でロググループが作成されています。

image-20231028140918832.png

ライブテールからログを確認します。

image-20231028141120209.png

Splunk logging driver

本ドライバーは SplunkへHTTP イベント コレクターとして送信します。Splunk Enterprise と Splunk Cloud に利用可能です。Splunk Cloudはあらゆるニーズに対応する柔軟なデータプラットフォームサービスです。※2

  • 機械学習とAI
    • 対応だけでなく予測と防止を視野に入れ、マシンレベルのインテリジェンスをデータに統合して、セキュリティとビジネス成果を向上
  • データストリーミング
    • リアルタイムのストリーム処理によってデータをミリ秒単位で収集、処理し、Splunkやその他の送信先に配信
  • 拡張可能なインデックス
    • SaaSをはじめとする数千に及ぶデータソースからテラバイト規模のデータを収集、取り込み
  • コラボレーションツール
    • モバイル、TV、AR (拡張現実)機能によって、場所に縛られないコミュニケーションとコラボレーションを実現
  • 統合サーチ
    • オンプレミスやサードパーティのデータストアを含む多様なデータエコシステムを横断的にサーチしてデータを相関付け、組織レベルで分析
  • 強力なダッシュボード
    • 直感的なダッシュボード画面で、複雑なデータストーリーでも簡単に状況を把握

ドライバーの指定方法

https://prd-p-XXXXXXX.splunkcloud.com:8080はインスタンス払い出し時に届くメールアドレスに記載があります。そこにポート番号を付け加えます。今回は検証のため、Splunk Cloudサーバの証明書確認を無効としています。

version: "3"
services:
  nginx:
    image: nginx:latest
    # 省略
    logging:
      driver: splunk
      options:
        splunk-token: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
        splunk-url: https://prd-p-XXXXXXX.splunkcloud.com:8088
        splunk-insecureskipverify: "true"
        tag: "{{.Name}}/{{.FullID}}"
        labels: location

Splunk Cloudでの設定

Splunk Cloudは15日の無料トライアルが可能です。制限はありますが、メールアドレスのみで作成可能です。

image-20231028144556043.png

データの追加を行うため、HTTP イベント コレクターを作成します。作成後、テキストトークンが表示されるので保存します。ログ送信時に必要になります。

image-20231028145852092.png

設定値が正しい場合、Nginxコンテナが起動できます。Nginxにアクセス後、Splunk Cloudから検索をかけます。ドライバーがログを送信する場合、オリジナルのNginxのログはlineに表示されるため上手くパースできていませんが、本章では以上となります。

image-20231028151825687.png

ETW logging driver

Event Tracing for Windows (ETW) ログ ドライバーは、コンテナー ログを ETW イベントとして転送します。ETW は Windows の Event Tracing の略で、Windows でアプリケーションをトレースするための共通のフレームワークです。各 ETW イベントには、ログとそのコンテキスト情報の両方を含むメッセージが含まれています。その後、クライアントはこれらのイベントをリッスンするための ETW リスナーを作成できます。

ETWはWindows PowerShellのlogmanコマンドを使用して設定を行います。以下のコマンドでイベントのリスナーを起動します。

logman start -ets DockerContainerLogs -p {a3693192-9ed6-46d2-a981-f8226c8363bd} 0 0 -o trace

公式ドキュメントよりETWとして転送されたログのサンプルを示します。イメージ名からも分かる通りWinsowsコンテナのため本記事での紹介は以上とさせていただきます。

container_name: backstabbing_spence,
image_name: windowsservercore,
container_id: f14bb55aa862d7596b03a33251c1be7dbbec8056bbdead1da8ec5ecebbe29731,
image_id: sha256:2f9e19bd998d3565b4f345ac9aaf6e3fc555406239a4fb1b1ba879673713824b,
source: stdout,
log: Hello world!

gcplogs

Google Cloud Loggingへログを転送します。クラウドベンダーが提供するログ基盤系のドライバーです。

まず、ログ転送用のサービスアカウントを作成します。Cloud Loggingサービスエージェントのロールを付与してください。プロジェクト単位より詳細にアクセス権限をコントロールしたい場合は新たなロールを作成してください。

image.png

作成したサービスアカウントのキーを生成してJSON形式でダウンロードします。

image.png

ダウンロードしたキーをログを転送する環境に設置します。

vim /etc/docker/gcp_credentials.json

Dockerログドライバーにcredentialの場所を渡します。

sudo vi /lib/systemd/system/docker.service

~~~ 省略 ~~~
[Service]
Environment=GOOGLE_APPLICATION_CREDENTIALS=/etc/docker/gcp_credentials.json
~~~ 省略 ~~~

確認用にNginxを起動します。

docker run \
    -p 0.0.0.0:8080:80 \
    --log-driver=gcplogs \
    --log-opt gcp-project=プロジェクトID \
    --log-opt gcp-meta-zone=west1 \
    --log-opt gcp-meta-name=`hostname` \
    nginx:latest

GCPログエクスプローラーで以下のクエリを実行します。

logName="projects/プロジェクトID/logs/gcplogs-docker-driver"

Nginxのログが確認できます。

image.png

おわりに

検証したすべてのドライバーをログの保存先で分類し、使用感をまとめました。

ドライバ 分類
none ローカル系 コンテナに対するログを記録しない。ログをボリュームに書き出し、fluentbitで転送する場合などに有効
local ローカル系 独自のファイル方式で保存され、ドライバの中で一番オーバーヘッドが少ない※コンテナ再作成で消える
json-file ローカル系 JSON 形式で保存され、デフォルトのドライバー※コンテナ再作成で消える
journald ローカル系 検索しやすく、単一ホストでの運用では有力※デフォルトの設定ではホスト再起動で消える
etwlogs ローカル系 Windowsサーバなどを運用している場合は有力だが、Windowsコンテナを利用する機会が少なく、判断が難しい
syslog ログ基盤系 既存のログ基盤と相性が良く、ファイルに保存する場合は一番制御しやすいと感じた
gelf ログ基盤系 Graylogを採用している場合は最有力
fluentd ログ基盤系 複数のログ基盤があり、基本はsyslog、アクセスログなどはsplunkに投げる、等使い分けるときは一元管理できる
splunk ログ基盤系
クラウド系
Splunkを採用しているプロジェクトでは一択
awslogs クラウド系 AWSを主に採用している場合は有力
gcplogs クラウド系 GCPを主に採用している場合は有力、Log explorerが使いやすい

上記から、ドライバーの選定には以下のケース別に判断すると良いと考えました。

  • 単一ホストの場合
    • journaldに格納し、永続化の設定を行う
  • 複数ホストの場合
    • syslogドライバーを使用し、ファイルに保存を行う
    • 法令、コンプライアンスでログの保存期間が決まっている場合でも対応が可能
  • クラウド基盤を利用している場合
    • 使用しているクラウドに対応するドライバーを使用する

参考

  1. https://docs.docker.com/config/containers/logging/local/
  2. https://www.splunk.com/ja_jp/products/splunk-cloud-platform.html
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?