ローカル環境だけで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が検知されないケースがあった。