matsukaz's blog

Agile, node.js, ruby, AWS, cocos2d-xなどなどいろいろやってます

ローカル環境だけでReplica SetとSharding構成を構築

試したいことがいろいろあったので、Windows7のローカル環境だけでReplica SetとSharding構成を構築してみた。MacLinuxでも基本的には一緒なはずです。
構築したのは以下の環境。利用したMongoDBのバージョンは1.8.1。1.6.5でも同じ動作しました。

  • Config Server × 1
    • システム管理プロセス
    • 設定情報(Sharding情報/mongosプロセス情報)管理やシステム管理を行う
    • 1台(テスト環境)、または3台(本番環境)が推奨される
  • mongos Router × 2
    • ルーティングプロセス
    • クライアントに対してはmongodのように振る舞い、Shardingによるmongodの分散構成を隠蔽する。
    • 2台以上置くことでSPOFを無くすことが推奨される
  • mongod Server × 4
    • 実データを保持するプロセス
    • 1台はReplicaSetなし。残り3台は1セットでReplica Set構成(PRIMARY/SECONDARY/SECONDARY)
    • mongodでReplica Set構成とそうじゃないものを混在させてるのは、単に試したかったから。通常はこんなことしない。テスト環境はReplica Setなし、本番環境は3台1セットのReplica Set構成を横に並べるのが一般的。

以下、作業手順です。MONGODB_HOMEがMongoDBのインストール先のディレクトリ。


Config Serverの起動

MONGODB_HOME/data/mongocディレクトリを作成。
以下のコマンドでConfig Serverを起動。

> MONGODB_HOME/bin/mongod.exe --port 28000 --configsvr --dbpath MONGODB_HOME/data/config

mongos Routerの起動

以下のコマンドで1台目のmongosを起動。chunkサイズはテスト用に1(MB)に設定。

> MONGODB_HOME/bin/mongos.exe --port 28101 --chunkSize 1 --configdb localhost:28000

同様に2台目のmongosを起動。

> MONGODB_HOME/bin/mongos.exe --port 28102 --chunkSize 1 --configdb localhost:28000

mongod Serverの起動

MONGODB_HOME/data/s1ディレクトリを作成
MONGODB_HOME/data/s1_r1ディレクトリを作成(ReplicaSetの1台目)
MONGODB_HOME/data/s1_r2ディレクトリを作成(ReplicaSetの2台目)
MONGODB_HOME/data/s1_r3ディレクトリを作成(ReplicaSetの3台目)

以下のコマンドで全てのmongodを起動。--replSetを付けるとReplica Set前提で起動される。--restは、Web管理画面でReplica Setの状態を見れるようにするためのオプション。

> MONGODB_HOME/bin/mongod.exe --port 28201 --dbpath MONGODB_HOME/data/s1 --rest
> MONGODB_HOME/bin/mongod.exe --port 28211 --replSet s2 --dbpath MONGODB_HOME/data/s2_r1 --rest
> MONGODB_HOME/bin/mongod.exe --port 28212 --replSet s2 --dbpath MONGODB_HOME/data/s2_r2 --rest
> MONGODB_HOME/bin/mongod.exe --port 28213 --replSet s2 --dbpath MONGODB_HOME/data/s2_r3 --rest

Replica Setを設定したmongodは、以下の警告が出続ける。

[startReplSets] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)

以下のコマンドで、Replica Setの1台目に接続する。

> MONGODB_HOME/bin/mongo.exe localhost:28211

Replica Setを初期化する。

> config = {
	_id: 's2', members: [
		{_id: 0, host: 'localhost:28211'},
		{_id: 1, host: 'localhost:28212'},
		{_id: 2, host: 'localhost:28213'}
	]
}
> rs.initiate(config);
{
	"info" : "Config now saved locally.  Should come online in about a minute.",
	"ok" : 1
}

Replica Setの状態を確認する。

> rs.status();
{
        "set" : "s2",
        "date" : ISODate("2011-04-17T13:51:19Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:28211",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "optime" : {
                                "t" : 1303048268000,
                                "i" : 1
                        },
                        "optimeDate" : ISODate("2011-04-17T13:51:08Z"),
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "localhost:28212",
                        "health" : 1,
                        "state" : 5,
                        "stateStr" : "STARTUP2",
                        "uptime" : 2,
                        "optime" : {
                                "t" : 0,
                                "i" : 0
                        },
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2011-04-17T13:51:17Z"),
                        "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync"
                },
                {
                        "_id" : 2,
                        "name" : "localhost:28213",
                        "health" : 0,
                        "state" : 6,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : {
                                "t" : 0,
                                "i" : 0
                        },
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2011-04-17T13:51:17Z"),
                        "errmsg" : "still initializing"
                }
        ],
        "ok" : 1
}

"errmsg" : "still initializing" や "errmsg" : "initial sync need a member to be primary or secondary to do our initial sync" が含まれていた場合は、まだ初期化中。表示されなくなるまで待つ。

mongodの状態は、以下のWeb管理画面でも閲覧可能(起動時のポート番号+1000)。

Replica Setが正しく動作したら、PRIMARYとなっているmongodを落としたり(SECONDARYがPRIMARYに昇格する)、mongodを2台落としたりといった動作確認が出来る。



Shardingを設定

いずれかのmongosに接続する。

> MONGODB_HOME/bin/mongo.exe localhost:28101

adminに接続し、addshardコマンドを実行する。

> use admin
> db.runCommand({addshard:"localhost:28201"});
{ "shardAdded" : "shard0000", "ok" : 1 }

mongosのコンソールには、以下のメッセージが表示される。

[conn1] going to add shard: { _id: "shard0000", host: "localhost:28201" }

同様に、Replica SetとなっているmongodもShardに追加する。*1

> db.runCommand({addshard:"s2/localhost:28211,localhost:28212,localhost:28213"});
{ "shardAdded" : "s2", "ok" : 1 }

mongosのコンソールには、以下のメッセージが表示される。

[conn1] updated set (s2) to: s2/localhost:28211,localhost:28213
[conn1] updated set (s2) to: s2/localhost:28211,localhost:28213,localhost:28212
[ReplicaSetMonitorWatcher] starting
[conn1] going to add shard: { _id: "s2", host: "s2/localhost:28211" }

Shardingの状態を確認する。

> db.printShardingStatus();
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:28201" }
      {
        "_id" : "s2",
        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
}
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }

> db.runCommand({listshards:1});
{
        "shards" : [
                {
                        "_id" : "shard0000",
                        "host" : "localhost:28201"
                },
                {
                        "_id" : "s2",
                        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
                }
        ],
        "ok" : 1
}

テストデータを入れてみる

mongosに接続する。

> MONGODB_HOME/bin/mongo.exe localhost:28101

hogeというDBをSharding可能にする。

> use admin
> db.runCommand({enablesharding:"hoge"});
{ "ok" : 1 }
> db.printShardingStatus();
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:28201" }
      {
        "_id" : "s2",
        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
}
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
        { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
        { "_id" : "hoge", "partitioned" : true, "primary" : "shard0000" }

userというコレクションをSharding対象とする.

> db.runCommand({shardcollection:"hoge.user",  key:{name: 1}, unique:true});
{ "collectionsharded" : "hoge.user", "ok" : 1 }
> db.printShardingStatus();
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:28201" }
      {
        "_id" : "s2",
        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
}
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
        { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
        { "_id" : "hoge", "partitioned" : true, "primary" : "shard0000" }
                hoge.user chunks:
                                shard0000       1
                        { "name" : { $minKey : 1 } } -->> { "name" : { $maxKey : 1 } } on : shard0000 { "t" : 1000, "i" : 0 }

hogeに接続し、テストデータを投入する。ここでは30万件適当なデータを挿入する。コンソール上ではJavaScriptを実行できるため、for文でぐるぐる回す。

> use hoge
> for(i=0;i<300000;i++){
	db.user.save({name:"foo"+i, age:20, comment:"This is sample user for MongoDB testing. User name is foo"+i+"."});
}
> db.printShardingStatus();
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:28201" }
      {
        "_id" : "s2",
        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
}
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
        { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
        { "_id" : "hoge", "partitioned" : true, "primary" : "shard0000" }
                hoge.user chunks:
                                s2      4
                                shard0000       17
                        too many chunksn to print, use verbose if you want to force print
}

しばらく経ってから再度printShardingStatus()を実行すると、いくつかのchunkが移動していることが確認出来る(printShardingStatus()は引数にtrueを渡すことで、全てのchunkの状態を返すようになる)。

> db.printShardingStatus(true);
--- Sharding Status ---
  sharding version: { "_id" : 1, "version" : 3 }
  shards:
      { "_id" : "shard0000", "host" : "localhost:28201" }
      {
        "_id" : "s2",
        "host" : "s2/localhost:28211,localhost:28213,localhost:28212"
}
  databases:
        { "_id" : "admin", "partitioned" : false, "primary" : "config" }
        { "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
        { "_id" : "hoge", "partitioned" : true, "primary" : "shard0000" }
                hoge.user chunks:
                                s2      10
                                shard0000       15
                        { "name" : { $minKey : 1 } } -->> { "name" : "foo0" } on : s2 { "t" : 2000, "i" : 0 }
                        { "name" : "foo0" } -->> { "name" : "foo136" } on : s2 { "t" : 3000, "i" : 0 }
                        { "name" : "foo136" } -->> { "name" : "foo145" } on : s2 { "t" : 4000, "i" : 0 }
                        { "name" : "foo145" } -->> { "name" : "foo1540" } on : s2 { "t" : 5000, "i" : 0 }
                        { "name" : "foo1540" } -->> { "name" : "foo1630" } on : s2 { "t" : 6000, "i" : 0 }
                        { "name" : "foo1630" } -->> { "name" : "foo17200" } on : s2 { "t" : 7000, "i" : 0 }
                        { "name" : "foo17200" } -->> { "name" : "foo181004" } on : s2 { "t" : 8000, "i" : 0 }
                        { "name" : "foo181004" } -->> { "name" : "foo19001" } on : s2 { "t" : 9000, "i" : 0 }
                        { "name" : "foo19001" } -->> { "name" : "foo199015" } on : s2 { "t" : 10000, "i" : 0 }
                        { "name" : "foo199015" } -->> { "name" : "foo20802" } on : s2 { "t" : 11000, "i" : 0 }
                        { "name" : "foo20802" } -->> { "name" : "foo22603" } on : shard0000 { "t" : 11000, "i" : 2 }
                        { "name" : "foo22603" } -->> { "name" : "foo24404" } on : shard0000 { "t" : 11000, "i" : 3 }
                        { "name" : "foo24404" } -->> { "name" : "foo28006" } on : shard0000 { "t" : 1000, "i" : 15 }
                        { "name" : "foo28006" } -->> { "name" : "foo3257" } on : shard0000 { "t" : 1000, "i" : 16 }
                        { "name" : "foo3257" } -->> { "name" : "foo36171" } on : shard0000 { "t" : 2000, "i" : 2 }
                        { "name" : "foo36171" } -->> { "name" : "foo39774" } on : shard0000 { "t" : 2000, "i" : 4 }
                        { "name" : "foo39774" } -->> { "name" : "foo43375" } on : shard0000 { "t" : 2000, "i" : 6 }
                        { "name" : "foo43375" } -->> { "name" : "foo46978" } on : shard0000 { "t" : 2000, "i" : 8 }
                        { "name" : "foo46978" } -->> { "name" : "foo50579" } on : shard0000 { "t" : 2000, "i" : 12 }
                        { "name" : "foo50579" } -->> { "name" : "foo5835" } on : shard0000 { "t" : 2000, "i" : 13 }
                        { "name" : "foo5835" } -->> { "name" : "foo61951" } on : shard0000 { "t" : 2000, "i" : 14 }
                        { "name" : "foo61951" } -->> { "name" : "foo65553" } on : shard0000 { "t" : 2000, "i" : 16 }
                        { "name" : "foo65553" } -->> { "name" : "foo69155" } on : shard0000 { "t" : 2000, "i" : 18 }
                        { "name" : "foo69155" } -->> { "name" : "foo9999" } on : shard0000 { "t" : 2000, "i" : 19 }
                        { "name" : "foo9999" } -->> { "name" : { $maxKey : 1 } } on : shard0000 { "t" : 1000, "i" : 4 }

*1:ReplicaSetの場合は、含まれるmongodを全て指定した方が良い。ドキュメント上は一つで良いことになっているが、他のmongodが検知されないケースがあった。