読者です 読者をやめる 読者になる 読者になる

aspec7's garage

エンジニア生活の中で学んだことの備忘録

MongoDBでレプリケーション その2

MongoDB

前回レプリケーションの基本的な設定を確かめたので、今回はフェイルオーバーについて調べました。

アービターがある場合の最低構成の場合は、下記のような感じでレプリカセットが実現されています。


アービターが、プライマリサーバを決定するので、プライマリが動作停止するとセカンダリサーバをプライマリに昇格させます。
この仕組みがあるので、アービターが仕事していれば自動フェイルオーバーが機能します。


では、アービターがダウンするとどうなってしまうのかを図にしてみたのが下記の図です。
アービターがダウンしただけの状態(①)では、サービスは停止されません。
この状態ではレプリカセットが機能しているので、プライマリもセカンダリも役割を果たします。
なので、このときアービターを復旧すれば問題ありません。
しかし、アービターがダウン中にプライマリがダウンする多重障害の場合(②)は問題が生じます。


アービターがダウンしている最中にプライマリがダウンすると、プライマリ選択する役割がいない状態なので、フェイルオーバーが出来ません。
つまりセカンダリだけの状態になってしまいます。
こうなってしまうと、書き込みが出来ない読み取り専用の状態になってしまうのでサービス継続が難しくなります。

実際に動きを見てみます。
3つのノードが動いている事を確認します。
この例では、ポート番号が30000がセカンダリ、30001がプライマリ、30002がアービターです。

$ ps -ax|grep mongod
16959 ?? 0:40.91 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db2/ --replSet=myrep --port=30001 --fork --logpath /Users/hoge/mongodb/mongodb30001.log --logappend --auth --keyFile /Users/hoge/mongodb/db2.key --config /usr/local/etc/mongod.conf
18833 ?? 0:03.87 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db3/ --replSet=myrep --port=30002 --fork --logpath /Users/hoge/mongodb/mongodb30002.log --logappend --auth --keyFile /Users/hoge/mongodb/db3.key --config /usr/local/etc/mongod.conf
18858 ?? 0:03.93 /usr/local/Cellar/mongodb/2.4.5/mongod --dbpath=db1/ --replSet=myrep --port=30000 --fork --logpath /Users/hoge/mongodb/mongodb30000.log --logappend --auth --keyFile /Users/hoge/mongodb/db1.key --config /usr/local/etc/mongod.conf


まず、アービターをダウンさせます。

$ kill 18833


DBの状態を確認します。

myrep:SECONDARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-21T05:37:17Z"),
"myState" : 2,
"syncingTo" : "localhost:30001",
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 932,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 930,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"lastHeartbeat" : ISODate("2013-07-21T05:37:16Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:17Z"),
"pingMs" : 0
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"lastHeartbeat" : ISODate("2013-07-21T05:37:16Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:08Z"),
"pingMs" : 0
}
],
"ok" : 1
}


アービターがダウンしていても、プライマリとセカンダリが機能している事がわかります。

続けて、プライマリをダウンさせます。

$ kill 16959


DBの状態を確認します。

myrep:SECONDARY> rs.status()
{
"set" : "myrep",
"date" : ISODate("2013-07-21T05:37:42Z"),
"myState" : 2,
"syncingTo" : "localhost:30001",
"members" : [
{
"_id" : 0,
"name" : "localhost:30000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 957,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"errmsg" : "db exception in producer: 10278 dbclient error communicating with server: localhost:30001",
"self" : true
},
{
"_id" : 1,
"name" : "localhost:30001",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : Timestamp(1374384014, 1),
"optimeDate" : ISODate("2013-07-21T05:20:14Z"),
"lastHeartbeat" : ISODate("2013-07-21T05:37:42Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:31Z"),
"pingMs" : 0
},
{
"_id" : 2,
"name" : "localhost:30002",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"lastHeartbeat" : ISODate("2013-07-21T05:37:42Z"),
"lastHeartbeatRecv" : ISODate("2013-07-21T05:37:08Z"),
"pingMs" : 0
}
],
"ok" : 1
}


プライマリがダウンしましたが、アービターもダウンしているので、セカンダリはそのままです。
この状態ではデータは検索出来ますが、更新処理が行えません。

myrep:SECONDARY> db.getMongo().setSlaveOk()
myrep:SECONDARY> db.sample.insert({no:2,value:100})
not master
myrep:SECONDARY> db.sample.find({no:1})
{ "_id" : ObjectId("51eb6f2a3cb9ecfa7cfa46ad"), "no" : 1, "value" : 1 }


この状態で、アービターを復旧すればセカンダリはプライマリに昇格できます。

アービター構成の場合は、アービターのサービス停止には気をつけないといけませんね。