본문 바로가기

엔지니어/Linux

MongoDB ReplicaSet

728x90
반응형

mongod --replSet tt --config=/etc/mongodb.conf <-- 이걸로 하세요 ~

echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.list


apt-get update


apt-get install mongodb-org=3.0.2 mongodb-org-server=3.0.2 mongodb-org-shell=3.0.2 mongodb-org-mongos=3.0.2 mongodb-org-tools=3.0.2



config = {
    _id : "pp_mongo",
     members : [
         {_id : 0, host : "192.168.0.12:27017"}, 
         {_id : 1, host : "192.168.0.13:27017"}, 
         {_id : 2, host : "192.168.0.14:27017", arbiterOnly: true},
     ]
}
rs.initiate(config);


최소한의 디비 구성

1개의 Primary DB + 1개의 Secondary DB + 1개의 Arbiter DB(Secondary DB)로 구성한다.

Primary DB

원본 데이터가 저장되는 디비이며 Read & Write 요청을 처리한다.

Secondary DB

원본 데이터의 사본을 저장하는 디비이며, Read 요청을 처리한다.

장애가 발생하면, 투표를 하여 Secondary DB 중 하나를 선택하여 Primary DB로 승격시킨다.

Arbiter DB

장애 발생시 투표에만 참여한다.

리소스를 많이 먹지 않기 때문에, 전용 서버를 따로 둘 필요는 없다.


구성해보기

Primary DB와 Secondary DB 구성

둘 다 동일하다.

테스트 용으로 서버 1대에서 실행할 때에는 logpatth, dbpath, port, pidfilepath를 다르게 지정해야 한다.

ReplicaSet의 이름은 first로 지정해 보자.

12345678910111213141516171819
# mongod.conf
 
#where to log
logpath=/var/lib/mongo/db1/mongod.log
 
port=10001
 
dbpath=/var/lib/mongo/db1
 
# location of pidfile
pidfilepath=/var/run/mongodb/mongod_repl1.pid
 
# Listen to local interface only. Comment out to listen on all interfaces.
bind_ip=127.0.0.1
 
# Replication Options
 
# in replicated mongo databases, specify the replica set name here
replSet=first
view rawmongod_repl.conf hosted with ❤ by GitHub

Arbiter DB 구성

리소스를 최소한으로 설정하기 위해 smallfiles=true, noprealloc=true, oplogSize=1로 설정한다. 

설정했을 때, 30메가 정도의 하드디스크를 먹었고, 

안했을 때, 400메가 정도의 하드디스크를 차지하였다.

123456789101112131415161718192021222324252627282930
# mongod.conf
 
#where to log
logpath=/var/lib/mongo/db3/mongod.log
 
port=10003
 
dbpath=/var/lib/mongo/db3
 
smallfiles=true
 
# location of pidfile
pidfilepath=/var/run/mongodb/mongod_arb.pid
 
# Listen to local interface only. Comment out to listen on all interfaces.
bind_ip=127.0.0.1
 
# Disables write-ahead journaling
nojournal=true
 
# Disable data file preallocation.
noprealloc=true
 
# Replication Options
 
# in replicated mongo databases, specify the replica set name here
replSet=first
 
# maximum size in megabytes for replication operation log
oplogSize=1
view rawmongod_arb.conf hosted with ❤ by GitHub

실행해보기

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
63
64
65
66
67
68
69
## 실행 편의를 위해 머신 1대에서 실행하기 위해
## port와 dbpath, logpath등을 변경하였다.
## dbpath, logpath에 지정한 폴더가 존재해야 한다. 없으면 만들어야 한다. 파일은 자동 생성되니 만들필요 없다..
 
## Primary DB 실행 port:10001
$ mongod --config /etc/mongod_repl1.conf
 
## Sencondary DB 실행 port:10002
$ mongod --config /etc/mongod_repl2.conf
 
## Arbiter DB 실행 port:10003
$ mongod --config /etc/mongod_arb.conf
 
## Primary DB 접속해서 ReplicaSet 설정하기
$ mongo localhost:10001/admin
>db.runCommand({"replSetInitiate" :
                    {"_id" : "first", "members" : [{"_id" : 1, "host" : "localhost:10001"},
                                                      {"_id" : 2, "host" : "localhost:10002"}
             ]}})
first:PRIMARY> rs.add('localhost:10003', true)
 
## 설정 확인
first:PRIMARY> rs.status()
{
        "set" : "first",
        "date" : ISODate("2014-06-23T08:10:24Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 1,
                        "name" : "localhost:10001",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 5086,
                        "optime" : Timestamp(1403505938, 1),
                        "optimeDate" : ISODate("2014-06-23T06:45:38Z"),
                        "lastHeartbeat" : ISODate("2014-06-23T08:10:23Z"),
                        "lastHeartbeatRecv" : ISODate("2014-06-23T08:10:23Z"),
                        "pingMs" : 0,
                        "syncingTo" : "localhost:10002"
                },
                {
                        "_id" : 2,
                        "name" : "localhost:10002",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 6041,
                        "optime" : Timestamp(1403505938, 1),
                        "optimeDate" : ISODate("2014-06-23T06:45:38Z"),
                        "electionTime" : Timestamp(1403505913, 1),
                        "electionDate" : ISODate("2014-06-23T06:45:13Z"),
                        "self" : true
                },
                {
                        "_id" : 3,
                        "name" : "localhost:10003",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 22,
                        "lastHeartbeat" : ISODate("2014-06-23T08:10:22Z"),
                        "lastHeartbeatRecv" : ISODate("2014-06-23T08:10:23Z"),
                        "pingMs" : 0
                }
        ],
        "ok" : 1
}

테스트 해보기

Secondary DB가 원본 데이터를 잘 복사하는지 보자

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
## Primary DB 접속해 테스트 데이터 넣기
$ mongo localhost:10002/
first:PRIMARY> use test
switched to db test
first:PRIMARY> for(var i = 0; i < 100; ++i) db.test_col.save({name:i})
WriteResult({ "nInserted" : 1 })
 
## Secondary DB 접속해 원본 데이터를 복사했는지 확인
$ mongo localhost:10001/
first:SECONDARY> use test
switched to db test
first:SECONDARY> db.test_col.find({})
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
 
## slaveOk함수를 호출해야 읽기가 가능함
first:SECONDARY> rs.slaveOk()
first:SECONDARY> db.test_col.find({})
{ "_id" : ObjectId("53a7bd6c08c322644599914a"), "name" : 0 }
{ "_id" : ObjectId("53a7bd6c08c322644599914b"), "name" : 1 }
{ "_id" : ObjectId("53a7bd6c08c322644599914c"), "name" : 2 }
{ "_id" : ObjectId("53a7bd6c08c322644599914d"), "name" : 3 }
{ "_id" : ObjectId("53a7bd6c08c322644599914e"), "name" : 4 }
{ "_id" : ObjectId("53a7bd6c08c322644599914f"), "name" : 5 }
{ "_id" : ObjectId("53a7bd6c08c3226445999150"), "name" : 6 }
{ "_id" : ObjectId("53a7bd6c08c3226445999151"), "name" : 7 }
{ "_id" : ObjectId("53a7bd6c08c3226445999152"), "name" : 8 }
{ "_id" : ObjectId("53a7bd6c08c3226445999153"), "name" : 9 }
{ "_id" : ObjectId("53a7bd6c08c3226445999154"), "name" : 10 }
{ "_id" : ObjectId("53a7bd6c08c3226445999155"), "name" : 11 }
{ "_id" : ObjectId("53a7bd6c08c3226445999156"), "name" : 12 }
{ "_id" : ObjectId("53a7bd6c08c3226445999157"), "name" : 13 }
{ "_id" : ObjectId("53a7bd6c08c3226445999158"), "name" : 14 }
{ "_id" : ObjectId("53a7bd6c08c3226445999159"), "name" : 15 }
{ "_id" : ObjectId("53a7bd6c08c322644599915a"), "name" : 16 }
{ "_id" : ObjectId("53a7bd6c08c322644599915b"), "name" : 17 }
{ "_id" : ObjectId("53a7bd6c08c322644599915c"), "name" : 18 }
{ "_id" : ObjectId("53a7bd6c08c322644599915d"), "name" : 19 }
Type "it" for more
## 정상이군

Auto failover가 잘 되는지 보자

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
## Primary DB에 접속해 디비 서버 종료
$ mongo localhost:10002/admin
MongoDB shell version: 2.6.2
connecting to: localhost:10002/admin
first:PRIMARY> db.shutdownServer()
 
## Sencondary DB에 접속해 자신이 Primary DB로 승격되었는지 확인
$ mongo localhost:10001/admin
MongoDB shell version: 2.6.2
connecting to: localhost:10001/admin
first:PRIMARY> rs.status()
{
        "set" : "first",
        "date" : ISODate("2014-06-23T08:33:50Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 1,
                        "name" : "localhost:10001",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 7083,
                        "optime" : Timestamp(1403511736, 100),
                        "optimeDate" : ISODate("2014-06-23T08:22:16Z"),
                        "electionTime" : Timestamp(1403512372, 1),
                        "electionDate" : ISODate("2014-06-23T08:32:52Z"),
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "localhost:10002",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : Timestamp(1403511736, 100),
                        "optimeDate" : ISODate("2014-06-23T08:22:16Z"),
                        "lastHeartbeat" : ISODate("2014-06-23T08:33:46Z"),
                        "lastHeartbeatRecv" : ISODate("2014-06-23T08:32:44Z"),
                        "pingMs" : 0
                },
                {
                        "_id" : 3,
                        "name" : "localhost:10003",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 1427,
                        "lastHeartbeat" : ISODate("2014-06-23T08:33:48Z"),
                        "lastHeartbeatRecv" : ISODate("2014-06-23T08:33:49Z"),
                        "pingMs" : 0
                }
        ],
        "ok" : 1
}

Auto Failover 동작 테스트

S와 A의 수가 장애극복 처리에 어떤 영향을 끼치는지, 

S와 A서버에 장애가 발했을 때, P서버에도 장애가 발생하면 어떻게 되는지 테스트 해보았다. 

 Primary = P, Secondary = S, Arbiter = A

 DB Type = O(alive) or X(dead)

동작 여부

 P = X

S = O

A = O

 O

 P = X

S = O

A = X

 X

 P = X

S = O

S = O

A = O

 O

 P = X

S = O

S = O

A = X

 X

 P = X

S = O

S = X

A = O

 X

 P = X

S = O

A = O

A = O

 O

 P = X

S = O

A = O

A = X

 X

 P = X

S = O

S = O

S = O

A = O

 O

 P = X

S = O

S = O

S = X

A = O

 O

 P = X

S = O

S = O

S = X

A = X

 X

 P = X

S = O

S = X

S = X

A = X

 X(P가 S로 자동으로 변경)

 P = X

S = O

S = O

A = O

A = O

 O

 P = X

S = O

S = O

A = X

A = X

 X

 P = X

S = O

S = X

A = X

A = X

 X(P가 S로 자동으로 변경)

 P = X

S = O

S = O

S = O

A = O

A = O

 O

 P = X

S = O

S = X

S = O

A = O

A = O

 O

 P = X

S = O

S = X

S = O

A = O

A = X

 X

 P = X

S = O

S = O

S = O

A = X

A = X

 X

 P = X

S = O

S = X

S = X

A = O

A = X

 X(P가 S로 자동으로 변경)


테스트 결론

A와 S에 속한 맴버 중 과반수에 장애가 발생한다면, P는 S로 자동으로 변경됨.

ReplicaSet 전체 맴버 중 살아있는 수가 과반수가 아니면 장애극복이 이루어지지 않는다. 즉, S가 2대 이상이 살았어도 살아남은 수가 과반수가 아니면 장애극복이 안된다.


반응형

'엔지니어 > Linux' 카테고리의 다른 글

node.js 설치  (0) 2016.05.26
iptables-xml  (0) 2016.05.26
텔레그램을 커맨드 라인으로 사용하기, Telegram_cli  (0) 2016.05.26
DRBD  (0) 2016.05.26
nginx upstream  (0) 2016.05.26