mongoDBのレプリカセットを構築してRails+mongoidで接続する


# mongoDBのレプリカセット

## 前提

- AWS EC2 AmazonLinux
- mongoDB 2.6

mongoDBでレプリケーションを作ろうと思ったら、超絶簡単だったので作業メモ。今回は3台構成で作ってみた。AWSでEC2インスタンスを3台使用。プライマリとセカンダリとアービター。

![Untitled (1).png](https://qiita-image-store.s3.amazonaws.com/0/2152/693a3315-dd14-8dfe-c822-14422fbda4d8.png "Untitled (1).png")

## Arbiter（アービター）とは

レプリカセットのプライマリサーバが障害で通信できなくなってしまった時に、どのデータベースをプライマリにするかを決定する役目。Arbiterサーバ自身はデータを持たず、単純に投票権だけを所持している。そのため、サーバスペックは低くても構わない。今回はEC2のt2.microで構成。

# mongoDBのインストール＆起動

AWS AmazonLinuxのパッケージの場合。公式のマニュアル通り。

```/etc/yum.repos.d/mongodb-org-2.6.repo
[mongodb-org-2.6]
name=MongoDB 2.6 Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1
```

```
$ sudo yum install -y mongodb-org
$ sudo service mongod start
$ sudo chkconfig mongod on
```

- 参照 http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/

## レプリカの設定

mongoDBサーバ動詞で接続ができるように設定をする。なお、mongoDBのEC2インスタンス自体はAWS VPCのセキュリティ設定でWANからはアクセスされないように設定が必要。グローバルIPが付与されている場合は、簡単に接続されてしまうので注意。

また、レプリカセットの名前を指定する。今回だと3台全て同じreplSet名にする。

```
$ sudo vi /etc/mongod.conf
```

```/etc/mongod.conf
bind_ip=127.0.0.1 
↓
# コメントアウト
# bind_ip=127.0.0.1 

# レプリカセットの名前を追加（全mongoDB共通にする）
replSet=hogeRepl
```

## プライマリ・セカンダリ・アービターの設定

どこか適当なmongoDBにログインし、下記の設定を書き込む。

```mongodb
> config = {_id: 'hogeRepl',
    members: [
      {_id: 0, host: '172.31.100.10:27017'},
      {_id: 1, host: '172.31.100.11:27017'},
      {_id: 2, host: '172.31.100.12:27017', arbiterOnly: true},
    ]
  }

> rs.initiate(config)
```

これだけで、レプリカセットの設定は完了。

### レプリカの確認

今のレプリカセットの状態を確認する。

```mongodb
> rs.status()
{
	"set" : "hogeRepl",
	"date" : ISODate("2015-04-08T00:17:48Z"),
	"myState" : 1,
	"members" : [
		{
			"_id" : 0,
			"name" : "172.31.100.10:27017",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 1631128,
			"optime" : Timestamp(1428452138, 2),
			"optimeDate" : ISODate("2015-04-08T00:15:38Z"),
			"electionTime" : Timestamp(1426821293, 1),
			"electionDate" : ISODate("2015-03-20T03:14:53Z"),
			"self" : true
		},
		{
			"_id" : 1,
			"name" : "172.31.100.11:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 1630985,
			"optime" : Timestamp(1428452138, 2),
			"optimeDate" : ISODate("2015-04-08T00:15:38Z"),
			"lastHeartbeat" : ISODate("2015-04-08T00:17:47Z"),
			"lastHeartbeatRecv" : ISODate("2015-04-08T00:17:47Z"),
			"pingMs" : 0,
			"syncingTo" : "172.31.100.10:27017"
		},
		{
			"_id" : 2,
			"name" : "172.31.100.12:27017",
			"health" : 1,
			"state" : 7,
			"stateStr" : "ARBITER",
			"uptime" : 1630983,
			"lastHeartbeat" : ISODate("2015-04-08T00:17:48Z"),
			"lastHeartbeatRecv" : ISODate("2015-04-08T00:17:48Z"),
			"pingMs" : 0
		}
	],
	"ok" : 1
}
```

### セカンダリからデータ閲覧

セカンダリにデータが同期されているか確認するために、セカンダリmongodbに入ってクエリを叩くと以下の様なエラーが出る。

```mongodb
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
```

```mongodb
> db.getMongo().setSlaveOk()
# もしくは
> rs.slaveOk()
# 元に戻すには
> db.getMongo().slaveOk = false
```

ただ、これを設定すると、間違ってセカンダリーで更新処理を行ってしまったりするので運用時はマスターからのみクエリ発行できるようにするのが良い。


# mongoidの設定

mongoidの設定は簡単で、プライマリ・セカンダリのホスト名を列挙し、read: primaryでプライマリから読み込むようにするだけ。

```/config/mongoid.yml
production:
  sessions:
    default:
      database: hoge
      hosts:
        - prd-mongo1.hoge.vpc:27017
        - prd-mongo2.hoge.vpc:27017
      options:
        max_retries: 30
        retry_interval: 1
        read: primary
  options:
```