AWS上に実践的なMongoDBのレプリケーション構成を構築する手順をまとめます。
前回MongoDBインストール後に作成したAMIから、異なるAvailability ZoneにEC2を作成します。
ホスト名(mongo1、mongo2)を定義して、なるべくec2-userで操作します。
今回はReplicationの設定が中心です。Shardingの設定は別の機会に…。
OS: amazon-linux-ami/2014.03 (64-bit)
MongoDB: 2.6.4
■システム構成概要図と方針
mongo1サーバ(10.54.10.90) → port=30001:Primary(初期)
mongo2サーバ(10.54.30.152) → port=30002:Secondary(初期)、port=30003:Arbiter
ReplicaSet Name: psa ←名前は適当です。 Primary/Secondary/Arbiterの頭文字。
MongoDBを利用するアプリの都合ではりますが、フェイルオーバー時の差分更新を考慮して、Priorityは設定せず、PrimaryとSecondaryは随時入れ替わることを想定してディレクトリ構成はできるだけ同一とします。
まず、MongoDBをインストールしたAMIを作成し、先にSecondary/Arbiter側から設定します。次にPrimary側でreplication設定すると作業の流れがスムーズです。
******* Secondary/Arbiter *******
前回作成したAMIからmongo2サーバを作成。mongo1サーバとは別のAZを利用する。
replication設定は後ほどPrimaryを想定しているmongo1サーバで実施するため、Secondary/Arbiterの設定を先に進める。
■EC2概要
Instance: i-bb41xxx2 (mongo2)
Private IP: 10.54.30.152
Availability zone: ap-northeast-1c
OS: amazon-linux-ami/2014.03 (64-bit)
■ホスト名変更
[ec2-user@ip-10-54-30-152 ~]$ sudo cp -p /etc/sysconfig/network /etc/sysconfig/network.org
[ec2-user@ip-10-54-30-152 ~]$ sudo vi /etc/sysconfig/network
[ec2-user@ip-10-54-30-152 ~]$ diff /etc/sysconfig/network /etc/sysconfig/network.org
2c2
< HOSTNAME=mongo2
---
> HOSTNAME=localhost.localdomain
[ec2-user@ip-10-54-30-152 ~]$ sudo reboot
■hostsファイルの設定
[ec2-user@mongo2 ~]$ sudo vi /etc/hosts
[ec2-user@mongo2 ~]$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain
10.54.10.90 mongo1
10.54.30.152 mongo2
[ec2-user@mongo2 ~]$
■設定ファイル作成(初期SecondaryおよびArbiter)
※Arbiterは固定だが、Primary/Secondaryは入れ替わることを想定しているので、secondary.confのようなファイル名にはしない。
[ec2-user@mongo2 ~]$ vi /data/mongo_rs/conf/mongod.conf
[ec2-user@mongo2 ~]$ cat /data/mongo_rs/conf/mongod.conf
bind_ip=mongo2
port=30002
dbpath=/data/mongo_rs/db
pidfilepath=/var/run/mongodb/mongod.pid
logpath=/var/log/mongodb/mongod.log
logappend=true
fork=true
replSet=psa
[ec2-user@mongo2 ~]$ mkdir /data/mongo_rs/arb
[ec2-user@mongo2 ~]$ vi /data/mongo_rs/conf/arbiter.conf
[ec2-user@mongo2 ~]$ cat /data/mongo_rs/conf/arbiter.conf
bind_ip=mongo2
port=30003
dbpath=/data/mongo_rs/arb
pidfilepath=/var/run/mongodb/arbiter.pid
logpath=/var/log/mongodb/arbiter.log
logappend=true
fork=true
replSet=psa
[ec2-user@mongo2 ~]$
◆参考:ファイルディスクリプタ数上限値拡張
デフォルトの1024→2048に変更。
[ec2-user@mongo2 ~]$ ulimit -a | grep open; ulimit -n 2048; ulimit -a | grep open
open files (-n) 1024
open files (-n) 2048
[ec2-user@mongo2 ~]$
システムの規模に応じて上限値を拡張する。
MongoDBのオフィシャルサイトでは"64000"を推奨しているようですが、EC2インスタンスがしょぼいので今回は2048に設定してみます。
この値はログインしているシェルで保持されているため、恒久的な設定でないことに注意!
■mongod起動
## Secondary
[ec2-user@mongo2 ~]$ ulimit -n 2048; sudo mongod --config /data/mongo_rs/conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 1732
child process started successfully, parent exiting
[ec2-user@mongo2 ~]$
※ec2-userをmongodグループに所属させたり、pidファイルやlogファイルの権限を変えてみたが、ec2-userで起動すると失敗する。
mongodはrootで起動することを想定している??
## Arbiter
[ec2-user@mongo2 ~]$ ulimit -n 2048; sudo mongod --config /data/mongo_rs/conf/arbiter.conf
about to fork child process, waiting until server is ready for connections.
forked process: 17768
child process started successfully, parent exiting
[ec2-user@mongo2 ~]$
## 起動確認
[ec2-user@mongo2 ~]$ /etc/init.d/mongod status
mongod (pid 17768 1732) is running...
[ec2-user@mongo2 ~]$ ps aux | head -1; ps aux | grep mongod
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1732 0.1 3.1 725500 32024 ? Sl 15:27 0:00 mongod --config /data/mongo_rs/conf/mongod.conf
root 17768 0.1 3.1 717308 32076 ? Sl 15:32 0:00 mongod --config /data/mongo_rs/conf/arbiter.conf
ec2-user 17862 0.0 0.0 110280 840 pts/0 S+ 15:36 0:00 grep mongod
[ec2-user@mongo2 ~]$
## 停止させる場合
mongod stopコマンドだとSecondary/Arbiterのどちらかしか停止できない。
完全に停止させるにはkillコマンドを使用する。
■データベース接続確認
[ec2-user@mongo2 ~]$ mongo mongo2:30002
MongoDB shell version: 2.6.4
connecting to: mongo2:30002/test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
> show dbs
admin (empty)
local 0.078GB
> exit
bye
[ec2-user@mongo2 ~]$ mongo mongo2:30003
MongoDB shell version: 2.6.4
connecting to: mongo2:30003/test
> show dbs
admin (empty)
local 0.078GB
> exit
bye
[ec2-user@mongo2 ~]$
Secondary/Arbiterの準備はこれで完了です。次はPrimaryの設定になります。
◆参考:MongoDBインスタンスの設定値の確認
[ec2-user@mongo2 ~]$ cat /proc/1732/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 7772 7772 processes
Max open files 2048 2048 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 7772 7772 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
[ec2-user@mongo2 ~]$
※1732はSecondaryのPIDです。
◆参考:MongoDBモニタリングツール
[ec2-user@mongo2 ~]$ mongostat -h mongo2 --port 30002
connected to: mongo2:30002
insert query update delete getmore command flushes mapped vsize res faults locked db idx miss % qr|qw ar|aw netIn netOut conn repl time
*0 1 *0 *0 0 1|0 0 80m 709m 31m 0 local:0.0% 0 0|0 0|0 62b 3k 1 REC 15:41:44
*0 1 *0 *0 0 1|0 0 80m 709m 31m 0 local:0.0% 0 0|0 0|0 62b 3k 1 REC 15:41:45
*0 1 *0 *0 0 1|0 0 80m 709m 31m 0 local:0.0% 0 0|0 0|0 62b 3k 1 REC 15:41:46
*0 1 *0 *0 0 1|0 0 80m 709m 31m 0 local:0.0% 0 0|0 0|0 62b 3k 1 REC 15:41:47
*0 1 *0 *0 0 1|0 0 80m 709m 31m 0 local:0.0% 0 0|0 0|0 62b 3k 1 REC 15:41:48
^C
[ec2-user@mongo2 ~]$
******* Primary *******
■EC2概要
Instance: i-eca4xxx5 (mongo1)
Private IP: 10.54.10.90
Availability zone: ap-northeast-1a
OS: amazon-linux-ami/2014.03 (64-bit)
■ホスト名変更
[ec2-user@ip-10-54-10-90 ~]$ sudo cp -p /etc/sysconfig/network /etc/sysconfig/network.org
[ec2-user@ip-10-54-10-90 ~]$ sudo vi /etc/sysconfig/network
[ec2-user@ip-10-54-10-90 ~]$ diff /etc/sysconfig/network /etc/sysconfig/network.org
2c2
< HOSTNAME=mongo1
---
> HOSTNAME=localhost.localdomain
[ec2-user@ip-10-54-10-90 ~]$ sudo reboot
■hostsファイルの設定
[ec2-user@mongo1 ~]$ sudo vi /etc/hosts
[ec2-user@mongo1 ~]$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain
10.54.10.90 mongo1
10.54.30.152 mongo2
[ec2-user@mongo1 ~]$
※AWSの場合、SecurityGroupでmongo2サーバとの通信を許可しておいてください。
■設定ファイル作成(初期Primary)
[ec2-user@mongo1 ~]$ vi /data/mongo_rs/conf/mongod.conf
[ec2-user@mongo1 ~]$ cat /data/mongo_rs/conf/mongod.conf
bind_ip=mongo1
port=30001
dbpath=/data/mongo_rs/db
pidfilepath=/var/run/mongodb/mongod.pid
logpath=/var/log/mongodb/mongod.log
logappend=true
fork=true
replSet=psa
[ec2-user@mongo1 ~]$
■mongod起動
[ec2-user@mongo1 ~]$ ulimit -n 2048; sudo mongod --config /data/mongo_rs/conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 1615
child process started successfully, parent exiting
[ec2-user@mongo1 ~]$
この時点ではまだreplicationされていませんのでご注意を。
■replication設定
いよいよrepricationの設定です。rs.initiate()実行後にコマンドプロンプトが
"ReplicaSet名:PRIMARY>"に変更されます。
## ReplicaSetの初期化
[ec2-user@mongo1 ~]$ mongo mongo1:30001
MongoDB shell version: 2.6.4
connecting to: mongo1:30001/test
> rs.initiate()
{
"info2" : "no configuration explicitly specified -- making one",
"me" : "mongo1:30001",
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
>
psa:PRIMARY> rs.conf()
{
"_id" : "psa",
"version" : 1,
"members" : [
{
"_id" : 0,
"host" : "mongo1:30001"
}
]
}
psa:PRIMARY>
## SecandaryとArbiter追加
psa:PRIMARY> rs.add("mongo2:30002")
{ "ok" : 1 }
psa:PRIMARY> rs.addArb("mongo2:30003")
{ "ok" : 1 }
psa:PRIMARY>
## 設定の確認
psa:PRIMARY> rs.conf()
{
"_id" : "psa",
"version" : 3,
"members" : [
{
"_id" : 0,
"host" : "mongo1:30001"
},
{
"_id" : 1,
"host" : "mongo2:30002"
},
{
"_id" : 2,
"host" : "mongo2:30003",
"arbiterOnly" : true
}
]
}
psa:PRIMARY> rs.status()
{
"set" : "psa",
"date" : ISODate("2014-09-04T07:13:39Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "mongo1:30001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 461,
"optime" : Timestamp(1409814782, 1),
"optimeDate" : ISODate("2014-09-04T07:13:02Z"),
"electionTime" : Timestamp(1409814552, 2),
"electionDate" : ISODate("2014-09-04T07:09:12Z"),
"self" : true
},
{
"_id" : 1,
"name" : "mongo2:30002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 53,
"optime" : Timestamp(1409814782, 1),
"optimeDate" : ISODate("2014-09-04T07:13:02Z"),
"lastHeartbeat" : ISODate("2014-09-04T07:13:38Z"),
"lastHeartbeatRecv" : ISODate("2014-09-04T07:13:38Z"),
"pingMs" : 2,
"syncingTo" : "mongo1:30001"
},
{
"_id" : 2,
"name" : "mongo2:30003",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 37,
"lastHeartbeat" : ISODate("2014-09-04T07:13:38Z"),
"lastHeartbeatRecv" : ISODate("2014-09-04T07:13:39Z"),
"pingMs" : 2
}
],
"ok" : 1
}
psa:PRIMARY>
## 念のためSecondary側でも確認
[ec2-user@mongo1 ~]$ mongo mongo2:30002
MongoDB shell version: 2.6.4
connecting to: mongo2:30002/test
psa:SECONDARY> rs.conf()
{
"_id" : "psa",
"version" : 3,
"members" : [
{
"_id" : 0,
"host" : "mongo1:30001"
},
{
"_id" : 1,
"host" : "mongo2:30002"
},
{
"_id" : 2,
"host" : "mongo2:30003",
"arbiterOnly" : true
}
]
}
psa:SECONDARY>
◆参考:replicationの動作検証
Primaryで作成した1000件のレコードが、Secondaryにレプリケートされいているか確認。
## レコード作成(Primary)
[ec2-user@mongo1 ~]$ mongo mongo1:30001/reptest
MongoDB shell version: 2.6.4
connecting to: mongo1:30001/reptest
psa:PRIMARY> for(var i=0; i<1000; i++) db.repcoll.insert( { "uid":i, "value":Math.floor(Math.random()*1000+1) } )
WriteResult({ "nInserted" : 1 })
psa:PRIMARY> db.repcoll.count()
1000
psa:PRIMARY> db.repcoll.find()
{ "_id" : ObjectId("5408144a85529d4cbf6eb107"), "uid" : 0, "value" : 929 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb108"), "uid" : 1, "value" : 547 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb109"), "uid" : 2, "value" : 26 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10a"), "uid" : 3, "value" : 813 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10b"), "uid" : 4, "value" : 609 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10c"), "uid" : 5, "value" : 992 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10d"), "uid" : 6, "value" : 75 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10e"), "uid" : 7, "value" : 449 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10f"), "uid" : 8, "value" : 817 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb110"), "uid" : 9, "value" : 422 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb111"), "uid" : 10, "value" : 133 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb112"), "uid" : 11, "value" : 280 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb113"), "uid" : 12, "value" : 613 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb114"), "uid" : 13, "value" : 830 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb115"), "uid" : 14, "value" : 790 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb116"), "uid" : 15, "value" : 155 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb117"), "uid" : 16, "value" : 915 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb118"), "uid" : 17, "value" : 317 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb119"), "uid" : 18, "value" : 903 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb11a"), "uid" : 19, "value" : 89 }
Type "it" for more
psa:PRIMARY>
## 確認(Secondary)
[ec2-user@mongo1 ~]$ mongo mongo2:30002/reptest
MongoDB shell version: 2.6.4
connecting to: mongo2:30002/reptest
psa:SECONDARY> db.repcoll.count()
2014-09-04T16:30:49.898+0900 count failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" } at src/mongo/shell/query.js:191
psa:SECONDARY>
→読み取り許可設定が必要。
psa:SECONDARY> db.getMongo().setSlaveOk()
psa:SECONDARY> db.repcoll.count()
1000
psa:SECONDARY> db.repcoll.find()
{ "_id" : ObjectId("5408144a85529d4cbf6eb107"), "uid" : 0, "value" : 929 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb108"), "uid" : 1, "value" : 547 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb109"), "uid" : 2, "value" : 26 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10a"), "uid" : 3, "value" : 813 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10b"), "uid" : 4, "value" : 609 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10c"), "uid" : 5, "value" : 992 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10d"), "uid" : 6, "value" : 75 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10e"), "uid" : 7, "value" : 449 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb10f"), "uid" : 8, "value" : 817 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb110"), "uid" : 9, "value" : 422 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb111"), "uid" : 10, "value" : 133 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb112"), "uid" : 11, "value" : 280 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb113"), "uid" : 12, "value" : 613 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb114"), "uid" : 13, "value" : 830 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb115"), "uid" : 14, "value" : 790 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb116"), "uid" : 15, "value" : 155 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb117"), "uid" : 16, "value" : 915 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb118"), "uid" : 17, "value" : 317 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb119"), "uid" : 18, "value" : 903 }
{ "_id" : ObjectId("5408144a85529d4cbf6eb11a"), "uid" : 19, "value" : 89 }
Type "it" for more
psa:SECONDARY>
Primaryに登録されたデータがSecondaryにレプリケートされていることを確認できました。
◆参考:MongoDBフェイルオーバー検証
Primaryに障害が発生した場合、SecandaryがPrimaryに昇格するか確認してみます。
## mongo1サーバのPrimary停止前(Arbiterに接続して確認)
[ec2-user@mongo1 ~]$ mongo mongo2:30003 --eval "printjson(rs.status())" | egrep "name|stateStr"
"name" : "mongo1:30001",
"stateStr" : "PRIMARY",
"name" : "mongo2:30002",
"stateStr" : "SECONDARY",
"name" : "mongo2:30003",
"stateStr" : "ARBITER",
[ec2-user@mongo1 ~]$
mongo ホスト名:ポート番号 --eval "printjson(mongoシェル)" とすると、Bashから直接mongoシェルを実行できます。
## mongo1サーバのPrimary停止
[ec2-user@mongo1 ~]$ ps -ef | grep mongo
root 1615 1 1 16:05 ? 00:02:43 mongod --config /data/mongo_rs/conf/mongod.conf
ec2-user 3681 2574 0 19:04 pts/0 00:00:00 grep mongo
[ec2-user@mongo1 ~]$ sudo kill -9 1615
[ec2-user@mongo1 ~]$
[ec2-user@mongo1 ~]$ mongo mongo2:30003 --eval "printjson(rs.status())" | egrep "name|stateStr"
"name" : "mongo1:30001",
"stateStr" : "(not reachable/healthy)",
"name" : "mongo2:30002",
"stateStr" : "PRIMARY",
"name" : "mongo2:30003",
"stateStr" : "ARBITER",
[ec2-user@mongo1 ~]$
SecandaryがPrimaryに昇格しました!!
※この後、元Primaryを復旧させると優先順位を定義していないのでSecondaryとなります。
長文にお付き合いいただきありがとうございました。こちらの情報が何かのお役に立てましたら幸いです。