複数台のサーバーやクラウド環境を組み合わせてのサービス運用においては、ログの収集方法に工夫が必要となる。こういった場合に有用なのが、さまざまなログの収集手段を提供するfluentdだ。今回はfluentdのアーキテクチャやそのインストール/設定方法、基礎的な設定例などを紹介する。
さまざまな方法でログを収集できるfluentd
今回紹介するfluentdは、Treasure Dataが開発するログ収集管理ツールだ(図1)。オープンソースで公開されており、Linuxや各種UNIXで動作する。

ログ収集のためのソフトウェアとしてはsyslogdやsyslog-ngなどが有名だが、fluentdがこれらと異なる点としては、以下が挙げられる。
さまざまなソースからのイベントをさまざまな媒体に出力できる
fluentdの大きな特徴としては、ログの収集方法やログの記録先などを柔軟にカスタマイズできる点がある。
一般的にログ収集ソフトウェアは何らかの「イベント」を検知し、その内容を発生時刻などの情報とともにファイルやデータベースなどのストレージなどに出力する、という処理を行うが、fluentdではイベントの受け取り(input)およびストレージなどへの出力(output)がすべてプラグインとして実装されている(図2)。

inputプラグインについてはsyslog互換プロトコルやMessagePackベースの独自プロトコル、HTTPのPOSTリクエスト、Unix Domain Socketなどを使うものや、任意のテキストファイルを監視してそこにテキストが書き込まれたらそれをイベントとして受け取る、といったものなどが用意されている。outputプラグインについては標準出力への出力やテキストファイルへの書き出し、MongoDBへの書き込み、任意のコマンドを実行してそこにログデータを渡す、といったものが用意されている。
また、サードパーティによって開発されたプラグインも多く公開されており、さらに自分でプラグインを作成することも可能だ。fluentdやそのプラグインはRubyで実装されており、Rubyの知識があれば比較的容易にfluentdの機能を拡張できる。
ログの種類はタグで管理される
fluentdでは、ログの管理を「タグ」と呼ばれる情報で管理する。タグはイベントを発生させる側やinputプラグイン側で任意の文字列を指定できるが、以下のように「.」を使った階層構造を取るのが一般的だ。
<親タグ名>.<子タグ名>
「foo.bar.hoge」のように3階層以上の構造を持つタグを指定することもできる。
ログの内容はJSON形式
fluentdではログの内容(レコード)がJSON形式になっている。そのため、アプリケーション側でのパースが容易であるというメリットがある。最近ではMongoDBなどのJSON形式をネイティブで扱えるデータベースも登場しており、これらと組み合わせることでデータの解析などがやりやすくなる。
さまざまな言語向けのモジュールが提供されている
fluentdでは、イベントを送信するためのモジュールがさまざまな言語で用意されている。公式のものとしてはRubyやJava、Python、PHP、Perl、Node.js、Scala向けのモジュールが提供されているほか、サードパーティによってこれ以外の言語向けのモジュールも開発されている。これらを利用することで、独自のアプリケーションのログをfluentd経由で簡単に記録させることができる。
fluentdが適している分野
fluentdはさまざまな環境で利用できるが、やはり多く使われているのはWeb関係の分野だろう。たとえば、一定以上の規模のWebサービスではロードバランサを使ってリクエストを複数台のWebサーバーに分散させる構成が一般的だ。このような構成の場合、fluentdを使ってWebサーバーのアクセスログを処理することで、ログを1つのデータベースに集約することが可能になる。
また、アクセスログに加えてWebアプリケーションから直接fluentdにログを記録させることで、より粒度の高いロギングが可能になる。
クラウド環境においてもfluentdは有用だ。クラウドサービスで提供される仮想マシンでは、ストレージの永続化がサポートされていない(サーバーを停止/再起動させた際にストレージがクリアされる)ことも多い。そのため、各種ログは仮想マシン外の永続的ストレージに記録する必要がある。fluentdを利用すれば、外部ストレージへのログ記録と複数サーバーのログ集約が容易に行える。
fluentdの設定手順
続いては実際にfluentdを利用するために必要な設定手順などを紹介しておこう。以下では、Red Hat Enterprise Linux 6.3互換であるCent OS 6.3上に、fluentdの開発しているTreasure Dataが提供しているRPMパッケージを使ってfluentdをインストールする例を解説する。
fluentdのインストール
fluentdのソースコードはGitHubで公開されている。ただ、通常はソースコードからではなく、RPMやDeb形式のパッケージ、もしくはRuby Gemを利用してインストールするほうが一般的だ。なお、Ruby Gemで(もしくはソースコードから)インストールを行う場合はRuby 1.9.2が必要となるが、fluentdの開発元であるTreasure Dataが提供しているRPM/DebパッケージにはRuby 1.9系があらかじめ含まれているので、こちらを利用する場合は別途Rubyをインストールする必要はない。Red Hat Enterprise Linux(RHEL) 6やその互換ディストリビューションなど、まだRuby 1.9系を公式にサポートしていない環境ではRPM/Debパッケージを利用することをおすすめする。
それ以外の環境でのインストール方法については、ドキュメントが用意されているので、そちらを参照してほしい。
RPMパッケージを使ったインストール
fluentdの開発元であるTreasure Dataでは、RHEL互換環境に簡単にfluentdをインストールできるシェルスクリプト(http://toolbelt.treasuredata.com/sh/install-redhat.sh)を公開している。これをダウンロードして実行すれば、それだけでfluentdのインストール作業が完了する。
もしくは、手動でTreasure Dataが提供するyumリポジトリを利用する設定を追加し、yumコマンドでtd-agentパッケージをインストールすることでもインストールが可能だ。Treasure Dataのリポジトリを利用するには、/etc/yum.repos.dディレクトリに「td.repo」というファイル名で以下の設定を記述すれば良い。
[treasuredata] name=TreasureData baseurl=http://packages.treasure-data.com/redhat/baseurl=http://packages.treasure-data.com/redhat/$basearch gpgcheck=0
この設定ファイルを用意すると、yumコマンドでtd-agentパッケージがインストール可能になる。
# yum install td-agent
なお、この場合fluentdの設定ファイルは/etc/td-agent以下に、ログは/var/log/td-agent/以下に保存される。
fluentdの起動や停止は、/etc/init.dディレクトリ以下に格納されるtd-agentスクリプトで行える。起動するには以下のように「start」引数付きでこのスクリプトを実行すれば良い。
# /etc/init.d/td-agent start
また、fluentdを停止させるには以下のようにする。
# /etc/init.d/td-agent stop
もちろん、RHEL互換ディストリビューションで使われているserviceコマンドやchkconfigコマンドでの制御も可能だ。
# service td-agent Usage: td-agent {start|stop|reload|restart|condrestart|status|configtest} # service td-agent status td-agent は停止しています # chkconfig td-agent --list td-agent 0:off 1:off 2:off 3:off 4:off 5:off 6:off
なお、fluentdのドキュメントではインストール前の作業として、NTPを使ったサーバーの時刻設定や、ファイルディスクリプタの上限数の設定、カーネルパラメータの最適化などが提示されている。これらは必須ではないが、問題の原因となる可能性もあるので、確認したうえで必要に応じて適用しておくと良いだろう。
fluentdの設定ファイル
RPMパッケージを利用してfluentdをインストールした場合、設定ファイルは/etc/td-agent/td-agnet.confとなる。この設定ファイルに収集したいイベントやログの出力先などを記述することで、ログの記録が可能になる仕組みだ。
デフォルトではいくつかの設定が記載されている(リスト1)が、そこで記載されているものは必ずしも必要であるわけではないので、設定内容をすべて破棄し、一から設定項目を記述していったほうが良いだろう。
リスト1 td-agentパッケージに含まれているデフォルトの設定ファイル(抜粋)
:
:
## match tag=debug.** and dump to console
<match debug.**>
type stdout
</match>####
## Source descriptions:
#### built-in TCP input
## @see http://docs.fluentd.org/articles/in_forward
<source>
type forward
</source>## built-in UNIX socket input
#<source>
# type unix
#</source># HTTP input
# POST http://localhost:8888/<tag>?json=<json>
# POST http://localhost:8888/td.myapp.login?json={“user”%3A”me”}
# @see http://docs.fluentd.org/articles/in_http
<source>
type http
port 8888
</source>
:
:
設定ファイルに記述するのは、「source」および「match」、「include」という3つの設定項目(ディレクティブ)だ。
まず、sourceディレクティブはイベントの受信方法を指定するものだ。「type」キーワードで使用する入力プラグインを指定し、さらに続けてプラグインごとの設定項目を記述していく。
<source> type <使用するプラグイン> <設定項目1> <設定値1> <設定項目2> <設定値2> : : </source>
matchディレクティブは、受信したイベントをどのように処理するかを条件文とともに指定するものだ。イベントに付加されたタグが指定したた条件文にマッチした場合、指定したプラグインにそのイベントが渡される。書式はsourceディレクトリとほぼ同じだが、matchのあとに条件文を指定する点が異なる。
<match <条件文>> type <使用するプラグイン> <設定項目1> <設定値1> <設定項目2> <設定値2> : : </match>
条件文内では「*」や「**」といったワイルドカードや、{}を使って複数の条件を指定することができる(表1)。
記号 | 説明 |
---|---|
* | 任意の単一要素にマッチする |
** | 任意の要素にマッチする。その要素が存在しない場合でもマッチする |
{} | 括弧内の少なくとも1つの要素にマッチする |
たとえば「a.*」という条件文を指定した場合、「a.b」というタグにはマッチするが、「a」や「a.b.c」というタグにはマッチしない。また、「a.**」というタグは「a」や「a.b」、「a.b.c」といったタグにマッチする。
{}はor演算子のような役割をするもので、{}内でカンマで区切られた複数のタグのどれか1つにマッチすれば条件が成立する。たとえば「{a, b, c}」という条件文は、「a」や「b」、「c」というタグに対してマッチする。
これらのワイルドカードや演算子は組み合わせて使うことが可能だ。たとえば、「a.{b, c}.*」は「a.b.*」もしくは「a.c.*」のどちらかにマッチすれば条件が成立する。
最後の「include」ディレクティブは、別の設定ファイルに記述されている内容を読み込むものだ。書式は次のようになる。
include <読み込む設定ファイル>
読み込む設定ファイルの指定では絶対パスおよび相対パスの両方が使用可能だ。また、ワイルドカード「*」も利用できる。さらに、URLを指定することでネットワーク越しに設定ファイルを取得することも可能だ。
入力プラグインと出力プラグイン
前述のとおり、fluentdではイベントの入力やログの出力などはすべてプラグインという形で実装されている。
まず入力プラグインだが、公式で用意されている(fluentdに同梱されている)のは、以下の6つだ(表2)。
プラグイン名 | 説明 |
---|---|
in_forward | TCP/IPを使ってイベントを受け取る。主にほかのfluentdや各言語向けのロギングモジュールからイベントを受け取るのに使われる |
in_http | HTTPのPOSTリクエスト経由でイベントを受け取る |
in_tail | テキストファイルを監視し、そこに書き込まれたテキストをイベントとして受け取る |
in_exec | 指定された外部プログラムを実行し、その出力をイベントとして受け取る。指定されたプログラムは永続的、もしくは指定された時間間隔で定期的に実行される |
in_syslog | UDPのsyslogプロトコル経由でイベントを受け取る |
in_scribe | Facebookが開発したログ収集サービス「Scribe」のプロトコル経由でイベントを受け取る |
入力プラグインに関する詳しい説明は、公式ドキュメントで提供されている。
また、出力プラグインについては次の12個が公式プラグインという位置付けだ(表3)。それぞれの詳細は公式ドキュメントで説明されているので、詳しくはそちらを参照して欲しい。
プラグイン名 | 種別 | 説明 |
---|---|---|
out_copy | Non-Buffered | 1つ以上の出力先にログを転送する |
out_null | Non-Buffered | なにも出力しない |
out_roundrobin | Non-Buffered | 指定した1つ以上の出力先にラウンドロビンアルゴリズムを使ってログを転送する |
out_stdout | Non-Buffered | 標準出力にログを出力する |
out_exec_filter | Buffered | 引数としてログを与えて外部プログラムを実行し、その入力をイベントとして受け取る |
out_forward | Buffered | ほかのfluentdにネットワーク経由でログを転送する |
out_mongo | Buffered | MongoDBにログを記録する |
out_mongo_replset | MongoDBにログを記録する | |
out_exec | Time Sliced | 引数としてログを与えて外部プログラムを実行する |
out_file | Time Sliced | 指定したファイルにログを記録する |
out_s3 | Time Sliced | Amazon S3ストレージにログを記録する |
out_webhdfsQ | Time Sliced | HDFS(Hadoop Distributed File System)にログを記録する |
なお、出力プラグインについてはイベントを受け取ったら即座に出力する「Non-Buffered」型プラグインと、イベントを一定量蓄積してから出力する「Buffered」型プラグイン、そして一定の時間間隔で出力を行う「Time Sliced」型のプラグインがある。Buffered型プラグインの場合、Bufferとして使うデバイスをプラグインで指定することが可能だ。
fluentdにはプリセットのBufferプラグインとしてメモリ内にログをバッファリングする「memory」とディスク上のファイルをバッファとして使う「file」が用意されており、どのBufferプラグインを利用するかは各プラグインの設定で指定できる。
設定例: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を使って別途加工・再利用・分析しやすい形でログを収集するシステムを構築することをおすすめしたい。
この記事についてコメントする