2014년 10월 12일 일요일

몽고디비2.6 리플리케이션

1.테스트용 디렉토리 만들기
1.1 컨피그 디렉토리 만들기
cd /
sudo mkdir /cofig
sudo chown lee /config

1.2 테스트 디비용 디렉토리 만들기
cd /
sudo mkdir /test
sudo chown lee /test
cd mkdir rs0

mkdir rs0
mkdir rs0/server1
mkdir rs0/server2
mkdir rs0/server3

1.3 레플리케이션 시작하기
mongod --port 27001 --dbpath /test/rs0/server1 --replSet 'rs0' --logpath /test/log/rs0_server_1 --fork
mongod --port 27002 --dbpath /test/rs0/server2 --replSet 'rs0' --logpath /test/log/rs0_server_2 --fork
mongod --port 27003 --dbpath /test/rs0/server3 --replSet 'rs0' --logpath /test/log/rs0_server_3 --fork

옵션 설명
--port
디비가 시작할 포트
--dbpath
디비가 위치할 디렉토리
--replSert
레플리케이션 이름
1.4 레플리케이션 설정하기
몽고 쉘로 접속해서 최초 레플리케이션을 init 한다.

mongo --port 27001
var rsconfig = {"_id":"rs0", "members":[{"_id":1, "host":"127.0.0.1:27001"}]}

rs.initiate(rsconfig)

*테스트를 위해 localhost ,127.0.0.1 을 지원하지만 다른 ip 와 섞어 쓰는건 안된다.

1.5 복제셋 맴버추가
rs.add("127.0.0.1:27002")
rs.add("127.0.0.1:27003")

1.6 복제셋 멤버제거
/*
rs.remove("127.0.0.1:27002")
*/

1.7 복세셋 멤버 확인
rs.config()
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 3,
"host" : "127.0.0.1:27003"
},
{
"_id" : 4,
"host" : "127.0.0.1:27002"
}
]
}

_id : 복제셋 이름
verstion : 멤버를 추가할때마다

1.8 컨피그 변경이 가능함
/*
var reconfig = rs.config()
reconfig.members[1].host = '0.0.0.0:27003';
rs.reconfig(reconfig)
*/

1.9 세컨더리 접속
mongo --port 27002

2.레플리케이션 상태 확인

2.0 마스터에서 확인
mongo --port 27001
db.isMaster()
{
"setName" : "rs0",
"setVersion" : 5,
"ismaster" : true,
"hosts" : [
"127.0.0.1:27001",
"127.0.0.1:27002",
"127.0.0.1:27003"
],
"primary" : "127.0.0.1:27001",
"me" : "127.0.0.1:27001",
"localTime" : ISODate("2014-10-10T14:59:32.981Z"),
"maxWireVersion" : 2,
"minWireVersion" : 0,
"ok" : 1
}

2.1 마스터 : 예제 컬랙션 insert
use test

function insertTest(count){
for(var i = 0; i< count; i++){
db.test1.insert({a:i, b:i});
}
}

insertTest(1000);
db.test1.find();

2.2 마스터 : 레플리케이션 상태확인
rs.status()

*실제로 레플리케이션에 모든 복제가 완료 되었는지 확인하려면
optimeDate 가 동일한지 확인하면 된다.
{
"set" : "rs0",
"date" : ISODate("2014-10-10T15:04:57Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "127.0.0.1:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2331,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"electionTime" : Timestamp(1412951411, 1),
"electionDate" : ISODate("2014-10-10T14:30:11Z"),
"self" : true
},
{
"_id" : 3,
"name" : "127.0.0.1:27003",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 924,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"lastHeartbeat" : ISODate("2014-10-10T15:04:55Z"),
"lastHeartbeatRecv" : ISODate("2014-10-10T15:04:56Z"),
"pingMs" : 0,
"syncingTo" : "127.0.0.1:27001"
},
{
"_id" : 4,
"name" : "127.0.0.1:27002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 893,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"lastHeartbeat" : ISODate("2014-10-10T15:04:56Z"),
"lastHeartbeatRecv" : ISODate("2014-10-10T15:04:56Z"),
"pingMs" : 0,
"syncingTo" : "127.0.0.1:27001"
}
],
"ok" : 1
}

2.3 슬레이브 : 읽기 시도
db.test1.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

몽고 디비에서 레플리케이션 셋의 기본 설정은 슬레이브에서 읽는게 안된다.
(슬레이브에서 읽을 경우 예전 데이터를 읽을수 있기 때문에)

어플리케이션에서 예전 데이터를 읽어도 상관없을때는
마스터 > rs.slaveOk()
슬레이브 읽기가 허용된다.

슬레이브 > db.test1.find(); 결과값이 리턴된다.

2.4 디비가 죽을 경우의 마스터 선출 작업
27001 디비를 죽인다. ctrl+c

슬레이브1 > rs.status()
현재 마스터 이며 member 의 27001 이 죽었음을 확인한다.
27001
"health" : 0,

mongod --port 27001 --dbpath /test/rs0/server1 --replSet 'rs0'
다시 시작한다.

슬레이브1 > rs.status()
좀전의 27001 이 정상동작 하는지 확인
27001
"health" : 1,

* 3 대중 2대가 살아 남을 경우 투표를 통해 하나의 슬레이브를 마스터로 승격 시킨다.
투표를 하기위해서는 과반수가 살아 있어야 한다.
ex)3 대 이면 2대, 4 대이면 3대 ...

* 과반수가 살아 있지 않는다면 투표를 할수 없기 때문에 secondery 상태가 유지된다.
why? 과반수 인가?
a, b 데이터 센터에 몽고디비 1,2,3을 배치했다
현재 마스터는 a:1 이다.
a : 1,2
b : 3

b 데이터 센터가 일시적으로 네트워크가 단절되었다
과반수가 아니라면 b: 3이 마스터가 되고 데이터를 받아드린다.
이때 a:1 도 마스터이기 때문에 네트워크가 복구 되면 데이터의 정합성 문제가 발행한다.

2.5 세컨더리에서 프라이머리로 변경하기
(3개의 멤버 중에서 2개의 맴버가 죽었다고 가정 살아있는 맴버를 프라이머리로 만들기)
var cfg = rs.config()
rs0:SECONDARY> cfg
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 3,
"host" : "127.0.0.1:27003"
},
{
"_id" : 4,
"host" : "127.0.0.1:27002"
}
]
}

27001 이 세건더리이고 27002, 27003 이 죽었을 경우
cfg.members = [cfg.members[0]]
cfg 로 멤버 확인
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
}
]
}

rs.reconfig(cfg, {force : true})
rs.status()
현재 서버가 프라이머리인거 확인




2.6 아비타 더하기
cd /test
mkdir rs0/arb1

mongod --port 27004 --dbpath /test/rs0/arb1 --replest rs0
프라이머리 : rs.addArb('127.0.0.1:27004')

rs.status()
{
"set" : "rs0",
"date" : ISODate("2014-10-12T06:49:55Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "127.0.0.1:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1095,
"optime" : Timestamp(1413096584, 1),
"optimeDate" : ISODate("2014-10-12T06:49:44Z"),
"electionTime" : Timestamp(1413096488, 1),
"electionDate" : ISODate("2014-10-12T06:48:08Z"),
"self" : true
},
{
"_id" : 2,
"name" : "127.0.0.1:27004",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 11,
"lastHeartbeat" : ISODate("2014-10-12T06:49:54Z"),
"lastHeartbeatRecv" : ISODate("2014-10-12T06:49:55Z"),
"pingMs" : 0
}
],
"ok" : 1
}

stateStr 'ARBITER' 확인

2.7 아비터가 실제 동작하는지 또한 실제로 데이터는 없는지 확인
mongo --prot 27004
show dbs
테스트 db가 없는지 확인

use test
test.test1.insert({a:1})

하게 되면 에러가 발생하면서 인설트 되지 않는게 맞다

2.8 프라이머리를 죽였을때 아비터가 실제로 투표에 참여하는지 확인
27001 을 죽인다.
mongo --port 27002
에서 secondery 에서 primary 로 변경 되었는지 확인

2.9 우선 순위로 프라이머리로 설정 하고 싶은 마스터를 설정하기
기본적으로 priority 1 높으면 높을 수록 우선 순위를 가진다.
만약 priority 를 0 으로 하면 해당 멤버는 절대 primary 가 될수 없다.

rs.add({_id:4, host:"127.0.0.1:27003", priority:1.5})

접속 되있던 창은 primary -> secnodery 가 되고
mongo --port 27003으로 접속하면 우선 순위가 높은 애가 프라이머리가 되어 있다.

우선 순위는 rs.config() 에서 확인 가능 하다
{
"_id" : "rs0",
"version" : 25498,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 2,
"host" : "127.0.0.1:27004",
"arbiterOnly" : true
},
{
"_id" : 3,
"host" : "127.0.0.1:27002"
},
{
"_id" : 4,
"host" : "127.0.0.1:27003",
"priority" : 1.5
}
]
}

이번에는 더하지않고 그냥 프라이머리를 바꿔만 보자
var cfg = rs.config()
cfg.members[0].priority = 2
rs.reconfig(cfg)

mongo --port 27001
프라이머리 확인

3.0 멤버 숨기기
클라이언트에 보이기 싫은 멤버는 아래와 같이 hidden 과 priority 를
조절해서 안보이게 할 수 있다.

var cfg = rs.config()
config.members[1].hidden = 0
config.members[1].priority = 0
rs.recofig(cfg)

rs.isMaster() 에서 사라지게 된다.
클라이언트는 isMaster() 를 호출해서 복제 셋의 멤버를 확인 하기 때문에
해당 부분만 고치면된다.

보이게 할려면 해당 속성 삭제하고 리컨피그 하면된다.

3.1 슬레이브 지연
컨피그에 아래 두개의 속성 추가
slaveDelay = 60 * 60 *24 (하루)
priority = 0

3.2 인덱스 생성안하기
인덱스를 생성하지 않는다.

buildIndexes = false
priority = 0