[小ネタ] MongoDB レプリカセットノードを切り離しスタンドアローンで起動する

こんにちは、菊池です。

今回は小ネタです。以前、MongoDBのレプリカセットパターンを紹介しました。

MongoDBのレプリカセットでは、AZ障害などにより過半数のノードで障害が発生すると、残存ノードでプライマリへの昇格が不可能になるため、書き込みを受け付けられなくなります。

MongoDB Enterprise s0:SECONDARY> rs.status()
{
       	"set" : "s0",
       	"date" : ISODate("2017-07-31T10:01:57.956Z"),
       	"myState" : 2,
       	"term" : NumberLong(13),
       	"heartbeatIntervalMillis" : NumberLong(2000),
       	"optimes" : {
       		"lastCommittedOpTime" : {
       			"ts" : Timestamp(0, 0),
       			"t" : NumberLong(-1)
       		},
       		"appliedOpTime" : {
       			"ts" : Timestamp(1501495292, 1),
       			"t" : NumberLong(13)
       		},
       		"durableOpTime" : {
       			"ts" : Timestamp(1501495292, 1),
       			"t" : NumberLong(13)
       		}
       	},
       	"members" : [
       		{
       			"_id" : 0,
       			"name" : "mongo0:27017",
       			"health" : 1,
       			"state" : 2,
       			"stateStr" : "SECONDARY",
       			"uptime" : 128,
       			"optime" : {
       				"ts" : Timestamp(1501495292, 1),
       				"t" : NumberLong(13)
       			},
       			"optimeDate" : ISODate("2017-07-31T10:01:32Z"),
       			"infoMessage" : "could not find member to sync from",
       			"configVersion" : 7,
       			"self" : true
       		},
       		{
       			"_id" : 1,
       			"name" : "mongo1:27017",
       			"health" : 0,
       			"state" : 8,
       			"stateStr" : "(not reachable/healthy)",
       			"uptime" : 0,
       			"optime" : {
       				"ts" : Timestamp(0, 0),
       				"t" : NumberLong(-1)
       			},
       			"optimeDurable" : {
       				"ts" : Timestamp(0, 0),
       				"t" : NumberLong(-1)
       			},
       			"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
       			"optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
       			"lastHeartbeat" : ISODate("2017-07-31T10:01:57.618Z"),
       			"lastHeartbeatRecv" : ISODate("2017-07-31T10:01:27.077Z"),
       			"pingMs" : NumberLong(0),
       			"lastHeartbeatMessage" : "Connection refused",
       			"configVersion" : -1
       		},
       		{
       			"_id" : 2,
       			"name" : "mongo2:27017",
       			"health" : 0,
       			"state" : 8,
       			"stateStr" : "(not reachable/healthy)",
       			"uptime" : 0,
       			"optime" : {
       				"ts" : Timestamp(0, 0),
       				"t" : NumberLong(-1)
       			},
       			"optimeDurable" : {
       				"ts" : Timestamp(0, 0),
       				"t" : NumberLong(-1)
       			},
       			"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
       			"optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
       			"lastHeartbeat" : ISODate("2017-07-31T10:01:57.642Z"),
       			"lastHeartbeatRecv" : ISODate("2017-07-31T10:01:05.867Z"),
       			"pingMs" : NumberLong(0),
       			"lastHeartbeatMessage" : "Connection refused",
       			"configVersion" : -1
       		}
       	],
       	"ok" : 1
}

ここの状況で書き込みを可能にするためには、残存ノードのうち1つをレプリカセットから切り離し、スタンドアローンで起動します。

設定ファイルのレプリカセットの部分をコメントアウトし、再起動します。

$ cat /etc/mongod.conf
net:
  port: 27017
 
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
 
storage:
  dbPath: /data
 
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb/mongod.pid
#replication:
#  replSetName: s0
$ sudo systemctl restart mongod

この状態では、書き込みが可能になります。

$ mongo
MongoDB shell version v3.4.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.6
MongoDB Enterprise > db.user.insert({name:"hoge"});
WriteResult({ "nInserted" : 1 })

まとめ

以上です。

二重障害などでPrimary昇格が不能になった際に、緊急でサービスを復旧させる必要がある場合にはこのような手段もあります。ただし、分断された過半数側のノードでも書き込みが発生してしまうと、不整合な状態となりますので注意しましょう。