概要
MongoDBのレプリカセットを組んで検証する必要があったのでdockerを使って構築することにしてみました。
環境
- Docker 1.12.0
- Mongo 3.2.9
構成
コンテナ名 | 役割 | ホスト側ポート |
---|---|---|
mongo1 | Primary | 30001 |
mongo2 | Secondary | 30002 |
arbiter | Arbiter。データは持たない | 30011 |
以下の様な形です。
ref: https://docs.mongodb.com/manual/core/replica-set-architecture-three-members/
準備
mongoの最新イメージの取得
$ docker pull mongo
ネットワークの構築をします。default networkでもいいですが名前解決をしてくれる用に別途ネットワークを用意します。
$ docker network create my-mongo-cluster
確認します。
$ docker network ls NETWORK ID NAME DRIVER SCOPE 6a5213fdac67 bridge bridge local d205b39c97f1 host host local ed7aae6ef398 my-mongo-cluster bridge local 3d8436389b6c none null local
構築
mongo1
$ docker run \ -p 30001:27017 \ --name mongo1 \ --net my-mongo-cluster \ -d \ mongo mongod --replSet my-mongo-set
mongo2
$ docker run \ -p 30002:27017 \ --name mongo2 \ --net my-mongo-cluster \ -d \ mongo mongod --replSet my-mongo-set
arbiter1
$ docker run \ -p 30011:27017 \ --name arbiter1 \ --net my-mongo-cluster \ -d \ mongo mongod --replSet my-mongo-set
設定
mongo1にアクセスします。
$ docker exec -it mongo1 mongo
設定を用意します。
> config = {_id: 'my-mongo-set', members: [ {_id: 0, host: 'mongo1:27017'}, {_id: 1, host: 'mongo2:27017'}, {_id: 2, host: 'arbiter1:27017', arbiterOnly: true}, ] }
反映します。
> rs.initiate(config) { "ok" : 1 }
以下のように設定されます。
my-mongo-set:PRIMARY> rs.status() { "set" : "my-mongo-set", "date" : ISODate("2016-08-23T04:26:30.371Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "mongo1:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1042, "optime" : { "ts" : Timestamp(1471926384, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2016-08-23T04:26:24Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1471926383, 1), "electionDate" : ISODate("2016-08-23T04:26:23Z"), "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "mongo2:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 17, "optime" : { "ts" : Timestamp(1471926384, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2016-08-23T04:26:24Z"), "lastHeartbeat" : ISODate("2016-08-23T04:26:29.931Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T04:26:27.256Z"), "pingMs" : NumberLong(0), "syncingTo" : "mongo1:27017", "configVersion" : 1 }, { "_id" : 2, "name" : "arbiter1:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 17, "lastHeartbeat" : ISODate("2016-08-23T04:26:29.931Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T04:26:30.256Z"), "pingMs" : NumberLong(0), "configVersion" : 1 } ], "ok" : 1 }
同期の確認
データを入れてみます。
my-mongo-set:PRIMARY> use mydb my-mongo-set:PRIMARY> for(var i=0; i<10000; i++) db.logs.insert( { "uid":i, "value":Math.floor(Math.random()*10000+1) } )
Secondaryに同期されているか確認します。
$ docker exec -it mongo2 mongo
my-mongo-set:SECONDARY> use mydb my-mongo-set:SECONDARY> db.getMongo().setSlaveOk() my-mongo-set:SECONDARY> db.logs.count() 10000
大丈夫ですね。
db.getMongo().setSlaveOk()
はプライマリ以外のノードからの読み込みを許可する設定です。これをしていないと以下のエラーが出ます。
2016-08-23T04:55:36.331+0000 E QUERY [thread1] Error: count failed: { "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } :
フェイルオーバーの確認
プライマリを落とします。
$ docker stop mongo1
mongo2に入ってみます。
$ docker exec -it mongo2 mongo
statusを確認します。
my-mongo-set:SECONDARY> rs.status() { "set" : "my-mongo-set", "date" : ISODate("2016-08-23T04:59:15.672Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "mongo1:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2016-08-23T04:59:15.266Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T04:59:01.642Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "HostUnreachable", "configVersion" : -1 }, { "_id" : 1, "name" : "mongo2:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 2990, "optime" : { "ts" : Timestamp(1471928353, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2016-08-23T04:59:13Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1471928352, 1), "electionDate" : ISODate("2016-08-23T04:59:12Z"), "configVersion" : 1, "self" : true }, { "_id" : 2, "name" : "arbiter1:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1980, "lastHeartbeat" : ISODate("2016-08-23T04:59:14.602Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T04:59:10.851Z"), "pingMs" : NumberLong(0), "configVersion" : 1 } ], "ok" : 1 } my-mongo-set:PRIMARY>
mongo2がPRIMARY
になっていました。
ここでmongo1を戻してみます。
$ docker start mongo1
構成がどうなるか確認します。
my-mongo-set:SECONDARY> rs.status() { "set" : "my-mongo-set", "date" : ISODate("2016-08-23T05:00:48.908Z"), "myState" : 2, "term" : NumberLong(2), "syncingTo" : "mongo2:27017", "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "mongo1:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 11, "optime" : { "ts" : Timestamp(1471928353, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2016-08-23T04:59:13Z"), "syncingTo" : "mongo2:27017", "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "mongo2:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 10, "optime" : { "ts" : Timestamp(1471928353, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2016-08-23T04:59:13Z"), "lastHeartbeat" : ISODate("2016-08-23T05:00:47.977Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T05:00:48.890Z"), "pingMs" : NumberLong(1), "electionTime" : Timestamp(1471928352, 1), "electionDate" : ISODate("2016-08-23T04:59:12Z"), "configVersion" : 1 }, { "_id" : 2, "name" : "arbiter1:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 10, "lastHeartbeat" : ISODate("2016-08-23T05:00:47.977Z"), "lastHeartbeatRecv" : ISODate("2016-08-23T05:00:48.006Z"), "pingMs" : NumberLong(0), "configVersion" : 1 } ], "ok" : 1 }
mongo2がPRIMARY
、mongo1がSECONDARY
で稼働するようになりました。