ボクココ

個人開発に関するテックブログ

Hello! MongoDB レプリカセット!

いよいよ来ましたレプリケーション。本番環境のMongoDBでは障害に対応できるよう複数DBサーバ構成にする。これによりデータ損失やサービス停止を防ぐ。そして負荷分散にもつながるレプリケーションについて学んでいこう。 MongoDB では複数のMongoDBをまとめてReplica Set と呼ぶ。

Hello, ReplicaSet

ローカル環境で Replica Set 構成を作ってみる。 MongoDB 2.4.7 mac 環境では /data/db//usr/local/var/log/mongodb/のパーミッションを変えてmongod実行ユーザが書き込めるようにしておく。

ちなみに、MongoDB2.4 からは ReplSetTest コマンドを有効にするには、 mongodコマンドに --setParameter enableTestCommands=1付与する必要があるらしい。ということで、デーモンで既に起動していた mongod を一旦ストップし、このパラメータを付けた状態で再起動した。

ちなみにMac でinit.d的なのは、/Library/LaunchDaemons以下にあった。ここのxmlを読み込んで起動時にmongodが起動されている状態になっていた模様。

以下Mac の場合の手順

sudo launchctl stop org.mongo.mongod
sudo /usr/local/Cellar/mongodb/2.4.7/mongod --dbpath /usr/local/var/mongodb/ --logpath /usr/local/var/log/mongodb/mongodb.log --config /usr/local/etc/mongod.conf --setParameter enableTestCommands=1

mongod が起動したのでいよいよReplica Set を組む。

mongo --nodb
replicaSet = new ReplSetTest({"nodes" : 3})
replicaSet.startSet()
replicaSet.initiate()
{
    "replSetInitiate" : {
        "_id" : "testReplSet",
        "members" : [
            {
                "_id" : 0,
                "host" : "DevApp.local:31000"
            },
            {
                "_id" : 1,
                "host" : "DevApp.local:31001"
            },
            {
                "_id" : 2,
                "host" : "DevApp.local:31002"
            }
        ]
    }
}

他のターミナルで ps aux | grep mongo すると

/usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31000 
/usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31001
/usr/local/Cellar/mongodb/2.4.7/mongod --oplogSize 40 --port 31002 

とあるのでどうやらローカルで3台構成になった模様だ。試しに繋いでみる

mongo
MongoDB shell version: 2.4.7
connecting to: test

> conn1 = new Mongo('localhost:31000')
connection to localhost:31000

> t = conn1.getDB('test')
test
> t.isMaster()
{
    "setName" : "testReplSet",
    "ismaster" : true,
    "secondary" : false,
    "hosts" : [
        "DevApp.local:31000",
        "DevApp.local:31002",
        "DevApp.local:31001"
    ],
    "primary" : "DevApp.local:31000",
    "me" : "DevApp.local:31000",
    "maxBsonObjectSize" : 16777216,
    "maxMessageSizeBytes" : 48000000,
    "localTime" : ISODate("2014-01-18T05:00:13.905Z"),
    "ok" : 1
}

ふむ。今回のReplica Setでは31000がmasterとして認識しているようだ。これでデータを入れると、他の salve にデータがレプリケーションされているはずだ。

> for(i = 0; i < 100; i++) { t.coll.insert({count: i}); }
> t.coll.count()
100

> conn2 = new Mongo('localhost:31001')
connection to localhost:31001
> t2 = conn2.getDB('test')
test
> t2.coll.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

これは slave の古いデータの読み込みを防ぐためにデフォルトでSlaveへのリクエストを拒否しているために起きているもの。これをOKにするには setSlaveOk() を実行する。

> conn2.setSlaveOk()
> t2.coll.find()
{ "_id" : ObjectId("52da0c23baa041eafb7c9878"), "count" : 0 }
{ "_id" : ObjectId("52da0c23baa041eafb7c9879"), "count" : 1 }
{ "_id" : ObjectId("52da0c23baa041eafb7c987a"), "count" : 2 }
{ "_id" : ObjectId("52da0c23baa041eafb7c987b"), "count" : 3 }
{ "_id" : ObjectId("52da0c23baa041eafb7c987c"), "count" : 4 }
{ "_id" : ObjectId("52da0c23baa041eafb7c987d"), "count" : 5 }

おお〜!2台目にデータがレプリケーションされていることを確認できた! slave にデータをinsertすることはできないので注意。slave は全ての書き込みを拒否する。さて、こっからがレプリケーションの本領発揮。 master をシャットダウンしてみよう。

t.adminCommand({'shutdown': 1})

conn3 = new Mongo('localhost:31002')
t3 = conn3.getDB('test')
t3.isMaster()
{
    "setName" : "testReplSet",
    "ismaster" : true,
        .....
}

今回は3台目がmasterとして役割が切り替わった。これが自動フェイルオーバというものになる。 ちなみにparimary と master そして slave と secondary は同じ意味。

Replica Set を停止するには rs.stopSet() でOK.

学び

  • クライアントは1台のmongodのときと同じオペレーションでprimaryへコマンドを送ることができる。
  • client は slave への書き込みはできない。
  • デフォルトではクライアントはslaveの読み込みはできない。 setSlaveOk() することでslaveの読み込みが可能になる。

複数サーバ構成にしても同じことだ。

今回はここまで。続きは細かい設定やサーバ構成について書いてまとめてみる予定。