
MongoDB 는 버전 4.0부터 트랜잭션을 지원합니다. 하지만 여타 다른 DB와 다르게 트랜잭션을 사용하기 위해선 Replica Set 환경을 구성해야합니다. 이번 포스트에선 Replica Set에 대해 알아보며 Docker를 통해 환경을 구성하는 방법을 알아봅니다.
Primary: 레플리카 셋에서 프라이머리 노드는 단 하나만 존재 할 수 있습니다. 클라이언트에서 DB로 읽기 및 쓰기 작업을 진행합니다.Secondary: 프라이머리로부터 데이터를 동기화 합니다. 프라이머리의 장애 상황에서 어떤 세컨더리 노드를 프라이머리로 올릴것인지 투표권을 가지고 있습니다. 설정에 따라 프라이머리 노드의 읽기 작업을 분담할 수 있습니다.Arbiter: 데이터를 동기화하지는 않으며 투표권만을 가져 프라이머리 장애 발생시 레플리카 셋의 복구를 돕는 역할을 합니다. (현재 예제에선 Arbiter을 다루지 않습니다.)Heartbeat 라고 부르며, 해당 작업을 통해 노드 및 DB의 장애를 파악합니다. 만약 장애가 발견된다면 다음과 같은 작업을 수행합니다.
다음 이미지는 프라이머리 노드에 장애가 발생해 다운되었을 때 입니다. 장애가 발견되면 세컨더리 노드 중 하나를 프라이머리 노드로 올리게 되는데 이때 투표권을 가진 노드들이 선거를 통해 프라이머리 노드로 승격시킬 노드를 결정합니다.
첫 단계부터 차근차근 정리해보도록 하겠습니다.
1docker network create mongoCluster
mongoCluster는 생성될 network 이름입니다. 사용자가 자유롭게 변경할 수 있습니다.docker-compose를 사용합니다. 먼저 MongoDB Image를 다운로드하는 것부터 진행하겠습니다.1docker pull mongo
해당 Docker 명령어를 통해 MongoDB 최신 버전을 다운로드 합니다.
docker-compose.yml 파일을 구성하기 전 Replica Set 내부 인증을 위한 키 파일을 먼저 생성해야 합니다.1openssl rand -base64 756 > mongodb.key
2chmod 400 mongodb.key
docker-compose.yml 파일을 생성합니다. 중요한 부분만 잘라서 보겠습니다.1# docker-compose.yml
2version: '3.8'
3services:
4 mongo1:
5 image: mongo:latest
6 hostname: mongo1
7 container_name: mongo1
8 restart: always
9 ports:
10 - 27017:27017
11 volumes:
12 - ./data:/data/db
13 - ./mongodb.key:/etc/mongodb.key
14 command: 'mongod --replSet myReplicaSet --keyFile /etc/mongodb.key --bind_ip_all'
15 mongo2:
16 ...
17 mongo3:
18 ...
hostname과 volumes, command입니다.hostname/etc/hosts 파일에 다음과 같이 추가합니다.mongo1 127.0.0.1
mongo2 127.0.0.1
mongo3 127.0.0.1
volumesmongodb.key 파일을 컨테이너 복사합니다.command각 옵션단위로 분석해 보겠습니다.
--replSet: 연결할 Replica Set의 이름--keyFile: 연결할 키 파일 이름--bind_ip_all: 모든 ip 주소에 접근 가능하도록 설정합니다.전체 코드입니다.
1version: '3.8'
2services:
3 mongo1:
4 image: mongo:latest
5 hostname: mongo1
6 container_name: mongo1
7 restart: always
8 ports:
9 - 27017:27017
10 volumes:
11 - ./data:/data/db
12 - ./mongodb.key:/etc/mongodb.key
13 command: 'mongod --replSet myReplicaSet --keyFile /etc/mongodb.key --bind_ip_all'
14 mongo2:
15 image: mongo:latest
16 hostname: mongo2
17 container_name: mongo2
18 restart: always
19 depends_on:
20 - mongo1
21 ports:
22 - 27018:27017
23 volumes:
24 - ./data2:/data/db
25 - ./mongodb.key:/etc/mongodb.key
26 command: 'mongod --replSet myReplicaSet --keyFile /etc/mongodb.key --bind_ip_all'
27 mongo3:
28 image: mongo:latest
29 hostname: mongo3
30 container_name: mongo3
31 restart: always
32 depends_on:
33 - mongo2
34 ports:
35 - 27019:27017
36 volumes:
37 - ./data3:/data/db
38 - ./mongodb.key:/etc/mongodb.key
39 command: 'mongod --replSet myReplicaSet --keyFile /etc/mongodb.key --bind_ip_all'
40networks:
41 default:
42 name: mongoCluster
networks에 생성한 Docker Network를 연결해주고 dcoker-compose up -d 명령어를 실행합니다.1docker exec -it mongo1 mongosh --eval "rs.initiate({
2 _id: \"myReplicaSet\",
3 members: [
4 {_id: 0, host: \"mongo1\"},
5 {_id: 1, host: \"mongo2\"},
6 {_id: 2, host: \"mongo3\"}
7 ]
8})"
docker exec 명령어를 통해 컨테이너 내부에서 입력된 명령을 실행합니다. 해당 명령은 프라이머리 노드에서 한번 실행하면 됩니다.rs.initiate는 입력된 정보를 통해 Replica set을 구성하는 명령어 입니다. 내부 정보는 다음과 같습니다.1{
2 _id: "설정한 Replica set 이름",
3 members: [
4 {_id: 0, host: "설정한 hostname"},
5 ...
6 ]
7}
{ ok: 1 }이 반환된다면 성공입니다.rs.status() 명령어를 통해 구성원 목록을 포함하여 Replica set의 상태를 확인할 수 있습니다.1docker exec -it mongo1 mongosh --eval "rs.status()"
Primary 노드인 mongo1 이 종료되더라도 Secondary 노드인 mongo2 나 mongo3 이 Primary 노드로 변경되어 정상적으로 실행됩니다.1docker stop mongo1
2docker exec -it mongo2 mongosh --eval "rs.status()"
앞으로 관련 글로 한 개의 글을 더 작성할 예정입니다. 다음 주제는 NestJS에 Transaction을 적용하면서 겪은 고민과 해결과정에 대해 정리할 예정입니다.
감사합니다.