[MongoDB] Ch13 - 관리
복제 셋 관리를 살펴보자
- 개별 멤버에 유지 보수 수행하기
- 다양한 환경에서 복제 셋 구성하기
- 사용자 oplog 정보를 얻고 크기 조정하기
- 좀 더 색다른 복제 셋 구성하기
- 마스터/슬레이브 구조에서 복제 셋으로 전환하기
13.1 독립 실행형 모드에서 멤버 시작
쓰기와 관련된 많은 유지보수 작업은 세컨더리에서 수행할 수 없고 애플리케이션 성능에 영향을 미치므로 프라이머리에서 수행도 어렵다
독립 실행형 모드에서 멤버를 시작하려면 먼저 명령행 인수를 확인해야 한다
replSet 옵션 없이 서버를 재시작한다
다른 멤버들이 서버를 발견하지 못하도록 서버가 다른 포트로 수신하게 된다
dbpath는 그대로 유지한다
유지 보수 수행을 마치면, 서버를 종료하고 원래 옵션으로 재시작한다
13.2 복제 셋 구성
복제 셋 구성은 항상 local.system.replset 컬렉션의 도큐먼트에 보관한다
이 도큐먼트는 복제 셋의 모든 멤버에서 같다
절대 update를 이용해서 도큐먼트를 변경하지 말자 대신 항상 rs 보조자나 replSetReconfig 명령을 사용하자
(1) 복제 셋 생성하기
var config = {
"_id" : <setName>,
"members" : [
{"_id":0, "host":<host1>},
{"_id":1, "host":<host2>},
{"_id":2, "host":<host3>}
]}
}
rs.initiate(config)
항상 config 객체를 rs.initiate()에 전달해야 한다
그렇지 않으면 몽고DB는 자동으로 단일 멤버 복제 셋을 위한 config를 생성한다
(2) 복제 셋 멤버 교체하기
복제 셋에 새로운 멤버를 추가할 때는 디렉터리에 아무것도 존재하지 않거나 다른 멤버 데이터의 복제본이 있어야 한다
(3) 큰 복제 셋 만들기
복제 셋에는 멤버는 50개, 투표 멤버는 7개로 제한된다
다른 멤버에 하트비트를 보내는 데 필요한 네트워크 트래픽량을 줄이고 선출에 걸리는 시간을 제한하기 위함이다
멤버가 7개 이상인 복제 셋을 생성한다면, 7개를 제외한 다른 멤버는 투표권이 0개여야 한다
rs.add({"_id":7, "host":"server-7:27017", "votes":0})
이는 해당 멤버들이 선출 과정에서 투표권을 행사하는 것을 방지한다
(4) 재구성 강제하기
복제 셋의 과반수를 영구적으로 잃으면, 프라이머리가 없는 상태에서 설정을 재구성할 필요가 있다
일반적으로 프라이머리에 재구성을 요청하지만 현재 프라이머리가 없다
이때 세컨더리에 재구성 명령을 보냄으로써 복제 셋 재구성을 강제할 수 있다
셸에서 세컨더리에 연결하고 아래와 같이 재구성 명령을 전달할 수 있다
rs.reconfig(config, {"force":true})
강제 재구성은 일반 재구성과 같은 규칙을 따른다
사용자는 유효하고 잘 구성된 구성 정보를 올바른 옵션으로 보내야 한다
‘force’ 옵션은 유효하지 않은 구성 정보를 허용하지 않고, 세컨더리가 재구성 정보를 받아들이도록 허용한다
세컨더리는 재구성 정보를 받으면 자신의 구성 정보를 갱신하고 새로운 구성 정보를 다른 멤버에 전달한다
복제 셋의 다른 멤버는, 정보를 전송하는 서버를 현재 구성 정보의 멤버로 인식하면 구성의 변화만을 찾아낸다
따라서 일부 멤버가 호스트명을 변경하면 강제 재구성해야 한다
모든 멤버가 새로운 호스트명을 가지면 복제 셋의 각 멤버를 다운시키고, 새로운 멤버를 독립 실행형 모드로 시작한 뒤, local.system.replset 도큐먼트를 수동으로 변경하고 멤버를 재시작한다
13.3 멤버 상태 조작
유지 보수를 수행할 때나 부하에 대한 응답으로 수동으로 멤버 상태를 변경하는 방법은 여러가지다
멤버가 프라이머리가 되도록 강제하려면 복제 셋을 상황에 맞게 구성하는 것 외에는 방법이 없다
(다름 멤버보다 높은 우선순위를 부여한다)
(1) 프라이머리에서 세컨더리로 변경하기
stepDown 함수를 이용하면 프라이머리를 세컨더리로 강등할 수 있다
rs.setpDown()
프라이머리를 60초 동안 세컨더리 상태로 만든다 (default : 60초)
그 기간 동안 다른 프라이머리가 선출되지 않으면 세컨더리 상태로 변했던 프라이머리는 재선출을 시도할 수 있다
# 60초가 아닌 다른 시간으로 변경하고 싶으면 아래와 같이 변경이 가능하다
rs.stepDown(600) # 10분
(2) 선출 방지하기
프라이머리상에서 유지 보수 작업이 필요할 때, 그 사이에 자격이 있는 다른 멤버가 프라이머리가 되지 않도록 하려면, 각각의 멤버에 freeze 명령을 실행함으로써 세컨더리 상태에 머물게 한다
rs.freeze(10000) # 10000초 동안 멤버는 세컨더리 상태로 유지된다
시간이 경과하기 전에 해지하려면, 각 멤버의 타임아웃을 0초로 지정해 명령을 다시 실행한다
rs.freeze(0) # freeze 상태를 해지할 수 있다
13.4 복제 모니터링
복제 셋의 상태를 모니터링하는 것은 중요하다
복제와 관련된 문제는 일시적일 때가 많다
서버가 다른 서버에 도달할 수 없었다가 다시 도달할 수 있게 될 때가 있다
로그를 확인하면 이 같은 문제를 쉽게 볼 수 있다
로그가 어디에 저장되는지 확인하고, 로그가 잘 저장되며 해당 로그에 접근할 수 있음을 확실히 확인해야 한다
(1) 상태 정보 가져오기
복제 셋의 모든 멤버의 정보를 얻는 데 가장 유용한 명령은 replSetGetStatus 다
(2) 복제 그래프 시각화하기
세컨더리에서 rs.status()를 실행하면 “syncingTo”라는 최상위 필드를 확인할 수 있다
이는 멤버가 복제를 수행하는 호스트를 제공한다
serve1.adminCommand({replSetGetStatus:1})['syncingTo']
# > server0:27017
## server0은 server1의 복제소스
몽고DB는 핑 시간을 기준으로 동기화할 대상을 결정한다
멤버는 다른 멤버에 하트비트를 보낼 때, 요청이 처리되기까지 걸리는 시간을 잰다
몽고DB는 이러한 평균 실행 시간을 저장한다
동기화할 멤버를 선택할 때, 멤버는 가장 가깝고 복제에서 자기보다 앞서 있는 멤버를 찾는다
(따라서 복제 순환이 발생하지 않는다. 멤버는 자신보다 앞서 있는 프라이머리나 세컨더리만 복제)
(3) 복제 루프
복제 루프는 모든 멤버가 다른 멤버로부터 복제를 수행하는 상태다
예를 들어 A는 B로부터 동기화하고, B는 C로부터, C는 A로부터 동기화하는 상태를 말한다
모든 복제 루프 멤버는 프라이머리가 될 수 없으므로, 멤버들은 복제를 위한 새로운 명령을 받을 수 없고 뒤처지게 된다
replSetSyncFrom 명령을 이용해 복제 루프를 강제로 수행할 수 있다
(4) 복제 사슬 비활성하기
복제 사슬은 세컨더리가 또 다른 세컨더리와 동기화할 때 발생한다
멤버는 자동으로 다른 멤버와 동기화하도록 결정할 수 있다
chainingAllowed 설정을 false로 변경해 (default : true) 모든 멤버가 프라이머리와 동기화하게 함으로써 복제 사슬을 사용하지 않도록 설정할 수 있다
false로 설정하면 모든 멤버는 프라이머리와 동기화된다
(프라이머리가 이용 불가능한 상태가 되면 세컨더리와 동기화하게 된다)
(5) 지연 계산하기
복제를 추적하는 지표로, 세컨더리가 얼마나 프라이머리를 잘 따라잡는지가 중요하다
지연은 세컨더리가 얼마나 뒤쳐져 있는지 나타내는데, 프라이머리가 마지막으로 수행한 연산과 세컨더리가 마지막으로 적용한 연산의 타임스탬프 차이를 의미한다
rs.printReplicationInfo()는 연산의 크기와 날짜 범위를 포함하는 프라이머리 oplog의 요약 정보를 제공한다
(6) Oplog 크기 변경하기
프라이머리의 oplog는 유지 보수 시간으로 여겨진다
프라이머리의 oplog 길이가 한 시간 정도라면, 잘못된 부분을 고칠 수 있는 시간이 한 시간 정도라는 의미다
일반적으로 며칠에서 1주 정도 데이터를 보관할 수 있는 oplog가 바람직하다
와이드타이거 스토리지 엔진을 사용하면 서버가 실행되는 동안 oplog 크기를 조정할 수 있다
프라이머리가 될 수 있는 서버는 충분히 큰 oplog가 필요하다
- oplog 크기 늘리기
# 1. 복제 셋 멤버에 연결, 인증이 활성화되면 local 데이터베이스를 수정할 권한이 있는 사용자를 사용
# 2. oplog 현재 크기를 확인
use local
db.oplog.rs.status(1024*1024).maxSize
# 3. 복제 셋 멤버의 oplog 크기를 변경
db.adminCommand({replSetResizeOplog: 1, size: 16000}_
## (연산은 복제 셋 멤버의 oplog 크기를 16기가 바이트(혹은 1만6000메가바이트)로 변경
# 4. oplog 크기를 줄였다면 compact를 실행해 할당된 디스크 공간을 회수해야 할 수도 있다
# 하지만 대상 멤버가 프라이머리인 동안에는 실행해서는 안 된다
일반적으로 oplog 크기를 줄이면 안 된다
oplog 크기가 수개월이더라도 일반적으로 디스크 공간은 충분하며 램이나 CPU 같은 귀중한 리소스를 소모하지는 않는다
(7) 인덱스 구축하기
프라이머리에 인덱스 구축을 전송하면 프라이머리는 정상적으로 인덱스를 구축하며, 세컨더리는 build index 연산을 복제할 때 인덱스를 구축한다
하지만 이런 방법은 한 가지 문제가 있다
만약 모든 세컨더리가 동시에 인덱스를 구축하기 시작한다면 복제 셋의 거의 모든 멤버는 인덱스 구축이 완료될 때까지 오프라인 상태가 된다
이 과정은 복제 셋에만 해당된다. 샤드 클러스터의 경우, 샤드 클러스터에서 인덱스를 구축하는 방법은 공식 홈페이지를 확인해보자
따라서 애플리케이션에 대한 영향을 최소화하려면 인덱스는 한 번에 한 멤버씩 구축하는 것이 바람직하다
- 세컨더리를 종료한다
- 종료한 세컨더리를 독립 실행형 서버로 재시작한다
- 재시작한 서버에 인덱스를 구축한다
- 인덱스 구축이 완료되면 서버를 복제 셋 멤버로 재시작한다
- 복제 셋의 각 세컨더리에 1단계부터 4단계까지 반복한다
다만 인덱스가 다른 멤버는 절대 프라이머리가 될 수 없으며 우선순위가 항상 0임을 기억하자
고유 인덱스를 구축할 때는 프라이머리가 중복 삽입을 하지 않아야 하며, 인덱스를 프라이머리에 먼저 구축해야 한다
그렇지 않으면 프라이머리는 중복 삽입을 하게 되고 세컨더리에 복제 오류를 발생시킨다
(8) 한정된 예산에서 복제하기
고성능 서버를 2개 이상 구하기 어렵다면, 적은 램과 CPU, 속도가 느린 디스크 입출력을 갖는 재해 복구용 세컨더리 서버를 고려해보자
좋은 서버는 항상 프라이머리로 쓰고, 상대적으로 값싼 서버는 절대 클라이언트 트래픽을 처리하지 않게 한다 (모든 읽기 요청을 프라이머리에 보내도록 클라이언트를 구성한다)
값싼 서버 설정값
priority : 0 # 해당 서버가 절대 프라이머리가 되지 않도록 설정
hidden : true # 클라이언트가 해당 세컨더리에 읽기 요청을 절대 보내지 않도록 설정
# 선택적이지만, 해당 서버가 처리해야 하는 부하를 상당히 줄일 수 있음
# 이 서버로부터 복원할 경우 인덱스를 재구축해야 한다
buildIndexes : false
# 서버가 두 개뿐이라면 세컨더리의 votes를 0으로 설정
# 해당 서버가 다운되더라도 프라이머리가 프라이머리로 유지되도록 한다
# 만약 3개의 서버가 있다면 해당 서버의 votes를 0으로 설정하는 대신 아비터를 실행한다
votes : 0
이러한 세컨더리가 있으면 두 개의 고성능 서버 없이도 안정성과 보안성을 얻을 수 있다