ファイル、HTTP、コンテナからログを収集する
最も一般的な 3 つの入力ソースを設定します。ディスク上のファイル、HTTP エンドポイント、Docker ホスト上のすべてのコンテナです。
実際のデプロイの多くは、複数の種類の入力ソースを同時に必要とします。ログファイルに書き込む既存のアプリケーション、代わりに HTTP でログを送れるサービス、ホスト上で動作するすべてのコンテナなどです。このガイドでは、前のガイドの source/filter/match の構造を土台に、この 3 つをすべて設定します。
tail でログファイルを追跡する
tail 入力プラグインは、tail -f と同じように、ファイルを追跡して新しい行が書き込まれるたびに取得します。
<source>
@type tail
path /var/log/apache2/access.log
pos_file /var/log/fluent/apache-access.log.pos
tag apache.access
<parse>
@type apache2
</parse>
</source>
pos_file は省略しやすいですが、真剣に考える価値があります。Fluentd がファイルのどこまで読み取り済みかを正確に記録する場所だからです。これがないと、Fluentd を再起動したときにファイル全体を最初から読み直し、すでに送信済みのものがすべて重複します。これがあれば、再起動しても中断したところから正確に再開します。プラグイン自身のドキュメントでも、まさにこの理由で強く推奨されています。
HTTP でログを受け付ける
すべてのものに Fluentd をインストールできるわけではありません。マネージドなクラウドサービス、サーバーレス関数など、OS レベルで制御できないインフラ上のものは、自分が書き込んでいるファイルの隣で tail ソースを動かせません。そのような場合、http 入力プラグインを使うと、Fluentd がそれから読み取る代わりに、それが Fluentdへログを送れます。
<source>
@type http
port 9880
bind 0.0.0.0
body_size_limit 32m
keepalive_timeout 10s
</source>
HTTP POST リクエストを送れるものなら何でも、たとえば独自のアプリケーション、そこへ転送するよう設定されたロギングライブラリ、スクリプト内の curl 呼び出しが、ログデータを直接届けられるようになります。body_size_limit と keepalive_timeout は、お使いのバージョンのデフォルトのままにせず、意図的に設定する価値があります。HTTP ソースは、到達するあらゆるトラフィックにさらされるため、無制限のリクエストサイズや開いたままの接続は、デフォルトの動作に任せるのではなく、自分で制御したいものだからです。
ホスト上のすべてのコンテナのログを収集する
Docker(およびコンテナランタイム全般)は、各コンテナの stdout/stderr を、ホスト上の JSON 形式のログファイルに、コンテナごとに 1 つのディレクトリで書き込みます。ワイルドカードを使ってこれらすべてに tail ソースを向ければ、個別に設定しなくても、すべてのコンテナの出力を収集できます。
<source>
@type tail
path /fluentd/log/containers/*/*-json.log
pos_file /var/log/fluent/docker.log.pos
tag docker.*
read_from_head true
<parse>
@type json
</parse>
</source>
これが機能するには、ホストの Docker ログディレクトリ(デフォルトでは /var/lib/docker/containers)が、Fluentd が動作している場所に、ソースの path パターンが指すパスで、実際にマウントされている必要があります。これはまさに、Fluentd が Kubernetes の DaemonSet として動作する仕組みでもあります。ノードごとに 1 つの Fluentd Pod があり、それぞれが自分のノードのコンテナログディレクトリをマウントするため、クラスター全体で、コンテナごとの設定なしに、すべてのノードのコンテナが同じ方法で収集されます。
read_from_head true は、まだ見ていないファイルを先頭から開始するよう Fluentd に指示します。これは、このソースを設定する前からすでに稼働し、ログを出力しているコンテナにとって重要です。これがないと、Fluentd は、特定のファイルの監視を開始した時点以降の新しい行しか取得しません。
コンテキストで補強する:どのコンテナ、どのホストか
生のコンテナのログ行は、それ自体では、どの Pod、ノード、コンテナから来たのかを教えてくれません。そのコンテキストは追加する必要があり、通常は record_transformer プラグインを使った filter で行います。
<filter docker.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
</record>
</filter>
これにより、docker. の下にタグ付けされたすべてのエントリに、現在のホスト名が付加されます。これにより、後から、ロードバランサーの背後やクラスター全体にある複数のレプリカのうち、どれから特定のログ行が来たのかを判別できます。同じパターンは、Pod 名、namespace、その他、後から再構築しようとするのではなく収集時に付加する価値のあるメタデータにも拡張できます。
各ソースを個別に確認する
実際の出力先を接続する前に、各ソースを単純な @type stdout の match に向けて、期待する構造が生成されていることを確認します。
<match apache.access>
@type stdout
</match>
リクエストを生成し(またはファイルに行を書き込む、HTTP エンドポイントに POST する、コンテナを起動する)、パースされてタグ付けされた出力が現れるのを確認します。フィルターや実際の出力先を重ねる前に、各ソースを単独で確認しておくと、パイプラインの後段で何かおかしく見えたときに、どの段階で問題が発生したのかを特定しやすくなります。