fluentd + MongoDB でログサーバー構築

fluentd + MongoDB でログサーバーを作ってみた。モノはここに。

https://github.com/januswel/server-base/tree/fluentd

できてみれば設定はシンプルだったんだけどハマるところも多かったのでちょっと書く。

とりあえず以下のルールで設定。

- ポート 24224 でまちうけ
- ログは MongoDB の fluentd データベースにつっこむ
    - fluentd で処理対象にするタグは node ではじまるもの
        - Node.js からのログを想定
    - このときに使う MongoDB ユーザーは readWrite ロールの logger ユーザー
        - できるだけ権限を限定する
- ログ解析するときは dbOwner ロールを持つ fluentd ユーザーを使って MongoDB にもぐる


MongoDB
-------

### インストール・設定

epel からインストールもできるけどバージョンが古いので公式の yum リポジトリから取得するのがいい。ここらへんを参照しながらてろてろと。

http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat-centos-or-fedora-linux/

設定は認証を有効にしただけでほかはデフォルト。そういえば 2.6 から yaml で設定できるって書いてあるんだけど書き方が悪かったのかさっぱり読んでくれなかった。サンプルは古いままだしまぁそのうちなんとかなるでしょということでスルー。

http://docs.mongodb.org/manual/reference/configuration-options/

設定を書き換えたら mongod をたちあげる。

### 管理ユーザー追加

mongod 立ち上げ時に --auth オプションを使ったり、設定ファイルで <code>auth=true</code> を書いておくと認証が有効になる。パフォーマンスとか重視する場合は使わないほうがいいのかもしれないけど、とりあえず認証ありで。

たちあげたあとに真っ先にすることは管理用ユーザーの追加。ユーザー登録なしだとローカルからなら認証なしでもつながるみたいだけどひとりでもユーザーが登録されてると認証を通さないと何もできなくなる。

admin データベースに root ロールを持つ root というユーザーを作る。下記のファイルを用意して <kbd>mongo admin mongodb-admin.js</kbd> 。

```js:mongodb-admin.js
/*
 * Usage
 *  mongo admin mongodb-admin.js
 *
 * A script to add administration user
 * */

var USERNAME = 'root';
var PASSWORD = 'root';

// Check if user is registered
var authed = db.auth(USERNAME, PASSWORD);
if (authed === 1) {
    quit();
}

// Register user
db.createUser({
    user: USERNAME,
    pwd: PASSWORD,
    roles: [
        {
            role: 'root',
            db: 'admin'
        }
    ]
});
```

これで何かあってもとりあえず <kbd>mongo -u root -p root --authenticationDatabase admin</kbd> しとけば設定変更ができるようになる。


fluentd
-------

### MongDB にユーザー追加

まず MongoDB にユーザーを追加する。こういうスクリプト書いておくと便利。

```js:fluentd-users-mongodb.js
/*
 * Usage
 *  mongo fluentd fluentd-users-mongodb.js -u root -p root --authenticationDatabase admin
 * */

// User definitions to register
var newUsers = [
    {
        user: 'logger',
        pwd: 'logger',
        roles: [
            {
                role: 'readWrite',
                db: 'fluentd'
            }
        ]
    },
    {
        user: 'fluentd',
        pwd: 'fluentd',
        roles: [
            {
                role: 'dbOwner',
                db: 'fluentd'
            }
        ]
    }
];

// Check if initial settings are done
var currentUsers = db.getUsers();
if (currentUsers.length === newUsers.length) {
    quit();
}

// Drop users for database
db.dropAllUsers();

// Add users
for (var i = 0, length = newUsers.length; i < length; ++i) {
    db.createUser(newUsers[i]);
}
```

で、こうやって起動する。長いよ。

```sh
mongo fluentd fluentd-users-mongodb.js -u root -p root --authenticationDatabase admin
```

### インストール

インストール前に NTP の設定しろだとかファイルディスクリプターの数を増やせだとか TCP 接続数があふれないようにしろだとか。ここらへんのリソースをかなり潤沢に使うもんみたい。

http://docs.fluentd.org/articles/before-install

で、 yum つかって RPM からインストール。 yum で全部管理できるほうがありがたい。

http://docs.fluentd.org/articles/install-by-rpm

### 設定

全部 /etc/td-agent/td-agent.conf に書く。 include で区切るのはあとで。実際に動く設定はここ。

https://github.com/januswel/server-base/blob/fluentd/ansible/roles/log/templates/td-agent.conf.j2

まずまちうけるポートの設定。

```conf
# Listen at the port 24224
<source>
  type forward
  port 24224
</source>
```

で、処理するデータの指定と MongoDB への書き込みの設定。

```conf
# from Node.js
<match node.**>
  type mongo
  host localhost
  port 27017
  database fluentd
  user logger
  password logger

  collection app

  flush_interval 1s
</match>
```

これで fluentd 起動すればどんどこログを飲み込んでくれる。

### 動作確認

Node.js 側で fluent-logger-node を使ってテストしてみる。

https://github.com/fluent/fluent-logger-node

```js:fluentd-test.js
var logger = require('fluent-logger');
logger.configure('node', {
    host: '192.168.50.120',
    port: 24224,
    timeout: 3.0
});
logger.emit('info', { test: 'test data' });
```

こんなんつくってこう。

```sh
npm install fluent-logger
node fluentd-test.js
```

ログサーバー側でみてみる。

```sh
mongo fluentd -u fluentd -p fluentd --eval 'db.app.find()'
```

```js
{ "_id" : ObjectId("5402c483dd3aa90a27000001"), "test" : "test data", "time" : ISODate("2014-08-31T06:45:23.046Z") }
```

なんかきてる !!


考えどころ
----------

MongoDB にはシャーディングという複数台で動くインスタンスをまとめてひとつとして管理できる機能があるらしく、このサーバー上で mongod のかわりに mongos をたちあげて、別ノードのインスタンスを管理する、とかするとビッグデータ的なアレができて夢が広がるかも。

http://docs.mongodb.org/manual/sharding/