ローカル環境だけでReplica SetとSharding構成を構築
試したいことがいろいろあったので、Windows7のローカル環境だけでReplica SetとSharding構成を構築してみた。MacやLinuxでも基本的には一緒なはずです。
構築したのは以下の環境。利用した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が検知されないケースがあった。

