柔軟なログ収集を可能にする「fluentd」入門

設定例:Apacheのアクセスログを記録する

fluentdの基本的な設定例として、まずはApacheのアクセスログを処理する例を紹介しよう。今回は入力プラグインとして「in_tail」を使用し、Apacheがデフォルトで出力するログはそのまま残したうえでfluentdでログを記録することにする。

in_tailプラグインは、指定したテキストファイルを監視し、ファイルに新たな行が追加されたらその内容をログとして記録するという動作をする。なお、テキスト形式でログを記録する場合、logrotateというツールで一定期間ごとにログを別ファイルに切り替えたり、ログファイルを圧縮する、といった作業を行うことが多いが、in_tailプラグインではlogrotateを考慮したファイル監視を行うようになっているため、logrotateを利用している場合でも特に追加の設定などを行う必要はない。

今回は、以下のような条件でApacheのログが記録されているとして設定を行っている。これ以外の環境の場合、適宜設定ファイルやログの形式を確認してほしい。

  • Apacheのログファイルは/var/log/httpd/access_log
  • ログの形式は、Combined Log Format(Apacheデフォルトの「combined」設定)

イベント収集に関する設定

まず必要なのは、イベントの入力ソースの指定だ。今回は以下のように設定した。

# get apache log
<source>
  type tail  ←in_tailプラグインを指定
  path /var/log/httpd/access_log  ←アクセスログのパスを指定
  tag apache.access  ←ログに付けるタグを指定
  pos_file /var/log/td-agent/httpd-access_log.pos  ←ファイル内のどの行までを読んだかを記録しておくファイルを指定
  format apache2  ←パースするためにログの書式を指定
</source>

「pos_file」という設定項目は必須ではないものの強く推奨される設定項目で、指定した監視対象ファイルのどの行までを読んだのかを記録しておくのに使用するファイルを指定するものだ。今回は「/var/log/td-agent/httpd-access_log.pos」というファイルを利用するよう指定している。

また、in_tailプラグインでは指定したログファイルに書き込まれた内容をパースしてJSON形式のレコードに変換する仕組みがあり、「format」設定項目でパースに使用する正規表現を指定できる。主要なフォーマットについてはあらかじめ内部的にパース用の正規表現が組み込まれており、特定のキーワードを利用することでそれを利用できる。たとえば今回はApacheのconbinedフォーマットのログをパースするが、この場合「apache2」というキーワードが利用できる。このキーワードは、以下のような正規表現を指定したものと同様だ。

/^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/

これにより、記録されるレコードの「host」要素にはリモートホストが、「user」にはユーザー名が、「method」には使用されたメソッドが……といったように、ログが解析しやすい形で記録される。この場合、取得されたログのレコードの例は以下のようになる。

{
  "host": "**.***.***.***",
  "user": null,
  "method": "GET",
  "path": "/path/to/resource/",
  "code": 200,
  "size": 143320,
  "referer": "http://example.com/path/to/file",
  "agent":"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; Touch; MDDCJS)"}
}

ログ出力の設定

続いて、ログ出力の設定を行っていく。まずはテストとして、ファイルへの出力を行う「out_file」プラグインを使用してみよう。

<match apache.access>  ←対象とするタグを指定
  type file  ←out_fileプラグインを指定
  path /var/log/td-agent/httpd/access.log  ←出力先ファイルを指定
  time_slice_format %Y%m%d  ←ファイル名に含める日時情報を指定
  time_slice_wait 10m  ←ログファイルの更新後に旧ログファイルへのログ記録を継続する時間を指定
  compress gzip  ←ログをgzip形式で圧縮
</match>

out_fileプラグインは単純にマッチしたログをファイルに出力するだけでなく、日時ごとにログファイルを自動的に分割する機能が用意されており、そのファイル名は「time_slice_format」設定項目で、POSIXのstrftime関数で使われるのと同様のフォーマットで指定できる。今回の例では「%Y%m%d」を指定しているが、この場合はログの記録された日にちごとにファイル名に「.<年><月><日>」という文字列が追加されるようになる。

なお、fluentdではイベントが発生した時刻と、実際にfluentdにそのログが通知される時刻の間に差が発生することがある。これはfluentdの仕様上避けられないのだが、これによってログファイル名の日時と、実際に記録されるログが異なるという状況が発生しうる。たとえば前日の23:55に発生したイベントが0時を過ぎてからfluentdに通知される、ということがあるのだ。この場合、日にちごとにログを分割するように設定していると、遅延して届いたイベントがログに記録されないという可能性がある。そのため、out_fileプラグインではログの分割時、分割する時刻になってから一定時間待機した後にログの書き出しを行うという仕様になっている。どれだけの時間待機するかは「time_slice_wait」設定項目で指定できる。今回の例の場合は「10m」、つまり10分が指定されている。

また、ログファイルは日にちだけでなく、一定サイズを超過した場合でも自動的に分割される。デフォルトでは256MBごとの分割となっているが、このサイズはbuffer_chunk_limitオプションで変更可能だ。

以上の設定を記述後、以下のように設定ファイルのリロードを実行することでイベントの収集とログの記録が開始される。

# /etc/init.d/td-agent reload
もしくは
# service td-agent reload

複数のマシンのログを集積する

fluentdでは、取得したログを別のマシンで稼動しているfluentdに送信することが簡単に行える。これを利用して、複数のサーバーのログを1つのサーバーにまとめることが可能だ。

ログを別のマシンに送信するには、「out_forward」プラグインを使用する。たとえば、先に例示した設定で取得したapacheのアクセスログを、192.168.100.1というIPアドレスを持つマシン上の24224ポートで待ち受けしているfluentdに送信するには、以下のような設定を追加すれば良い。

<match apache.access>  ←対象とするタグを指定
  type forward  ←out_forwardプラグインを指定
  <server>  ←送信先サーバーを指定
    host 192.168.100.1  ←送信先マシンのIPアドレスを指定
    port 24224  ←送信先fluentdが待ち受けているポートを指定
  </server>
</match>

また、ログを受け取る側のマシンで稼動しているfluentdでは、次のように「in_forward」プラグインを利用するよう設定しておく必要がある。

<source>
  type forward  ←in_forwardプラグインを指定
  port 24224  ←待ち受けに使用するポートを指定
</source>

ログをMongoDBに記録する

さて、先の例ではログをテキストファイルに記録していたが、この場合ログの解析時に再度ログをパースして読み込むといった作業が発生してしまう。fluendではログをデータベースに格納するためのプラグインが用意されており、これを利用することでログの取得時に自動的に項目をパースしたうえでデータベースにその情報を格納できる。続いてはその1つである、MongoDBにログを格納する「out_mongo」プラグインを使う例を紹介する。といっても実際の設定方法は簡単で、以下のような設定項目をfluendの設定ファイルに追加するだけだ。

<match apache.access>  ←対象とするタグを指定
  type mongo  ←out_mongoプラグインを指定
  host localhost  ←MongoDBが稼動しているホストを指定
  port 27017  ←MongoDBが待ち受けしているポートを指定
  database fluentd  ←データを格納するデータベースを指定
  collection apache_access  ←データを格納するコレクションを指定
  capped  ←パフォーマンス向上のためMongoDBのCapped Collection機能を利用する
  capped_size 1024m  ←Collectionの上限サイズを1Gに設定
  # flush
  flush_interval 10s  ←10秒おきにログをMongoDBにflushする
</match>

なお、ファイルへのログ保存とMongoDBへのログ保存を同時に行いたい場合は、次のように「out_copy」プラグインを利用する必要がある。

<match apache.access>
  type copy  ←out_copyプラグインを指定

  <store>  ←1つめの出力先プラグインを指定
    type file
    path /var/log/td-agent/httpd/access.log
    time_slice_format %Y%m%d
    time_slice_wait 10m
    compress gzip
  </store>

  <store>  ←2つめの出力先プラグインを指定
    type mongo
    host localhost
    port 27017
    database fluentd
    collection apache_access
    capped
    capped_size 1024m
    flush_interval 10s
  </store>

</match>

これだけの設定で、自動的に指定されたタグがMongoDBに格納されるようになる。なお、fluentdのドキュメントではMongoDBのCapped Collection機能を利用するよう推奨されているため、今回の設定例でもそのように設定を行っている。この場合、コレクション(SQLデータベースでのテーブルに相当)のサイズが指定されたサイズを超えると、古いデータから自動的に上書きされてしまうので注意が必要だ。古いデータを保持したい場合は、適切に別のデータベースにコピーするといった作業が必要になるだろう。また、MongoDBのデフォルト設定ではユーザー認証が無効になっているが、ユーザー認証を利用するよう設定している場合は「user」や「password」設定項目で指定することで対応できる。

MongoDBについて詳しくは省略するが、MongoDBではデータをJSON形式で格納でき、その要素をキーにクエリを実行することが可能だ。たとえば、「/」というパスへのアクセスについてのログの件数を確認するには、MongoDBのシェル(mongoコマンド)内で以下のようにcountメソッドを実行すれば良い。

> db.apache_access.count({path: "/"});
103

この例の場合、103件のログがそれに該当している。また、findメソッドを利用すれば指定した条件に該当するログを抽出できる。

> db.apache_access.find({path: "/"}).limit(3);
{ "_id" : ObjectId("529892a55f580e7e3b00008f"), "host" : "***.**.***.**", "user" : null, "method" : "GET", "path" : "/", "code" : 301, "size" : null, "referer" : null, "agent" : "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)", "time" : ISODate("2013-11-29T13:11:58Z") }
{ "_id" : ObjectId("5299902e5f580e7e3b000477"), "host" : "***.***.**.**", "user" : null, "method" : "GET", "path" : "/", "code" : 301, "size" : null, "referer" : null, "agent" : "Mozilla/5.0 (compatible; spbot/4.0.3; +http://www.seoprofiler.com/bot )", "time" : ISODate("2013-11-30T07:13:44Z") }
{ "_id" : ObjectId("529b39fd5f580e7e3b000be3"), "host" : "**.***.**.***", "user" : null, "method" : "GET", "path" : "/", "code" : 301, "size" : null, "referer" : null, "agent" : "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "time" : ISODate("2013-12-01T13:29:37Z") }

fluentdと従来のログ収集システムを並列で利用するのがおすすめ

今回はfluentdの概要と、基本的な設定方法のみを解説したが、fluentdには多くのプラグインが用意されており、これ以外にも多くの利用方法が考えられる。公開されているプラグインやドキュメントなどを参考に、利用しているアプリケーションや必要なログに合わせて活用法を考えると良いだろう。

fluentdはsyslogなどの従来のログ収集管理ソフトウェアを完全に置き換えるものではなく、それらと組み合わせて利用することが可能だ。たとえば近年のLinuxディストリビューションでは、syslogdなどがあらかじめ適切な設定でインストールされている。いっぽう、fluentdは設定の自由度が高いこともあり、設定ミスなどでログを記録し損ねる場合なども考えられる。そのため、従来のログ記録機構は有効にしたで、そのうえでfluentdを使って別途加工・再利用・分析しやすい形でログを収集するシステムを構築することをおすすめしたい。

おしらせ