회사에서 MongoDB 를 구축할 필요가 생겼다. 원래 Atlas 를 사용해서 구축할 예정이였으나, 관리 포인트가 늘어난다는 점 때문에 직접 MongoDB 를 구축 하기로 했다. AWS 에서도 NOSQL 기반 DB 를 서비스하고 있었는데, DocumentDB 는 너무 비싸고 외부 접속이 안 되며 DynamoDB 는 사용 목적과 달라서 직접 구축하기로 했다.
사전 지식
일단 나는 MongoDB 자체를 써 본 적이 없는 상황이라 MongoDB 및 NoSQL 에 대해서 간단히 정리가 필요했다.
NoSQL 장점
- 유연한 스키마를 제공하여 비정형 데이터에 적합하다.
- 분산형 클러스터를 사용하여 유연하고, 확장성 있게 설계하기 쉽다. 이로 인해 신뢰도가 증가한다.
NoSQL 단점
- DB 의 일관성이 부족하다.
- 데이터의 중복으로 인해 데이터 update 의 속도가 느리다.
결론적으로 보면 RDS 와 사용 목적이 완전히 반하는 것으로 생각하면 될 것 같아 보인다.
그렇다면 이제 MongoDB 를 가용성 있게 구축하는 방법에 대해서 알아보자.
MongoDB 설계
공식 문서를 찾아 보니 PSS 방식과 PSA 방식이 있다고 나온다.
PSS
- 하나의 프라이머리와, 적어도 두개의 세컨더리 멤버가 있다.
- 프라이머리는 두개의 세컨더리에 데이터를 복제 한다.
- 프라이머리가 죽을 경우, 세컨더리 중 하나가 투표를 통해 프라이머리 노드가 된다.


PAA
- 하나의 프라이머리 노드, 적어도 하나의 보조 노드, 그리고 아비터라는 중재자가 있다.
- 중재자는 데이터가 없지만 투표권이 존재한다.


나의 경우에는 조금 더 고가용성을 유지할 수 있는 PSS 방식을 선택했다.
아키텍쳐 설계
데이터가 적재되는 양, 가용성, 예산을 고려할 때 다음과 같은 방식이 추진되었다.
먼저 데이터 적재는 연 1GB 정도 될 예정이라, 각 인스턴스마다 100GB 의 EBS 볼륨을 할당했다. 그리고 예산을 최대한 절감하여 최소한으로 고가용성을 유지하기 위해 3개의 인스턴스를 실행한다. 각 인스턴스는 다른 AZ 의 서브넷에 존재하며, 각 인스턴스에 1개의 mongo 프로세스를 실행한다.
한개의 인스턴스에는 한개의 몽고 프로세스가 도커 위에서 실행되며, 실제 데이터는 EBS 볼륨에 마운트하여 도커 프로세스가 종료되어도 데이터는 그대로 보존되게 했다.
프라이머리는 27017 포트로 외부에서 접속 가능하게 설정했으며, IP 소스 기반으로 보안그룹에 화이트리스트를 설정했다. 나머지 두개의 서브넷에 있는 세컨더리 서브넷은 프라이빗 서브넷으로 설정했으며, 여기 접속하기 위해서는 Bastion host 를 사용해야 한다. 세컨더리 서브넷의 보안그룹은 내부 네트워크의 요청만 열어 두었다.
간단하게 각 노드의 인프라 환경은 다음과 같은 그림으로 나타낼 수 있다.

각 노드를 실행하기 위한 docker-compose 파일은 다음과 같다.
services:
mongo:
image: mongo:8.0.6
container_name: mongo
restart: always
network_mode: host
ports:
- "27017:27017"
volumes:
- ./data/db:/data/db
- ./data/configdb:/data/configdb
- ./mongo-keyfile:/etc/mongo-keyfile
environment:
MONGO_INITDB_ROOT_USERNAME: user
MONGO_INITDB_ROOT_PASSWORD: password
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--keyFile", "/etc/mongo-keyfile"]
각 설정의 뜻은 다음과 같다.
- network mode 를 host 로 설정해야 인스턴스와 네트워크망 동기화가 되어 각 노드끼리 통신이 가능하다.
- 포트는 기본 포트인 27017 을 사용한다.
- mongodb 의 데이터는 ebs 와 동기화 한다.
- rs0 의 이름을 가진 레플리카 셋을 설정한다.
- bind_ip_all 을 통해 ipv6 지원
- keyFile : 복제본 세트 인증 시 사용할 키파일
인스턴스에서 도커 프로세스를 킨 다음, 메인 노드에 접속해서 다음 명령어를 실행하여 레플리카 셋을 설정한다. 세컨더리 노드는 프라이머리 노드와 잘 연결이 된다면 메인 노드에서 보내는 신호로 인해 자동으로 세컨더리 노드가 설정이 된다.
# mongo shell 접속
# 복제본 활성화
rs.initiate()
# secondary node 의 정보 입력
rs.add({ host: "INTERNAL_IP:27017", priority: 0 })
rs.add({ host: "INTERNAL_IP:27017", priority: 0 })
프라이머리에서 rs.status() 를 실행했을 때 다음과 같은 내용이 뜬다면 PSS 구축이 완료가 된 것이다.
{
set: 'rs0',
members: [
{
stateStr: 'PRIMARY',
...etc
},
{
stateStr: 'SECONDARY',
lastHeartbeatRecv: ISODate('2025-04-11T08:47:48.650Z'),
...etc
},
{
stateStr: 'SECONDARY',
lastHeartbeatRecv: ISODate('2025-04-11T08:47:49.528Z'),
...etc
}
],
...etc
}
세컨더리 노드의 heartbeatRecv 가 현재 시간으로 잘 보인다면, 구축이 완료된 것이다. 각자 간단히 읽기 요청을 했을 때 데이터 복제가 잘 되는지 확인하고, 프라이머리 노드가 죽었을 때 세컨더리 노드가 잘 동작한다면 PSS 구축이 잘 된 것이다.
이제 여기에 SSL 설정과 정기적인 백업 시스템, 그리고 모니터링 시스템은 추후 추가할 예정이다.
'서버 인프라 > Aws' 카테고리의 다른 글
AWS 운영중인 도메인 호스팅 이전하기 (0) | 2024.10.01 |
---|---|
AWS IPv4 요금변경 (2) | 2024.03.03 |
VPC Peering 사용하기 (0) | 2023.09.23 |
SSH Bastion 안전하게 연결하기 (2) | 2023.07.14 |
AWS 솔루션 아키텍트 어소시에시트 합격 후기 (2) | 2023.06.02 |