2012年9月7日金曜日

fluentd+MongoDBで集めたApacheログの検索システムをRailsで試作

Apacheのアクセスログをfluentdで集めてきてMongoDBに突っ込んで、Webで検索するシステムの試作品を作ったので、作業ログを残す。
WebにはRuby on Railsを使うことにした。
Ruby on Railsのインストール手順は、本Postでは省略。
前提と仕様
今回はテスト用のため、fluentdで集めるのは同一サーバ上のApacheのアクセスログのみ。
環境はCentOS 5.5。
とりあえず、検索条件としてクライアントIPアドレスと期間を指定できるようにしてみる。検索条件の入力省略も許容する。
クライアントIPアドレスには正規表現を使えるようにする。
fluentdとMongoDBのインストール
インターネットに自由に出られない環境へのインストールを見据え、yumは使わない。
MongoDBのRPMはhttp://downloads-distro.mongodb.org/repo/redhat/os/i686/RPMS/から、fluentdのRPMはhttp://packages.treasure-data.com/redhat/i386/からダウンロードした。
まずfluentdをインストール。
$ cd /tmp/mongo+fluentd/
$ ls
mongo-10gen-2.2.0-mongodb_1.i686.rpm         td-agent-1.1.9-0.i386.rpm
mongo-10gen-server-2.2.0-mongodb_1.i686.rpm  td-libyaml-0.1.4-1.i386.rpm
$ rpm -ivh td-*
準備中...                ########################################### [100%]
   1:td-libyaml             ########################################### [ 50%]
   2:td-agent               ########################################### [100%]
adding 'td-agent' group...
adding 'td-agent' user...
Installing default conffile  ...
prelink detected. Installing /etc/prelink.conf.d/td-agent-ruby.conf ...
Configure td-agent to start, when booting up the OS...
次にMongoDBをインストールし、起動。
$ rpm -ivh mongo*
準備中...            :    ########################################### [100%]
   1:mongo-10gen            ########################################### [ 50%]
   2:mongo-10gen-server     ########################################### [100%]

$ service mongod start
all output going to: /var/log/mongo/mongod.log
forked process: 22342
child process started successfully, parent exiting
[  OK  ]
fluentdのMongoプラグインをインストール。
$ /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-mongo
Fetching: bson-1.7.0.gem (100%)
Successfully installed bson-1.7.0
1 gem installed
Installing ri documentation for bson-1.7.0...
Installing RDoc documentation for bson-1.7.0...
Apacheのアクセスログをfluentd+MongoDBに入れてみる
まずfluentdの設定。
$ vi /etc/td-agent/td-agent.conf
…
(snip)
<source>
  type tail
  format apache
  path /var/log/httpd/access_log
  tag mongo.apache
</source>

<match mongo.**>
  type mongo

  database apache
  collection access

  host localhost
  port 27017

  flush_interval 10s
</match>
fluentdを起動。
$ service td-agent restart
Shutting down td-agent: [  OK  ]
Starting td-agent: [  OK  ]
これでログ収集が始まったはずなので、その辺のPCのブラウザから適当にApacheにアクセスしてアクセスログを書き込んでみた。
MongoDBにアクセスログが入ったかどうか確認してみよう。
$ mongo
MongoDB shell version: 2.2.0
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
> show dbs
apache0.0625GB
local(empty)
> use apache
switched to db apache
> show collections
access
system.indexes
> db.access.find();
{ "_id" : ObjectId("50444fef30bad75840000001"), "host" : "(クライアントのIPアドレス)", "user" : "-", "method" : "GET", "path" : "/", "code" : "304", "size" : "-", "referer" : "http://server/redmine", "agent" : "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0", "time" : ISODate("2012-09-03T06:36:26Z") }
{ "_id" : ObjectId("50444fef30bad75840000002"), "host" : "(クライアントのIPアドレス)", "user" : "-", "method" : "GET", "path" : "/javascripts/prototype.js?1290948531", "code" : "304", "size" : "-", "referer" : "http://server/", "agent" : "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0", "time" : ISODate("2012-09-03T06:36:28Z") }
…
(snip)
> exit
bye
キタで! ちゃんと書き込まれてる。
Ruby on Railsでログ検索アプリを作る
では本題のログ検索アプリの開発に入る。
今回は/var/www/配下に"fluentd"というプロジェクトを作ることにした。
(あまりいい名前じゃないな…)
$ cd /var/www
$ rails fluentd
      exists  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      create  config/environments
…
(snip)
"search"というコントローラを作る。"index""result"の2つのアクションを作成。
indexが検索条件入力画面、resultが検索結果表示画面のつもり。
$ cd fluentd
$ ruby script/generate controller search index result
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/search
      exists  test/functional/
      create  test/unit/helpers/
      create  app/controllers/search_controller.rb
      create  test/functional/search_controller_test.rb
      create  app/helpers/search_helper.rb
      create  test/unit/helpers/search_helper_test.rb
      create  app/views/search/index.html.erb
      create  app/views/search/result.html.erb
Mongo以外のDBを使わないので、environment.rbに下記2行を追加(endの直前)。
$ vi /var/www/fluentd/config/environment.rb
…
(snip)
  # 2012.09.05 dsp74118
  config.frameworks -= [ :active_record ]
(snip)
…
routes.dbは下記のように直した。
$ vi /var/www/fluentd/config/routes.rb
ActionController::Routing::Routes.draw do |map|
  # 2012.09.05 dsp74118
  map.connect ':controller/:action'
end
では、画面とロジックを作っていく。
まず、ログ検索画面のビューを作る。
一応断っておくと、HTMLは超手抜き。
$ cd /var/www/fluentd/app/views/search
$ vi index.html.erb
<html>
<head>
<title>検索条件入力</title>
</head>
<body>
<h1>検索条件入力</h1>
<% form_tag "/search/result" do %>
  クライアントIPアドレス:<%= text_field_tag :HOST %>
  検索範囲(開始日時):<%= text_field_tag :START %>
  検索範囲(終了日時):<%= text_field_tag :END %>
  <%= submit_tag "検索" %>
<% end %>
</body>
</html>
コントローラを書く。
本当は検索処理はコントローラに書きたかったのだけど、作っていざ動かしてみたら、検索結果が大量に出すぎたためかCookieOverflowが出てしまった。これは、検索結果をセッション変数に乗せてビューに渡そうとしたことが原因なのは疑いようがなくて、もっといいやり方があるはず。
今回はテスト用の手抜き実装なので、コントローラではなくビューに検索処理を実装することにした。
というわけで、コントローラは検索パラメータを受け取るだけ。
$ cd /var/www/fluentd/app/controllers
$ vi search_controller.rb
class SearchController < ApplicationController
  def index
  end

  def result
    flash[:queryHost] = params[:HOST]
    flash[:inputS] = params[:START]
    flash[:inputE] = params[:END]
  end
end
検索結果画面のビューを書く。デコレーションは一切なし。
$ cd /var/www/fluentd/app/views/search
$ vi result.html.erb
<html>
<head>
<title>ログ検索結果</title>
</head>
<body>
<h1>ログ検索結果</h1>
<%
require "time"
require 'mongo'

# ホスト(IPアドレス)
queryHost = flash[:queryHost]
if queryHost == '' then
  queryHost = '.*'
end
r = Regexp.new(queryHost)

# 検索範囲(開始日時)
inputS = flash[:inputS]
if inputS == '' then
        inputS = '2000-01-01T00:00:00Z'
end
qts = Time.parse(inputS)

# 検索範囲(終了日時)
inputE = flash[:inputE]
if inputE == '' then
        qte = Time.now
else
        qte = Time.parse(inputE)
end

# MongoDBに接続
m = Mongo::Connection.new('localhost', 27017)
db = m.db('apache')

# クエリ
results = db['access'].find({:host => r, :time => {"$gt" => qts, "$lte" => qte}}).to_a
%>
HOST:<%= queryHost %><br />
日時:<%= qts %>~<%= qte %><br /><br />
<%
results.each {|val|
%>
<%= val.inspect %><br />
<%
}
%>
</body>
</html>
完成したのでサーバを起動してみる。
$ ruby script/server
=> Booting WEBrick
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2012-09-05 20:37:15] INFO  WEBrick 1.3.1
[2012-09-05 20:37:15] INFO  ruby 1.8.7 (2010-08-16) [i686-linux]
[2012-09-05 20:37:15] INFO  WEBrick::HTTPServer#start: pid=4551 port=3000
無事起動したようだ。ブラウザでアクセスしてみよう。
Figure.1 検索条件入力画面
検索画面が出たので、適当に検索条件を入れてみる。
Figure.2 検索結果画面
検索結果出た!
MongoDBに入っているデータをそのまま出しただけなので見た目は最悪だが、今回は目をつむる。

とりあえず今回はここまで。
検証用に作ったシステムなので、これ以上手を入れるかは未定。

0 コメント:

コメントを投稿