2026年7月3日

2026年7月3日

Dockerのコンテナログをfluentdで収集する方法|fluentdロギングドライバーの設定と注意点

Dockerのコンテナログをfluentdで集めるには、まずfluentdをコンテナとして起動して24224番ポートでログを受け付けるようにし、ログを送りたいコンテナのロギングドライバーを fluentd に切り替えます。ドライバーがコンテナの標準出力・標準エラーをfluentdに転送し、fluentd側でファイルへの保存や検索基盤への送信を行う、という分担です。この記事では、受け取り側のfluentdの準備、Composeでの送信側の設定、タグの付け方、届かないときの確認順、fluentdが止まったときにコンテナまで止まらないようにする設定を説明します。

前提はLinux上のDocker EngineとDocker Compose v2、fluentdは公式のv1系のDockerイメージです(2026年10月現在)。ホストに直接入れる場合のパッケージは、以前の td-agent がサポートを終え、後継の fluent-package に移っています。

仕組みを先に押さえる

Dockerの既定のロギングドライバー(json-file)は、コンテナの出力をホストのファイルに書きます。これを fluentd ドライバーに変えると、Dockerデーモンがログを1行ずつfluentdのforwardプロトコルで送ります。アプリ側の変更は不要で、標準出力に出ているログならそのまま集められます。

1行ごとのログは、次のような形のレコードとしてfluentdに届きます。

{
  "container_id": "3f2a...(64文字のID)",
  "container_name": "/web",
  "source": "stdout",
  "log": "172.18.0.1 - - [18/Oct/2026:10:00:00 +0000] \"GET / HTTP/1.1\" 200 615"
}

source で標準出力か標準エラーかを区別でき、log にアプリが出力した1行が入ります。fluentdの「タグ」はレコードの振り分けに使う名前で、既定ではコンテナIDの先頭12文字になります。そのままでは扱いにくいので、後述のようにコンテナ名を使ったタグに変えるのが一般的です。

もう1つ大事なのが、ログを送るのはコンテナではなくDockerデーモン(ホスト側)だという点です。送信先の fluentd-address はホストから見たアドレスで解決されるため、Composeのサービス名(fluentd など)を書いても届きません。fluentdのポートをホストに公開し、localhost:24224 を指定します。

受け取り側のfluentdを用意する

設定ファイル fluentd/fluent.conf を作ります。まずは受け取ったログを画面(fluentd自身の標準出力)とファイルの両方に出す構成にして、届いているかを確認しやすくします。

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match docker.**>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type file
    path /fluentd/log/docker
    <buffer time>
      timekey 1h
      timekey_wait 5m
    </buffer>
  </store>
</match>
  • <source> の @type forward が、Dockerのドライバーからの送信を受け付ける入口です。
  • <match docker.**> は、docker. で始まるタグのレコードを対象にします。送信側のタグをこれに合わせます。
  • @type file は、時間ごとにまとめてファイルに書き出します。timekey 1h は1時間単位でまとめる指定で、書き出されるのはその1時間が終わり、さらに timekey_wait の5分が過ぎたあとです。設定直後にファイルが見当たらないのは異常ではなく、その間はバッファに溜まっています。動作確認はまず標準出力のほうで行うのがおすすめです。

次に、Composeでfluentdと、ログを送るWebサーバーを定義します。

services:
  fluentd:
    # タグは例。実際に使える版はDocker Hubの fluent/fluentd で確認する
    image: fluent/fluentd:v1.19-debian-1
    volumes:
      - ./fluentd/fluent.conf:/fluentd/etc/fluent.conf:ro
      - fluentd-log:/fluentd/log
    ports:
      - "127.0.0.1:24224:24224"
    logging:
      driver: local
    restart: unless-stopped

  web:
    image: nginx:stable
    ports:
      - "127.0.0.1:8080:80"
    depends_on:
      - fluentd
    logging:
      driver: fluentd
      options:
        fluentd-address: "localhost:24224"
        fluentd-async: "true"
        tag: "docker.{{.Name}}"

volumes:
  fluentd-log:

公式イメージは /fluentd/etc/fluent.conf を設定ファイルとして読み込みます。ポートを 127.0.0.1:24224 で公開しているのは、forwardの入口は初期状態では認証がなく、外部に開くと誰でもログを送り込めてしまうからです。fluentd自身のログには local ドライバーを指定し、自分自身に送ろうとしてループしたり、起動できなくなったりするのを防いでいます。

起動して届いているか確かめる

docker compose up -d
curl -s http://localhost:8080/ > /dev/null
docker compose logs fluentd

fluentdのログに docker.myapp-web-1 のようなタグ付きでNginxのアクセスログの行が表示されれば、収集できています。Composeで作られるコンテナ名は「プロジェクト名-サービス名-番号」の形式で、プロジェクト名は既定ではディレクトリ名です(ここでは myapp とします)。

Compose以外のコンテナを単発で試すなら、次のようにします。

docker run --rm --log-driver=fluentd \
  --log-opt fluentd-address=localhost:24224 \
  --log-opt tag="docker.test" \
  alpine echo "hello fluentd"

タグの付け方

tag には、次のようなテンプレートが使えます。

  • {{.Name}}:コンテナ名
  • {{.ImageName}}:イメージ名
  • {{.ID}}:コンテナIDの先頭12文字
  • {{.FullID}}:コンテナIDの全体

docker.{{.Name}} のように共通の接頭辞を付けておくと、fluentd側の <match docker.**> でまとめて受けたうえで、<match docker.myapp-web-*> のように個別に振り分けることもできます。fluentdは上から順に最初に一致した <match> だけを使うので、個別の指定は docker.** より前に書きます。タグはfluentdの設定と対になるものなので、命名の規則を先に決めておくと設定が散らかりません。

届かない・コンテナが起動しないときの確認順

  1. 送信側のコンテナが起動しない:fluentdに接続できないと、既定ではコンテナの起動自体が失敗します。docker compose ps -a でコンテナが作成されたまま止まっていないか、docker compose up の出力に接続エラーが出ていないかを見ます。fluentd-async: "true" を指定すると、接続をバックグラウンドで再試行しながら起動するため、fluentdの停止や再起動にアプリが巻き込まれにくくなります。そのかわり、fluentdに届くまでの間のログはDockerのメモリ上のバッファに溜まり、上限を超えた分は失われ得る点は理解しておきます。
  2. アドレスの指定:fluentd-address にComposeのサービス名を書いていないか、ポートの公開と番号が一致しているかを確認します。
  3. fluentd側で受け取っているか:docker compose logs fluentd に設定の読み込みエラー(config error など)が出ていないかを見ます。設定ファイルを直したら docker compose restart fluentd で反映します。
  4. タグとmatchの不一致:送信側の tag が docker. で始まっていないと、<match docker.**> に一致せず、どこにも出力されません。一致しないレコードについての警告がfluentdのログに出ます。
  5. ファイルが空・できない:前述の timekey の待ち時間か、/fluentd/log の書き込み権限を疑います。公式イメージはroot以外の fluent ユーザーで動くため、ホストのディレクトリをバインドマウントした場合はそのユーザーが書き込めるようにする必要があります。

なお、Docker Engine 20.10以降は「デュアルロギング」という仕組みにより、fluentd ドライバーを使っているコンテナでも docker logs でログを見られます(Dockerがローカルにも一時的なコピーを残すためです)。手元での確認は docker logs、集約と保管はfluentd、という使い分けができます。

実運用で考えておきたいこと

  • 全コンテナを対象にする場合:/etc/docker/daemon.json の log-driver と log-opts で既定値にできますが、fluentdのコンテナ自身も対象になってしまいます。fluentdには上の例のように別のドライバーを個別に指定し、fluentd-async も既定値に含めておくと、fluentdが止まったときに全コンテナが起動できなくなる事態を避けられます。daemon.json の変更後はDockerの再起動が必要で、変更は新しく作ったコンテナから反映されます。
  • 送信先を増やす:OpenSearchやElasticsearch、オブジェクトストレージなどに送るには、fluentdのプラグインを入れた独自イメージを作ります。公式イメージをもとに、USER root に切り替えてプラグインをインストールし、USER fluent に戻すのが基本の形です。プラグインごとに対応するfluentdや送信先の版が異なるので、各プラグインの説明を確認してください。
  • 複数行のログ:Javaのスタックトレースのように複数行にわたるログは、1行ずつ別のレコードになります。まとめたい場合は、fluent-plugin-concatのような連結用のプラグインを使います。
  • JSONで出力するアプリ:log の中身がJSONなら、fluentdの parser フィルターでフィールドに展開できます。JSONでない行が混ざったときの扱いも設定で決めておきます。
  • 別のサーバーに送る場合:forwardの通信は既定では暗号化されません。ホストをまたぐ場合はTLSと共有キーによる認証を設定し、ファイアウォールで送信元を絞ります。
  • より軽量な選択肢:各サーバーで集める役割には、同じforwardプロトコルを話せるFluent Bitを使い、集約サーバーだけfluentdにする構成もよく使われます。

まとめ

  • 送信側のコンテナは logging.driver: fluentd にし、fluentd-address・tag・fluentd-async を指定します。
  • ログはDockerデーモンが送るため、アドレスはホストから見た localhost:24224 を使い、ポートは 127.0.0.1 に限定して公開します。
  • タグは docker.{{.Name}} のように接頭辞を付け、fluentd側の <match> と対にします。
  • fileへの出力は timekey の区切りでまとめて書かれるので、動作確認は標準出力で行います。
  • fluentd自身は別のドライバーで動かし、fluentd-async でfluentdの停止がアプリの起動に影響しないようにします。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲