January 29, 2023
MongoDB를 사용시, 동일한 데이터의 복제본을 여러 개 만들어서 데이터의 가용성을 높일 수 있다고 한다!
mongod MongoDB 시스템을 위한 primary daemon process replica set 같은 데이터셋을 유지하는 mongod의 그룹으로 데이터의 중복성을 제공하고, 가용성을 높인다. 위의 그림처럼 클라이언트는 secondary에 데이터를 쓸 수는 없지만, 읽을 수는 있다.
primary의 사용이 불가능해지면, secondary 중 어떤 것이 primary될지 election을 거친다.
heartbeat replicat set member끼리는 서로에게 매 2초마다 서로 ping을 보내서 상태를 확인한다.각 mongod 컨테이너가 작동할 docker network 생성
docker network create mongoCluster
mongod 컨테이너간의 인증, 클라이언트 access control 위해 keyfile 생성
openssl rand -base64 756 > mongodb.key // openssl 이용하여 공유 암호로 사용하기 위한 complex pseudo-random 1024 character string을 생성
chmod 400 mongodb.key //사용자에게만 read 권한 설정
docker-compose.yml에 3개의 mongodb 컨테이너 정보 작성
//docker-compose.yml
version: '3.8'
services:
mongo_1:
image: mongo:latest
container_name: mongo_1
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
ports:
- 27017:27017
volumes:
- ./data/db/replica/mongo_1:/data/db
- ./mongodb.key:/etc/mongodb.key
command:
- '--replSet'
- 'myReplicaSet'
- '--keyFile'
- '/etc/mongodb.key'
- '--bind_ip_all'
# command 내용
# replicaSet의 이름은 myReplicaSet으로 한다.
# keyFile은 /etc/mongodb.key를 사용한다.
# --bind_ip_all로 외부에서 클라이언트 접속이 가능하게 한다.
mongo_2:
image: mongo:latest
container_name: mongo_2
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
depends_on:
- mongo_1
ports:
- 27018:27017
volumes:
- ./data/db/replica/mongo_2:/data/db
- ./mongodb.key:/etc/mongodb.key
command:
- '--replSet'
- 'myReplicaSet'
- '--keyFile'
- '/etc/mongodb.key'
- '--bind_ip_all'
mongo_3:
image: mongo:latest
container_name: mongo_3
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
depends_on:
- mongo_2
ports:
- 27019:27017
volumes:
- ./data/db/replica/mongo_3:/data/db
- ./mongodb.key:/etc/mongodb.key
command:
- '--replSet'
- 'myReplicaSet'
- '--keyFile'
- '/etc/mongodb.key'
- '--bind_ip_all'
networks:
default:
name: mongoCluster컨테이너 실행
docker-compose up -d
➜ mongo_replication docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0640cfad95e0 mongo:latest "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:27019->27017/tcp mongo_3
3dc08fc9a3a5 mongo:latest "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:27018->27017/tcp mongo_2
34092fe360a8 mongo:latest "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:27017->27017/tcp mongo_1docker exec -it 34092fe360a8 /bin/sh → mongosh -u root -p root
현재처럼 초기화를 거치지 않은 상태에서 rs.status()를 입력시 no replset config has been received가 나오면서 별도의 config이 설정되지 않은 것을 확인할 수 있다.
rs.initiate({_id: "myReplicaSet", members: [{ _id: 0, host: "mongo_1" }, { _id: 1, host: "mongo_2" }, { _id: 2, host: "mongo_3" }]});를 입력하면
결과가 {ok:1}이 나오며 설정이 잘 완료된 것을 볼 수 있다.
3개의 컨테이너에 각각 들어가보면 primary, secondary, secondary로 설정된 것을 확인할 수 있다.

또한 rs.status()로 replica set의 config, 상태 등을 확인해볼 수도 있다.
primary에서 replicaTest라는 db를 생성하고, 하나의 document를 insert한다.
(insertOne이나 insertMany가 아닌 insert 자체는 deprecated되었다고 나오지만 작동은 가능했다.)
secondary에서 find 시도하면 primary가 아니라는 메세지가 나온다.
이 때 rs.secondaryOk()를 입력하면 조회가 가능하다.
기존에 사용하던 rs.slaveOk()와 마찬가지로 rs.secondaryOk()역시 deprecated라고 나왔다.
작동 자체는 가능하였기 때문에 여기서는 rs.slaveOk()로 find 결과를 얻을 수 있었고,
다른 secondary에서는db.getMongo().setReadPref('secondary') 로 같은 결과를 얻을 수 있었다.
당연하지만 이 때 꼭 어떠한 db를 사용할 것인지 명시한 후 (여기서는 use replicaTest) find를 해야 결과가 제대로 나온다.
use <db name> 없이 db.<db name>.find()를 하면 빈 결과가 나온다.
secondary에서 document를 삭제하려고 하면 not primary라고 에러가 나오는 것을 볼 수 있다.
primary(아래 이미지에서 왼쪽 터미널)에서 삭제 후 secondary(오른쪽 터미널)에서 확인하니 document가 없어진 것을 확인할 수 있다.
https://www.mongodb.com/docs/manual/replication/
https://www.mongodb.com/compatibility/deploying-a-mongodb-cluster-with-docker
https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-with-keyfile-access-control/