ラベル mongodb の投稿を表示しています。 すべての投稿を表示
ラベル mongodb の投稿を表示しています。 すべての投稿を表示

2012年11月7日水曜日

Fluentdってなんじゃ?(Mongo&Node編 ブラウザでtail)

前回までで、fluentdを利用してApacheのログをmongoDBに保存することができました。

構成はこのようになっていました。
  +----------------------------+   +------------------------+
  | web server (10.0.0.8)      |   | mongo server(10.0.0.16)|
  |----------------------------|   |------------------------|
  |          fluentd           |   |                        |
  |                            |   |                        |
  | +---------+     +--------+ |   |      +----------+      |
  | | input   |     | output | |   |      |  fluent  |      |
  | |---------|+--> |--------| +--------->|----------|      |
  | |  tail   |     |  mongo | |   |      |   test   |      |
  | +---------+     +--------+ |   |      +----------+      |
  +----------------------------+   +------------------------+

今回はnode.jsを使用して、mongoDBに保存されたログをブラウザでリアルタイムにtailしてみます。
構成は以下のとおりです。
  +----------------------------+   +-------------------------+
  | web server (10.0.0.8)      |   | mongo server(10.0.0.16) |
  |----------------------------|   |-------------------------|
  |          fluentd           |   |         mongod          |
  |                            |   |                         |
  | +---------+     +--------+ |   |      +----------+       |
  | | input   |     | output | |   |      |  fluent  |       |
  | |---------|+--> |--------|+---------->|----------|       |
  | |  tail   |     |  mongo | |   |      |   logs   |       |
  | +---------+     +--------+ |   |      +-----+----+       |
  +----------------------------+   +------------|------------+
                                                |
                                 +--------------+
                                 | +-------------------------+
                                 | | node server(10.0.0.100) |
                                 | |-------------------------|
                                 | |         node.js         |
                                 | |                         |
                                 | |      +-----------+      |
                                 +------->| mongoose  |      |
                                   |      |-----------|      |
                                   |      | socket.io +----------> Brower
                                   |      +-----------+      |
                                   +-------------------------+


今回、web serverとmongo serverは、ほとんど前回の設定のままですが、
新規nodeサーバーでmongodbにアクセスするmongooseはデフォルトで複数系の名前のコレクションにアクセスするようになっています。
そこで、webサーバーのtd-agentのmongo outputプラグインの書き出し先をlogsという名前に変更しておきます。
また、リアルタイムに表示させたいのでflush_intervalを0sに設定しておきます。
<source>
  type tail
  format apache
  path /var/log/httpd/access_log
  tag apache.access
</source>

<match apache.access="apache.access">
  type mongo
  flush_interval 0s
  database fluent
  collection logs

  host 10.0.0.16
  user memorycraft
  password *******
</match>


それでは、nodeサーバーを設定します。
新規にEC2インスタンスをたちあげてnodeとhttpdをインストールします。
# yum install httpd -y
# /etc/init.d/httpd start

# yum install -y wget
# cd /usr/local/src/
# wget http://nodejs.org/dist/v0.8.14/node-v0.8.14.tar.gz
# tar xzvf node-v0.8.14.tar.gz 
# cd node-v0.8.14
# ./configure
# make
# make install


次にnpmをインストールしてからsocket.io, log, forever, mongooseをnpmインストールします。
# curl https://npmjs.org/install.sh | sh
# npm install -g socket.io
# npm install -g forever


nodeを動かすためのユーザーを作成します
# useradd appadmin
# passwd appadmin
# chmod 755 /home/appadmin
# su - appadmin


ノードモジュールへのパスを通すために環境変数を設定します。
$ vi ~/.bash_profile 
$ source ~/.bash_profile 
---
export NODE_PATH=/usr/local/lib/node_modules
---

$ source ~/.bash_profile


ここで、nodeサーバーとHTML用のコンテンツ置き場を用意します。
ディレクトリ構成は以下のとおりです。
cd ~/nodetest
tree
.
|-- node
|   |-- logs
|   |   `-- app.log
|   `-- server.js (nodeサーバー)
`-- public
    |-- assets
    |   |-- css
    |   |-- img
    |   `-- js
    |       `-- client.js (nodeクライアント)
    `-- index.html (tail用の画面)


また、publicをhttpdのDocumentRootにするため、以下のように設定します。
$ su -
# cd /var/www
# mv html html.org
# ln -s /home/appadmin/nodetest/public html
# vi /etc/httpd/conf/httpd/conf
---
~略~
<directory html="html" var="var" www="www">
    Options FollowSymLinks
    AllowOverride All
~略~
---
</directory>

# /etc/init.d/httpd restart


そして、httpdとnodeに接続させるため、SecurityGroupを以下のように設定します。



次にサーバーとコンテンツを実装してみます。 それぞれの内容は以下の通りです。

node/server.js
//3001番でlistenします
var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(3001);

//logsコレクションのスキーマ定義をしておきます。
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var AccessSchema = new Schema({
    host: String,
    user: String,
    method: String,
    path: String,
    code: Number,
    size: Number,
    referer: String,
    agent: String,
    time : Date
});
mongoose.model('log', AccessSchema);

//fluentデータベースに接続します。
mongoose.connect('mongodb://10.0.0.16/fluent');
var Access = mongoose.model('log');

//socket.ioを起動します。
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
    /**
     * クライアント接続時に、初期データとして、
     * 保存されているlogsコレクションをすべてemitします。
     */
    Access.find({}).sort({time:1}).exec(function(e, docs){
        if(e){
        }
        else{
            socket.emit('init', docs);
        }
    });
});

/** 
 * 1.5秒おきに最終取得ログの日時以降のログを取得して
 * 全員にブロードキャストし、最終日時を保存します。
 */
var last = new Date();
setInterval(function(){
    Access.find({}).where('time').gt(last).sort({time:1}).exec(function(e, docs){
        if(e){
        }
        else{
            for(var i=0; i<docs.length; i++){
                if(last < docs[i].time){
                    last = docs[i].time;
                }
            }
            io.sockets.emit('log', docs);
        }
    });
}, 1500);


public/assets/js/client.js
$(function(){
  //接続します
  var socket = io.connect('http://' + location.hostname + ':3001/');
  socket.on('connect', function() {
   /**
     * 接続成功時に初期データを時系列降順で表示させます
     */
    socket.on('init', function(data){
        $("#log").empty();
        for(var i=0; i<data.length; i++){
            row = data[i];
            $("#log").prepend(row.host+" "+row.time+" "+row.path+"<br/>");
        }
    });

    /**
     * 随時送られてくるデータは一番上に表示させます
     */
    socket.on('log', function(data){
        for(var i=0; i<data.length; i++){
            row = data[i];
            $("#log").prepend(row.host+" "+row.time+" "+row.path+"<br/>");
        }
    });

  });
});


public/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
    function load(){
        $.getScript("assets/js/client.js");
    }
    $.getScript("http://" + location.hostname + ":3001/socket.io/socket.io.js", function(){
        load();
    });
});
</script>
<body>
    <div id="content">
        <h1>ログ</h1>
        <div id="log" width="500px" height="100%">
        </div>
    </div>
</body>
</html>


早速動かしてみます。
$ cd ~/nodetest/node
$ forever start server.js

これでnodeサーバーが起動しました
それでは、ブラウザを見てみます。



初期ログが表示されています。うまく動いているようです。
そこで、fluentdのtail対象サーバーであるwebサーバーへアクセスをしてみます。
わかりやすくするために、何度かリロードしておきます。


そして、さきほどのnodeサーバーの画面をみると、、、


おお!ログが追加で現れました!
これであるサーバーのログデータを別のサーバーでリアルタイムに表示させることができました。

以上です。

2012年11月5日月曜日

Fluentdってなんじゃ?(MongoDB編)


今回は、fluentdを利用して、Apacheのログを別サーバーのMongoDBに保存してみようと思います。

前回のlogサーバー(10.0.0.16)にmongoDBをホストし、webサーバー(10.0.0.8)のtd-agentから直接mongoに保存します。

まずはlogサーバーにmongoDBをインストールします。
yumリポジトリとして以下を追加してからyumインストールを行います。
# vi /etc/yum.repos.d/10gen.repo]
---
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686
gpgcheck=0
---

# yum install mongo-10gen mongo-10gen-server -y

mongoのインストールは完了しました。
起動しておきます。
# /etc/init.d/mongod start


続いてwebサーバー側にmongoプラグインを設定します。
mongo Output Pluginはビルトインではなく、td-agent用のrubygemでインストールして使用するため、以下のようにインストールを行います。
# /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-mongo
Fetching: fluent-plugin-mongo-0.6.10.gem (100%)
Successfully installed fluent-plugin-mongo-0.6.10
1 gem installed
Installing ri documentation for fluent-plugin-mongo-0.6.10...
Installing RDoc documentation for fluent-plugin-mongo-0.6.10...


設定ファイルには、以下のように出力先をtype mongoを設定します。これは、

source
 apacheフォーマットの/var/log/httpd/access_logファイルをtailし、apache.accessというタグをつける

match
  apache.accessタグの入力があったら、10.0.0.16のmongoDBのfluentデータベースへmemorycraftユーザーで接続し、hogeコレクションとしてデータを登録する

という意味になります。
# vim /etc/td-agent/td-agent.conf

---

<source>
  type tail
  format apache
  path /var/log/httpd/access_log
  tag apache.access
</source>

<match apache.access>
  type mongo
  database fluent
  collection hoge
  host 10.0.0.16
  user memorycraft
  password ******
</match>

---


この時点ではwebサーバーからlogサーバーへmongoの接続ポートが閉じているので、まだ接続できません。
EC2で、mongoのデフォルトポートの27017番を開放します。




ここで一度webサーバー側のtd-agentを再起動しておきます。
# /etc/init.d/td-agent restart


この時点でmongoに接続すると、fluentデータベースはできているようです。
ここで、認証用にユーザーを作成します。
# mongo
MongoDB shell version: 2.2.1
connecting to: test

> use fluent
switched to db fluent
> db.addUser("memorycraft", "*****")
{
 "_id" : ObjectId("5096e46f1b4da85be896fb1e"),
 "user" : "memorycraft",
 "readOnly" : false,
 "pwd" : "2922d9ce24a959b1f359f7ba2d044017"
}

これで認証が設定されたので、webサーバー側のtd-agentを改めて再起動します。
# /etc/init.d/td-agent restart


ここで、mongoシェルに接続してみるとfluentというDBにhogeというコレクションができていることがわかります。
# mongo
MongoDB shell version: 2.2.1
connecting to: test
> use fluent
switched to db fluent
> show collections
system.indexes
system.users
hoge


中身を見てみると、、、
> db.hoge.find()
{ "_id" : ObjectId("5096e4e5a3441113d4000001"), "host" : "219.117.233.241", "user" : "-", "method" : "GET", "path" : "/", "code" : "403", "size" : "5039", "referer" : "-", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.1 Safari/537.17", "time" : ISODate("2012-11-04T21:57:09Z") }
{ "_id" : ObjectId("5096e4e5a3441113d4000002"), "host" : "219.117.233.241", "user" : "-", "method" : "GET", "path" : "/icons/poweredby.png", "code" : "304", "size" : "-", "referer" : "http://54.249.23.246/", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.1 Safari/537.17", "time" : ISODate("2012-11-04T21:57:10Z") }
{ "_id" : ObjectId("5096e4e5a3441113d4000003"), "host" : "219.117.233.241", "user" : "-", "method" : "GET", "path" : "/icons/apache_pb.gif", "code" : "304", "size" : "-", "referer" : "http://54.249.23.246/", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.1 Safari/537.17", "time" : ISODate("2012-11-04T21:57:10Z") }
{ "_id" : ObjectId("5096e4e5a3441113d4000004"), "host" : "219.117.233.241", "user" : "-", "method" : "GET", "path" : "/favicon.ico", "code" : "404", "size" : "288", "referer" : "-", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.1 Safari/537.17", "time" : ISODate("2012-11-04T21:57:10Z") }



おおー。データが入っています。
たとえば、pathが"/"のデータのみのログも簡単に抽出できるようになりました!
> db.hoge.find({path:"/"})
{ "_id" : ObjectId("5096e4e5a3441113d4000001"), "host" : "219.117.233.241", "user" : "-", "method" : "GET", "path" : "/", "code" : "403", "size" : "5039", "referer" : "-", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.1 Safari/537.17", "time" : ISODate("2012-11-04T21:57:09Z") }


このようにfluentdを用いると、ログの集約や集計がデイリーやマンスリーではなく準リアルタイムで行えるようになります。とても便利ですね。

今回はここまで。

2011年10月20日木曜日

WebSocketってなんじゃ?(Node編4 MongoDBってなんじゃ?)

今回はMongoDBの話題です。
MongoDBはいわゆるNoSQLで、BSONというJSONのバイナリ版のような形式でデータの入出力を行い、クエリなどのデータ操作をjavascriptで行うため、サーバーサイドJavascriptと非常に相性がよく、node.jsとともによく利用されているようです。

今回はこのMongoDBをnode.jsから操作してみます。

MongoDB公式サイトにあるように、yumでインストールするために、MongoDB用のyumリポジトリを追加します。
以下のファイルを作成します。
/etc/yum.repos.d/10gen.repo
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686
gpgcheck=0


さっそく、yumでインストールします。
yum install mongo-10gen mongo-10gen-server -y


インストールが終了したら、mongodが/etc/init.d/以下にできるので、起動します。
/etc/init.d/mongod start


MongoDBのコンソールを立ち上げてみます。
$ mongo

MongoDB shell version: 2.0.0
connecting to: test
testというdbにつながったようです。
helpと入力すると、いくつかのコマンドが紹介されています。DBの一覧を見てみます。
> show dbs
local (empty)
test (empty)
localとtestというDBがあり、中身はまだ空になっています。

MongoDBは基本的にはスキーマを定義することなく、いきなりデータを保存できます。データが保存された時点でそれがスキーマになります。
以下のように、hogeというデータを保存、その直後にhogeを参照してみます。
> db.hoge.save({id:"1", name:"HOGE"});
>
> db.hoge.find();
{ "_id" : ObjectId("4e9f8ced316d58abc218dc43"), "id" : "1", "name" : "HOGE" }

このように、クエリにあたるものもすべてjavascriptで行い、結果はJSON形式で表示されます。いかにもnodeと相性がよさそうです。


それでは、このMongoDBにnode.jsからアクセスしてみます。
node.jsからMongoDBにアクセスするには、MongoDB用のパッケージモジュールを入れる必要があります。
Mongo用のモジュールはいくつかあるのですが、今回はMongooseを利用してみます。
mongooseはnpmでインストールします。
# npm install -g mongoose

> mongodb@0.9.6-22 install /usr/local/lib/node_modules/mongoose/node_modules/mongodb
> bash ./install.sh

================================================================================
=                                                                              =
=  To install with C++ bson parser do <npm --mongodb:native="" install="" mongodb="">   =
=  the parser only works for node 0.4.X or lower                               =
=                                                                              =
================================================================================
Not building native library for cygwin
Using GNU make
mongoose@2.3.5 /usr/local/lib/node_modules/mongoose
├── colors@0.5.0
├── hooks@0.1.9
└── mongodb@0.9.6-22


以下のようにmongoにデータを投入するコードを書いてみます。
#!/usr/local/bin/node

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/memorycraft');

var Schema = mongoose.Schema;
var ItemSchema = new Schema({title:String, auther:String, price:Number, date:Date});
var Item = mongoose.model('Item', ItemSchema);

var item = new Item();
item.title = "体脂肪計タニタの社員食堂 ~500kcalのまんぷく定食~";
item.auther = "タニタ";
item.price = 1200;
item.date = new Date(2010, 1, 21);

item.save(function(err){
  if(err){
    console.log(err.message);
  }
  else{
    console.log("保存しました");
  }
});


これを実行します。
$ ./hello_mongo.js
保存しました


ここで、実際にデータが投入されているか再度mongoのコンソールで確認してみます。
$mongo
> show dbs
local (empty)
memorycraft 0.0625GB
test 0.0625GB
>
> use memorycraft
switched to db memorycraft
>
> show collections
items
system.indexes
>
> db.items.find()
{ "date" : ISODate("2010-02-20T15:00:00Z"), "price" : 1200, "auther" : "タニタ", "title" : "体脂肪計タニタの社員食堂 ~500kcalのまんぷく定食~", "_id" : ObjectId("4e9f98394699a3fb40000001") }

memorycraftというdbが増えていて、投入したデータが入っていることが確認できました。

ここで、mongodb+nodeがいいのは、データの保持の時点でJSONなので、取得結果もJSONになっていることです。
そして、上記のコードを書いたようにアクセスの結果がすべてコールバックで記述できるので、とても柔軟にコード記述ができます。
例えば以下のように別途WEBフレームワークなどを使用した場合、取得したデータをそのままレスポンスに返すことで余計な変換処理が必要なくなります。
Item.find({}, function(err, items) {
  if(!err) {
    res.json(items);
  }
});

ほんの少し触れただけですが、とても便利なことがわかりました。
まだまだいろいろいじってみようと思います。
本日は以上です。