Docker Log Driver & Fluent-bit:マイクロサービスにおけるログ管理の「救世主」

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

ログ管理を負担にしないために

サーバーにSSHでログインし、システムに不具合が生じるたびに20〜30個ものマイクロサービスに対して docker logs -f コマンドを打ち続けるのは、まさに苦行です。VPS上で1〜2個のコンテナを動かす程度なら問題ありませんが、規模が大きくなると、膨大なログの中からエラーを探し出すのは「雲を掴むような話」になってしまいます。

デフォルトでは、Dockerは json-file ログドライバーを使用します。この仕組みは、すべての stdout/stderr をホストマシンのディスク上のJSONファイルに書き込みます。以前、log-rotate の設定を忘れたために、わずか4時間でログファイルが50GBまで膨れ上がり、サーバーがダウンした現場を目の当たりにしたことがあります。

以前のEコマースプロジェクトでは、メモリリークの原因を突き止めるだけで2日間を費やしました。コンテナが頻繁にクラッシュし、Dockerが自動再起動を繰り返したため、古いログがすべて消えてしまったのです。その苦い経験から、プロフェッショナルな環境では、Fluent-bitによるログ集約が不可欠であると痛感しました。

Fluent-bit:軽量級ロギングツールの決定版

ロギングといえば、多くのエンジニアがまず ELK Stack を思い浮かべるでしょう。しかし、LogstashはJVM上で動作するため、リソースを非常に多く消費します。起動するだけで少なくとも500MBのRAMを「食い」ます。中規模なサーバーにとって、ログ収集ボットのためだけに1GBのRAMを割くのはあまりにも贅沢すぎます。

Fluent-bitは全く別物です。C言語で書かれており、RAM消費量はわずか15〜20MB程度ですが、毎秒数千ものログイベントを処理する能力を持っています。このアーキテクチャでは、Docker Fluentd Log DriverがログをFluent-bitに送り、そこからFluent-bitがElasticsearchやLoki、あるいは単純な中央集約ファイルへとデータを振り分けます。

ログ集約システムを構築する3つのステップ

ステップ1:Fluent-bitの設定ファイルを作成する

まず、fluent-bit.conf ファイルを作成します。このファイルは、ログの受信元と送信先を指定する「司令塔」の役割を果たします:

[SERVICE]
    Flush        1
    Daemon       Off
    Log_Level    info
    Parsers_File parsers.conf

[INPUT]
    Name   forward
    Listen 0.0.0.0
    Port   24224

[OUTPUT]
    Name   stdout
    Match  *

forward インプットを使用してポート24224で待機させます。出力(Output)は一旦 stdout に設定します。これにより、Fluent-bitのコンソール上でログを即座に確認でき、デバッグが容易になります。

ステップ2:Docker ComposeでFluent-bitをデプロイする

Docker Composeを使用すれば、この「ログ掃除機」の管理が非常に楽になります。以下のように docker-compose.yml を作成します:

services:
  fluent-bit:
    image: fluent/fluent-bit:latest
    container_name: fluent-bit
    volumes:
      - ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
    ports:
      - "24224:24224"
      - "24224:24224/udp"
    restart: always

docker-compose up -d と入力するだけで、ログ収集システムが稼働します。

ステップ3:ログを送信するようにアプリケーションを設定する

Dockerが自動的にJSONファイルへログを書き込む代わりに、データストリームをFluent-bitに向けるようにします。これには2つの方法があります:

方法1:Runコマンドを直接使用する

docker run --log-driver=fluentd \
           --log-opt fluentd-address=localhost:24224 \
           --log-opt tag="web-app-production" \
           nginx

方法2:Docker Composeで設定する(推奨)

services:
  web-app:
    image: nginx:latest
    logging:
      driver: fluentd
      options:
        fluentd-address: "localhost:24224"
        tag: "nginx.logs"
        fluentd-async: "true"

信じてください、fluentd-async: "true" オプションはまさに救世主です。もしFluent-bitがダウンした際に非同期設定(async)が有効でないと、アプリケーションのコンテナがブロッキングI/Oによりフリーズする可能性があります。非同期モードにすることで、ログシステムに問題が発生してもアプリケーションはスムーズに動き続けます。

実機での確認と運用

起動後、アプリケーションにアクセスしてログを生成させてみてください。以下のコマンドで結果を確認します:

docker logs -f fluent-bit

Nginxのログが nginx.logs というタグ付きのJSON形式で表示されれば成功です。さらに本格的に運用する場合は、[OUTPUT] セクションを変更してGrafana Lokiにログを送信するようにしましょう:

[OUTPUT]
    Name        loki
    Match       *
    Host        loki-server-address
    Port        3100
    Labels      job=docker_logs

運用における「血肉となる」教訓

ロギングシステムが負担にならないよう、以下の3つの重要なポイントに注意してください:

  • バッファ設定: Elasticsearchのレスポンスが遅い場合、Fluent-bitはログをRAMに蓄積します。RAMがいっぱいになるとログが破棄(ドロップ)されてしまいます。必要に応じてログをディスクにバッファリングできるよう、storage.type filesystem を設定しましょう。
  • スマートなタグ付け: 曖昧なタグを付けないようにしましょう。{{.Name}} などの変数を使用して、同じサービスの異なるインスタンス間でログを区別できるようにします。
  • セキュリティ: デフォルトのポート24224にはパスワードがありません。悪意のある第三者がログをスパム送信してディスクをいっぱいにすることを防ぐため、VPSでこのポートをパブリックに公開しないでください

Fluent-bitの初期設定には15〜20分ほどかかるかもしれません。しかし、システムが安定稼働することによる安心感は、その手間に十分見合う価値があります。

Share: