MongoDB Replica Set 구성하기
MongoDB 는 Replication 을 지원한다. 이는 서비스의 지속성과 안전성을 제공하는 데이터베이스 시스템의 설비다. MongoDB 는 단순하게 데이터 복제를 위한 것뿐만 아니라 Master 가 장애시에 이를 Slave 를 Master 로 자동승격시켜준다. 수 많은 Slave 중에 어떤 것을 Master 로 승격할지를 투표를 통해서 결정한다. MongoDB 는 투표에 참여하기만 하는 것으로 Arbiter 를 세팅할 수도 있다.
이글은 다음의 아키텍쳐를 기반으로 한다.
Replica Set 설정
replication 설정을 위해서 서버 두대에 etc/mongod.conf 파일을 다음과 같이 설정을 해준다.
1 2 3 |
replication: oplogSizeMB: 5120 replSetName: "rs1" |
oplogSizeMB 는 Replication 을 위해사용되는 로그파일의 용량을 지정하는 것이다. 메뉴얼에 따르면 파일시스템의 활용가능한 5% 를 지정하라고 되어 있지만 고용량 스토리지 시대에는 너무 큰 것 같다.
위와같이 하고 Replica Set 을 설정해주면 된다. 보통 Replica Set 은 적어도 3대로 구성하는데 Primary, Secondary, Aribter 가 바로 그것이다.
한가지 주의해야할 사항은 Replica Set 구성을 미리해놓은다고 서버가 한대인데도 mongod.conf 에 위 설정을 하고 서버에서 명령어를 치면 다음과 같은 오류를 만나게 된다.
1 2 3 4 5 6 7 |
> show dbs; 2016-06-04T22:32:51.328+0900 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1 shellHelper.show@src/mongo/shell/utils.js:760:19 shellHelper@src/mongo/shell/utils.js:650:15 @(shellhelp2):1:1 |
위 설정은 Master/Slave 두 서버에 모두 해줘야 한다. 그리고 두 서버를 모두 재시작 해준다.
Replication 설정
어떤 블로그에는 Replication 설정을 위해서 다음의 명령어를 치라고 나온다.
1 |
> rs.initiate() |
적어도 MongoDB 3.2 버전에서는 절대로 위 명령어를 먼저 쳐서는 안된다. 내 경우에 멋모르고 위 명령어를 먼저 쳤다가 MongoDB 가 Primary 로 지정되지 않아 아무것도 할 수 없었다. 이럴 경우에 강제로 Primary 임을 지정해줘야 하는데 절차는 다음과 같다.
1 2 3 |
> cfg = rs.conf() > cfg.members = [cfg.members[0]] <- Primary로 지정할 멤버하나만 넣는다. > rs.reconfig(cfg, {force : true}) |
force 옵션을 주어서 강제로 설정을 지정하게 해줘야한다.
아무튼, 맨 처음에 아무런 생각없이 rs.initiate() 을 해서는 안된다. 특히나 Primary 임을 지정해야하는 서버의 경우에는 이렇게 해서는 안된다. 반드시 멤버지정을 수동으로 해줘야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 |
> rsconf = {_id:"rs1", members:[{_id:0, host:"192.168.96.6:27017"}]} { "_id" : "rs1", "members" : [ { "_id" : 0, "host" : "192.168.96.6:27017" } ] } > rs.initiate(rsconf) { "ok" : 1 } |
이렇게 하면 이 서버는 이제 Primary 로 지정된다. 상태를 살펴보면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
rs1:PRIMARY> rs.status() { "set" : "rs1", "date" : ISODate("2016-06-05T07:17:39.321Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "192.168.96.6:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 785, "optime" : { "ts" : Timestamp(1465110577, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2016-06-05T07:09:37Z"), "electionTime" : Timestamp(1465110576, 2), "electionDate" : ISODate("2016-06-05T07:09:36Z"), "configVersion" : 1, "self" : true } ], "ok" : 1 } |
“stateStr” 이 PRIMARY 라고 나와야 정상이다.
이제 슬레이브를 붙이면 되는데, MongoDB 는 이를 아주 쉽게 명령어로 할 수 있다. Slave 서버를 구동하고 다음과 같이 Primary 서버에서 명령어를 입력해주면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
rs1:PRIMARY> rs.add("192.168.96.30:27017") { "ok" : 1 } rs1:PRIMARY> rs.status() { "set" : "rs1", "date" : ISODate("2016-06-05T07:19:56.103Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "192.168.96.6:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 922, "optime" : { "ts" : Timestamp(1465111191, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2016-06-05T07:19:51Z"), "electionTime" : Timestamp(1465110576, 2), "electionDate" : ISODate("2016-06-05T07:09:36Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "192.168.96.30:27017", "health" : 1, "state" : 5, "stateStr" : "STARTUP2", "uptime" : 4, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2016-06-05T07:19:55.545Z"), "lastHeartbeatRecv" : ISODate("2016-06-05T07:19:51.894Z"), "pingMs" : NumberLong(1), "syncingTo" : "192.168.96.6:27017", "configVersion" : 2 } ], "ok" : 1 } |
Slave 를 붙이자마자 상태를 확인해보면 stateStr 이 STARTUP2 로 나온다. 하지만 시간이 지나서 다시 확인해보면 SECONDARY 가 되어 있는 것을 확인할 수 있다.
PRIMARY 와 SECONDARY 가 동기화가 잘되고 있는지는 optime 의 수치가 모두 동일한지를 보고 판단할 수 있다.
Aribter 추가
Aribter 는 PRIMARY 가 장애가 발생했을시에 이를 감지하고 SECONDARY 중에 하나를 PRIMARY 로 선택할때 투표에 참여하는 역화을 한다. 즉, 오직 투표만을 위한 시스템이라고 보면 된다. 따라서 데이터 복제, 데이터 저장이 없기 때문에 저장소를 위한 설정은 다 필요가 없다. 따라서 mongod.conf 설정은 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # where to write logging data. systemLog: verbosity: 0 destination: file logAppend: true logRotate: rename timeStampFormat: ctime path: "/home/instance2/mongodb/log/mongod.log" component: replication: verbosity: 2 # Where and how to store data. storage: dbPath: "/home/instance2/mongodb/data" directoryPerDB: true journal: enabled: false # engine: "wiredTiger" # mmapv1: # wiredTiger: # engineConfig: # cacheSizeGB: 1 # how the process runs processManagement: fork: true # fork and run in background pidFilePath: "/home/instance2/mongodb/run/mongod.pid" # location of pidfile # network interfaces net: port: 27017 bindIp: 0.0.0.0 # Listen to local interface only, comment to listen on all interfaces. maxIncomingConnections: 1024 http: enabled: false security: keyFile: "/home/instance2/mongodb/etc/.keyfile/mongodb_keyfile" clusterAuthMode: "keyFile" authorization: "enabled" operationProfiling: slowOpThresholdMs: 100 mode: off replication: oplogSizeMB: 5120 replSetName: "rs1" #sharding: ## Enterprise-Only Options #auditLog: #snmp: |
MongoDB 3.2 메뉴얼에 따르면 Aribter 로 동작하는 Node 의 경우에 storage.journal.enabled 를 false 로 설정하도록 권장하고 있다. 그리고 Engine 관련 설정은 모두 주석처리를 한다.
이렇게 설정한 후에 Aribter 서버를 시작시킨다. 그리고 Master 서버에서 다음과 같이 Aribter 를 추가해 준다.
1 |
rs1:PRIMARY> rs.addArb("192.168.96.31:27017") |
위와같이 하면 Arbiter 가 Replica Set 에 추가 된다. 그리고 Master 가 장애가 발생하면 투표에 참여해 SECONDARY 서버중에 하나를 Master 로 지목하게 된다.