2012年1月23日月曜日

MongoDB 環境構築(その1: Replica Set)。

今年明けてすぐに gmail アカウントが乗っ取られるという事態になり復旧するまで google アカウントに紐づいたサービスが全て使用不可となってしまった。
セキュリティは大事だと思った。復旧後すぐに二段階認証を導入しました。


MongoDB の環境構築をする必要に迫らてしばらく MongoDB の勉強をしてみたので記録を残す事にする。
とりあえず3回くらいに分けて下記の順序で環境構築を実施してみたいと思う。
  1. Replica Set
  2. Sharding
  3. Replica Set + Sharding
今回の目標: 3台構成の Replica Set 構築

MongoDB の Replica Set は2台以上のサーバで構成される master/slave の拡張(的な理解)である。Replica Set はフェイルオーバ機能を備えており、Primary サーバが停止した事が検知されると自動的に Secondary サーバが Primary へと昇格する事で障害時にもサービスを自動的に継続し続ける事が可能となる。

フェイルオーバ機能



Replica Set を構成する環境において Primary サーバが停止すると、残りの Secondary サーバの投票によって1つのサーバが選出される事で新たな Primary サーバが生成されるが、その際に特定の状況(ex. 2台構成の Replica Set)で新たな Primary が選出されない事を防ぐ為に投票専用のサーバである Arbiter を設置する事で Primary の不選出を回避する事が可能となる。

以下では3台構成の Replica Set において a) Arbiter を設置しない / b) Arbiter を設置する という2つのパターンを試してみる事とする。

*複数サーバ環境が無いためローカルのPC1台のサービス毎にそれぞれポート番号を割り振る事で仮想的なサーバ環境を構築する事とする

a) Arbiter なし

まずは Arbiter なしの3台構成で Replica Set を構築してみる。


Replica Set 用のサーバは mongod コマンドに引数 --replSet <ReplicaSet名> を追加して起動すればよい。
今回は Replica Set 名として "repl01" を使用する。

サーバを起動
// データ保存ディレクトリの作成
$ mkdir -p data/shrd11
$ mkdir data/shrd12
$ mkdir data/shrd13

// 下記3行はそれぞれ別ターミナルで実行
$ mongod --replSet repl01 --port 10001 --dbpath data/shrd11
$ mongod --replSet repl01 --port 10002 --dbpath data/shrd12
$ mongod --replSet repl01 --port 10003 --dbpath data/shrd13

サーバに接続して Replica Set の設定を実施
$ mongo localhost:10001
MongoDB shell version: 2.0.2
connecting to: localhost:10001/test

> rs.initiate()
{
 "info2" : "no configuration explicitly specified -- making one",
 "me" : "shimajiro.local:10001",
 "info" : "Config now saved locally.  Should come online in about a minute.",
 "ok" : 1
}

// Replica Set の準備が整った時点でシェルの表示が PRIMARY に変わる
PRIMARY>

// Replica Set の状態を確認し、"localhost:10001" が追加されている事を確認
PRIMARY> rs.status()
{
 "set" : "repl01",
 "date" : ISODate("2012-01-22T14:31:58Z"),
 "myState" : 1,
 "members" : [
  {
   "_id" : 0,
   "name" : "shimajiro.local:10001",
   "health" : 1,
   "state" : 1,
   "stateStr" : "PRIMARY",
   "optime" : {
    "t" : 1327242667000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T14:31:07Z"),
   "self" : true
  }
 ],
 "ok" : 1
}

// 残り2つのサーバを Replica Set に追加
PRIMARY> rs.add('shimajiro.local:10002')
PRIMARY> rs.add('shimajiro.local:10003')
PRIMARY> rs.status()
{
 "set" : "repl01",
 "date" : ISODate("2012-01-22T14:37:59Z"),
 "myState" : 1,
 "members" : [
  {
   "_id" : 0,
   "name" : "shimajiro.local:10001",
   "health" : 1,
   "state" : 1,
   "stateStr" : "PRIMARY",
   "optime" : {
    "t" : 1327243031000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T14:37:11Z"),
   "self" : true
  },
  {
   "_id" : 1,
   "name" : "shimajiro.local:10002",
   "health" : 1,
   "state" : 2,
   "stateStr" : "SECONDARY",
   "uptime" : 50,
   "optime" : {
    "t" : 1327243031000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T14:37:11Z"),
   "lastHeartbeat" : ISODate("2012-01-22T14:37:57Z"),
   "pingMs" : 0
  },
  {
   "_id" : 2,
   "name" : "shimajiro.local:10003",
   "health" : 1,
   "state" : 2,
   "stateStr" : "SECONDARY",
   "uptime" : 48,
   "optime" : {
    "t" : 1327243031000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T14:37:11Z"),
   "lastHeartbeat" : ISODate("2012-01-22T14:37:57Z"),
   "pingMs" : 0
  }
 ],
 "ok" : 1
}
これで Replica Set の構築は終了、簡単である。
とりあえず実際に Replica Set が機能するか確認してみる。

@localhost:10001
PRIMARY> use db1
PRIMARY> db.foo.insert({x: 1})
PRIMARY> db.foo.find()
{ "_id" : ObjectId("4f1c229b6c5f22c51c19f16c"), "x" : 1 }

@localhost:10002
$ mongo localhost:10002
MongoDB shell version: 2.0.2
connecting to: localhost:10002/test

// db1 データベースが複製されている事を確認
SECONDARY> show dbs
db1 0.203125GB
local 0.453125GB

// slave ではデータにアクセス出来ないらしい
SECONDARY> use db1
switched to db db1
SECONDARY> show collections
Sun Jan 22 23:55:42 uncaught exception: error: { "$err" : "not master and slaveok=false", "code" : 13435 }
SECONDARY> db.foo.find()
error: { "$err" : "not master and slaveok=false", "code" : 13435 }

次に Primary サーバを停止して他の Secondary サーバが Primary に昇格する事を確認。
// localhost:10001 を停止
^C

// localhost:10003 のサーバログを一部抜粋
Mon Jan 23 00:01:56 [rsHealthPoll] replSet member shimajiro.local:10001 is now in state DOWN
Mon Jan 23 00:01:56 [rsMgr] replSet info electSelf 2
Mon Jan 23 00:01:56 [rsMgr] replSet PRIMARY
どうやら localhost:10003 が Primary に昇格しているらしい
$ mongo localhost:10003
MongoDB shell version: 2.0.2
connecting to: localhost:10003/test

// データを確認
PRIMARY> use db1
switched to db db1
PRIMARY> db.foo.find()
{ "_id" : ObjectId("4f1c229b6c5f22c51c19f16c"), "x" : 1 }

上手くいっているようだ。

b) Arbiter あり

次に2台の mongod + 1台の Arbiter を構築する。


Arbiter は普通に Replica Set の一員として mongod を立ち上げてしまえば良い。
// data/shard1* ディレクトリの中身は削除済み
$ mongod --replSet repl01 --port 10001 --dbpath data/shrd11
$ mongod --replSet repl01 --port 10002 --dbpath data/shrd12
$ mongod --replSet repl01 --port 10003 --dbpath data/shrd13

// localhost:10001 に接続
$ mongo localhost:10001
MongoDB shell version: 2.0.2
connecting to: localhost:10001/test

// 今回は config オブジェクトを作成して初期化を実施
> config = {_id: 'repl01', members: [{_id: 0, host: 'localhost:10001'}, {_id: 1, host: 'localhost:10002'}]}
> rs.initiate(config)
{
 "info" : "Config now saved locally.  Should come online in about a minute.",
 "ok" : 1
}

// Arbiter を追加
PRIMARY> rs.addArb('localhost:10003');
{ "ok" : 1 }
PRIMARY> rs.status()
{
 "set" : "repl01",
 "date" : ISODate("2012-01-22T15:23:38Z"),
 "myState" : 1,
 "members" : [
  {
   "_id" : 0,
   "name" : "localhost:10001",
   "health" : 1,
   "state" : 1,
   "stateStr" : "PRIMARY",
   "optime" : {
    "t" : 1327245701000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T15:21:41Z"),
   "self" : true
  },
  {
   "_id" : 1,
   "name" : "localhost:10002",
   "health" : 1,
   "state" : 2,
   "stateStr" : "SECONDARY",
   "uptime" : 239,
   "optime" : {
    "t" : 1327245701000,
    "i" : 1
   },
   "optimeDate" : ISODate("2012-01-22T15:21:41Z"),
   "lastHeartbeat" : ISODate("2012-01-22T15:23:37Z"),
   "pingMs" : 0
  },
  {
   "_id" : 2,
   "name" : "localhost:10003",
   "health" : 1,
   "state" : 7,
   "stateStr" : "ARBITER",  // Arbiter として登録
   "uptime" : 117,
   "optime" : {
    "t" : 0,
    "i" : 0
   },
   "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
   "lastHeartbeat" : ISODate("2012-01-22T15:23:37Z"),
   "pingMs" : 0
  }
 ],
 "ok" : 1
}

// データを登録
PRIMARY> use db2
switched to db db2
PRIMARY> db.hoge.insert({y: 999})
localhost:10001 を停止させてみる。
// localhost:10001 を停止
^C

// localhost:10002 のサーバログを一部抜粋
Mon Jan 23 00:28:50 [rsHealthPoll] replSet member localhost:10001 is now in state DOWN
Mon Jan 23 00:28:50 [rsMgr] replSet info electSelf 1
Mon Jan 23 00:28:50 [rsMgr] replSet PRIMARY

$ mongo localhost:10002
MongoDB shell version: 2.0.2
connecting to: localhost:10002/test

PRIMARY> use db2
switched to db db2
PRIMARY> db.hoge.find()
{ "_id" : ObjectId("4f1c2aa4d9d3ce4872829a4a"), "y" : 999 }

停止させた localhost:10001 は再度起動させれば自動的に Replica Set の一員として復帰できる。
$ mongod --replSet repl01 --port 10001 --dbpath data/shrd11
ホントに楽である。

次は Sharding 環境の構築を予定。

0 件のコメント:

コメントを投稿