fluentd で集めたログを Splunk で可視化する

ウェブアプリケーションのログ収集には fluentd を使うとして、集めたログを検索したりグラフ化するには、別途システムを組む必要がある。

最近だと、オープンソースの Kibana というのが流行っているようで、公式ページにも紹介がある。

Free Alternative to Splunk Using Fluentd | Fluentd

ここで比較対象とされている "Splunk" だけど、これを fluentd と組み合わせて使っている人は多くないようなので、軽く紹介しておきたい。

Splunkとは?

商用のログ収集&検索エンジンとしてはメジャーな製品で、

  • 独自のクエリ言語でログを検索、加工、集計、グラフ化する
  • あらかじめダッシュボードを作っておいてPDFでレポートを送る
  • 検索条件を設定しておいてアラートを飛ばす

といったことが出来るようになっている。

f:id:keisukenishida:20130819200555p:plain

詳しくは公式のビデオでも。


Splunk Product Overview - YouTube

典型的な使い方の一つとして、条件にマッチするログの数を時系列グラフにしたいなら、次のようなクエリを書く。

sourcetype=access_combined status=404 | timechart count by host

どんなグラフを作りたくなるかは事前にはわからないので、Splunk にはあらかじめすべてのログを送っておく。実際に集計したいと思った時に、ちょっとしたクエリでインタラクティブにグラフを作っていけるのが Splunk の魅力。毎日の、あるいはここ数ヶ月のログを対象にして、サーバがどのような動きをしていたのか対話的に探っていくのが主な使い方になっている。

体感的な性能としては、例えば数ヶ月にわたって数億件のレコードがあるとして、そこから検索で数十万レコードに絞り込んでグラフにするくらいであれば、一台のサーバでストレスなく使える。ただ、条件に一致するレコード数が数百万件を超えてくると遅くなるので、検索を高速化するための分散化やチューニング等が必要になる。

価格的には、一日のログの量に応じた課金体系となっているので、大量のログを処理するには高コストかもしれない。ただ、一日500MB(≒ 数百万レコード)までなら無料で使えるので、用途によってはそれで十分かもしれない。

うちの場合、開発者は全員自分のマシンに Splunk をインストールしていて、仮想環境のサーバから fluentd で各自の Splunk にログを集めている。アプリケーションごとのダッシュボードも共有していて、開発中はダッシュボードを見ながらサーバの動きを確かめている。とりわけ性能試験をするときには、流れるログの量によってボトルネックが把握できて便利。

クエリ言語を覚えるのはなかなか大変で、Kibana と比べて使いやすいかどうかはわからないけれども、今のところ自分は Splunk で満足している。*1

fluent-plugin-splunkapi

Splunk にログを送るには、一度ファイルに落としてから Splunk Universal Forwarder で転送するのが正攻法だが、ここでは fluentd から直接送ることを考えたい。

ということで、fluentd から Splunk にログを送るためのプラグインを書いてみた。

k24d/fluent-plugin-splunkapi · GitHub

例えば Splunk サーバが 10.10.0.1 で動いているとして、すべてのログを転送するなら次のようになる。

<match **>
  type splunkapi
  protocol rest
  server 10.10.0.1:8089
  verify false
  auth admin:changeme
  host my-app
  time_format localtime
  format kvp

  # Memory buffer with a short flush internal.
  buffer_type memory
  buffer_queue_limit 16
  buffer_chunk_limit 8m
  flush_interval 2s
</match>

ここで "format kvp" というのは、fluentd に送られた JSON 形式のデータ(例: {"x": 1})を、すべて Key-Value ペア(例: x="1")に変換してから転送する。すると Splunk はそれをフィールドとして自動認識してくれるので、"x = 1" といった条件で検索や集計に使えるようになる。

JSON形式

JSON 形式がよければ、次のように設定する。

  time_format none
  format json

この場合も Splunk は JSON 形式を認識してくれて、ネストされた複雑なデータ構造でも、spathという簡易な書式で検索が可能。例えば、次のログは "x=1" や "attributes.username=1234" などにマッチする。

f:id:keisukenishida:20130819213938p:plain

(8/21追記) 注意点として、送信データからタイムスタンプをなくすとログの境界線が自動認識されなくなるので、「一行一レコード」というのを明示的に設定しないといけない。これにはサーバ側でprops.confを用意する。

# /opt/splunk/etc/apps/search/local/props.conf

[source::myapp.*] SHOULD_LINEMERGE=false

JSON 形式はタイムスタンプの扱いが何かと面倒なので、どうしても構造データを扱いたいのでない限り、自分は kvp を使うほうが多い。

テキスト形式 

既存のログファイルを tail してそのまま送るなど、ログファイルにタイムスタンプが含まれる場合にはテキスト形式を用いる。

  time_format none
  format text

この場合、元のログをそのまま転送できるので、Splunk Universal Forwarder を使うのと同じようにログを送ることも可能。

Splunk Universal Forwarder との併用

fluent-plugin-splunkapi は最新の Splunk 5 と Splunk Storm の両方に対応しているが、手抜き実装のためいくつかの問題がわかっており、回避策として Splunk Universal Forwarder を併用するのがおすすめ。つまり、

 applications → fluentd → Splunk Universal Forwarder → Splunk 5 / Splunk Storm

という経路でログを送るのがいい。

fluentd と Forwarder はなるべく同じホストで動かしたほうがいい。両者がネットワーク的に断絶されるリスクをなるべくなくし、Splunk サーバとの通信断が発生したときのバッファリングは Forwarder に委ねるのが好ましい。

この場合、設定は次のようになる。

<source>
  type forward
</source>

<match **> type splunkapi protocol rest server 127.0.0.1:8089 verify false auth admin:changeme host my-app time_format localtime format kvp
# Set "check_index" to "false" if you use index.
index main
check_index false
# Memory buffer with a short flush internal. buffer_type memory buffer_queue_limit 16 buffer_chunk_limit 8m flush_interval 2s </match>

ファイルを tail して無加工で送るなら次のようになる。

<source>
type tail path /var/log/apache2/error.log tag apache_error.log format /(?<message>.*)/ pos_file /var/log/td-agent/apache_error.pos </source>

<match apache_error.log> type splunkapi protocol rest server 127.0.0.1:8089 verify false auth admin:changeme host my-app
time_format none format text
# Set "source" and "sourcetype" for Splunk.
source /var/log/apache2/error.log sourcetype apache_error
# Set "check_index" to "false" if you use index.
index main
check_index false
# Memory buffer with a short flush internal. buffer_type memory buffer_queue_limit 16 buffer_chunk_limit 8m flush_interval 2s </match>

Forwarder からの先の転送設定は、Forwarder 自身で行う。例えば、Splunk 5 に転送するなら次のようになる。

$ sudo /opt/splunkforwarder/bin/splunk add forward-server 10.10.0.1:9997

Splunk 5 では index を指定することも出来る。ただし、Forwarder に対してデフォルト以外のインデックスを指定するとエラーになるので、"check_index" を "false" にセットするか、あるいは Forwarder の設定ファイル(indexes.conf)で index を定義しておく必要がある。

既知の問題点

現在の fluent-plugin-splunkapi は、その名の通り Splunk API を使ってログの転送を行っている。しかし、これにはパフォーマンスの問題があるとされており、大量のログを送るには Splunk 5 の新機能である Modular Inputs を使うのがよいらしい。余裕ができたら調べてみたい。

Splunk は、リアルタイムで対話的にデータを検索、グラフ化するには便利だが、長期に渡る大量のデータ(数千万レコード以上)を分析するには向いていない。また、独自のクエリ言語を覚えるのは学習コストが高く、誰でも使いこなせるわけではない。

fluentd で集めたログを可視化するもう一つのツールとして Tableau を利用しているので、次回はそちらについて説明したい。

*1:というか、もう1年以上使っているので、いまさら他のものを覚える気にならない、というのが正直なところ。