지난 시간에는 MongoDB의 성능을 최적화하는 방법, 특히 인덱싱과 쿼리 분석의 중요성에 대해 배웠습니다. 빠른 성능만큼 중요한 것이 바로
서비스의 안정성
입니다.
데이터베이스 서버에 문제가 생겨도 서비스가 중단되지 않고 계속 동작하도록 보장하는 것, 즉
고가용성(High Availability)
을 확보하는 것이 매우 중요합니다.
MongoDB에서는 이 고가용성을 위해
복제 세트(Replica Set)
라는 강력한 기능을 제공합니다. 우리는 이미 2일차 아키텍처 시간에 복제 세트의 개념에 대해 간략히 살펴보았습니다.
오늘은 그 개념을 넘어, 실제로 우리 컴퓨터에서 복제 세트를
어떻게 구성하고 관리하는지
, 그리고 가장 중요한 역할인
장애 복구(Failover)
가 어떻게 이루어지는지 직접 확인해보는 실습 시간을 갖겠습니다.
본격적인 실습 전에 복제 세트의 핵심 개념을 다시 한번 정리해 봅시다.
복제 세트란 동일한 데이터셋을 유지하는 여러 개의 MongoDB 서버(mongod 프로세스) 그룹입니다.
데이터의 중복성(Redundancy)을 제공하여 고가용성을 확보하고, 데이터의 안정성을 높입니다.
프라이머리(Primary)
: 오직 하나만 존재하며, 모든 쓰기(Write) 작업을 처리합니다. 기본적으로 읽기(Read) 작업도 처리합니다.
세컨더리(Secondary)
: 여러 개 존재할 수 있으며, 프라이머리의 데이터를 비동기적으로 복제합니다. 읽기 작업을 분담할 수 있습니다. 프라이머리에 문제가 생기면 세컨더리 중에서 새로운 프라이머리가 선출됩니다.
(선택 사항) 아비터(Arbiter)
: 데이터는 저장하지 않고, 프라이머리 선출 과정에서 투표 역할만 수행하는 특별한 멤버입니다.
복제 세트 구성도
🚀
로컬 환경에서 복제 세트 구성 실습하기 🔗
실제 운영 환경에서는 여러 대의 물리적 서버에 MongoDB 인스턴스를 분산하여 구성하지만, 학습 목적으로는 우리 컴퓨터 한 대에서 여러 개의 mongod 인스턴스를 실행하여 복제 세트를 시뮬레이션해 볼 수 있습니다.
✅
1단계: 여러 mongod 인스턴스 실행 준비 🔗
로컬에서 여러 mongod 인스턴스를 동시에 실행하려면 각 인스턴스가 서로 다른
포트(port)
번호,
데이터 디렉토리(dbpath)
,
로그 파일 경로(logpath)
를 사용하도록 설정해야 합니다.
예를 들어, 3개의 멤버(Primary 1, Secondary 2)로 구성된 복제 세트를 만든다고 가정해 봅시다. 각 멤버에 대한 디렉토리를 미리 생성합니다.
Linux/MacOS 터미널 mkdir -p ~/mongodb/rs/node1/data ~/mongodb/rs/node1/log
mkdir -p ~/mongodb/rs/node2/data ~/mongodb/rs/node2/log
mkdir -p ~/mongodb/rs/node3/data ~/mongodb/rs/node3/log
Windows PowerShell mkdir ~/mongodb/rs/node1/data, ~/mongodb/rs/node1/log
mkdir ~/mongodb/rs/node2/data, ~/mongodb/rs/node2/log
mkdir ~/mongodb/rs/node3/data, ~/mongodb/rs/node3/log
이제 각 노드에 대한 설정 파일(mongod.conf)을 생성해줍니다.
위도우는
C:\Users\your_username\mongodb\rs\node1\mongod.conf
와 같이 절대 경로를 사용해야 합니다. (Linux/MacOS는 상대 경로 사용 가능)
~/mongodb/rs/node1/mongod.conf # 스토리지 설정
storage :
dbPath : /Users/your_username/mongodb/rs/node1/data # node1 데이터 경로 (절대 경로 권장)
# dbPath: C:\Users\your_username\mongodb\rs\node1\data # 윈도우용 절대 경로
# 시스템 로그 설정
systemLog :
destination : file
path : /Users/your_username/mongodb/rs/node1/log/mongod.log # node1 로그 경로
# path: C:\Users\your_username\mongodb\rs\node1\log\mongod.log # 윈도우용 절대 경로
logAppend : true
# 네트워크 설정
net :
port : 27017 # node1 포트
bindIp : 127.0.0.1 # 로컬에서만 접속 허용 (보안상 중요)
# 복제 설정
replication :
replSetName : "myReplicaSet" # 복제 세트 이름 (모든 멤버가 동일해야 함)
(node1 설정 파일에서 dbPath
, path
, port
만 변경)
~/mongodb/rs/node2/mongod.conf # 스토리지 설정
storage :
dbPath : /Users/your_username/mongodb/rs/node2/data # node2 데이터 경로 (절대 경로 권장)
# dbPath: C:\Users\your_username\mongodb\rs\node2\data # 윈도우용 절대 경로
# 시스템 로그 설정
systemLog :
destination : file
path : /Users/your_username/mongodb/rs/node2/log/mongod.log # node2 로그 경로
# path: C:\Users\your_username\mongodb\rs\node2\log\mongod.log # 윈도우용 절대 경로
logAppend : true
# 네트워크 설정
net :
port : 27018 # node2 포트
bindIp : 127.0.0.1 # 로컬에서만 접속 허용 (보안상 중요)
# 복제 설정
replication :
replSetName : "myReplicaSet" # 복제 세트 이름 (모든 멤버가 동일해야 함)
(node1 설정 파일에서 dbPath
, path
, port
만 변경)
~/mongodb/rs/node3/mongod.conf # 스토리지 설정
storage :
dbPath : /Users/your_username/mongodb/rs/node3/data # node3 데이터 경로 (절대 경로 권장)
# dbPath: C:\Users\your_username\mongodb\rs\node3\data # 윈도우용 절대 경로
# 시스템 로그 설정
systemLog :
destination : file
path : /Users/your_username/mongodb/rs/node3/log/mongod.log # node3 로그 경로
# path: C:\Users\your_username\mongodb\rs\node3\log\mongod.log # 윈도우용 절대 경로
logAppend : true
# 네트워크 설정
net :
port : 27019 # node3 포트
bindIp : 127.0.0.1 # 로컬에서만 접속 허용 (보안상 중요)
# 복제 설정
replication :
replSetName : "myReplicaSet" # 복제 세트 이름 (모든 멤버가 동일해야 함)
🖐️
위 경로에서 your_username 부분은 실제 사용자 이름으로 변경해야 합니다.
bindIp
를
127.0.0.1
로 설정하면 외부에서는 접속할 수 없습니다. 실제 운영 환경에서는 서버 IP 등을 적절히 설정해야 합니다.
최종적으로 mongodb 디렉토리 구조는 다음과 같아야 합니다.
📦rs
┣ 📂node1
┃ ┣ 📂data
┃ ┣ 📂log
┃ ┗ 📜mongod.conf
┣ 📂node2
┃ ┣ 📂data
┃ ┣ 📂log
┃ ┗ 📜mongod.conf
┗ 📂node3
┃ ┣ 📂data
┃ ┣ 📂log
┃ ┗ 📜mongod.conf
✅
2단계: 각 mongod 인스턴스 실행 🔗
이제 각 설정 파일을 사용하여 3개의 mongod 인스턴스를 실행합니다. 터미널 창을 3개 열고 각각 다음 명령어를 실행합니다.
Linux/MacOS 터미널 # 터미널 1: node1 실행
mongod --config ~/mongodb/rs/node1/mongod.conf
# 터미널 2: node2 실행
mongod --config ~/mongodb/rs/node2/mongod.conf
# 터미널 3: node3 실행
mongod --config ~/mongodb/rs/node3/mongod.conf
Windows PowerShell # 터미널 1: node1 실행
mongod --config $HOME /mongodb/rs/node1/mongod.conf
# 터미널 2: node2 실행
mongod --config $HOME /mongodb/rs/node2/mongod.conf
# 터미널 3: node3 실행
mongod --config $HOME /mongodb/rs/node3/mongod.conf
mongod 프로세스 3개 실행행
✅
3단계: 복제 세트 초기화 및 멤버 추가 🔗
이제 3개의 mongod 인스턴스가 각각 실행 중입니다. 하지만 아직 서로를 모르고 복제 세트로 묶이지 않은 상태입니다. 이제 MongoDB Shell(mongosh)을 사용하여 복제 세트를 구성해야 합니다.
첫 번째 노드에 접속
: 실행 중인 노드 중 하나(예: node1 - 포트 27017)에 mongosh로 접속합니다.
복제 세트 초기화 (rs.initiate
)
: 접속한 노드를 첫 번째 멤버로 하여 복제 세트를 초기화합니다.
_id
에는 복제 세트 이름을,
members
배열에는 초기 멤버 정보를 넣어줍니다.
// 초기화 설정 객체 정의
config = {
_id: "myReplicaSet" , // 설정 파일의 replSetName과 동일하게
members: [
{ _id: 0 , host: "127.0.0.1:27017" } // 현재 접속한 노드 정보
]
}
// 복제 세트 초기화 실행
rs. initiate (config)
성공하면 ok: 1
이 반환되고, 프롬프트가 복제 세트 이름과 현재 역할을 표시하도록 변경됩니다 (예: myReplicaSet [primary] >
).
첫 노드 rs.initiate() 완료 시
여기서 rs.status() 를 실행하면 현재 노드의 상태를 확인할 수 있고, [direct: primary] 라는 메시지가 표시됩니다. (직접 연결된 노드가 Primary라는 의미)
rs.status() 실행 시시
멤버 추가 (rs.add
)
: 이제 나머지 두 노드(node2, node3)를 복제 세트에 추가합니다.
rs. add ( "127.0.0.1:27018" ) // node2 추가
rs. add ( "127.0.0.1:27019" ) // node3 추가
각 멤버를 추가할 때마다 ok: 1
이 반환됩니다.
복제 세트가 잘 구성되었는지 확인해 봅시다.
이제 여러분은 3개의 멤버로 구성된 복제 세트를 성공적으로 구성했습니다!
Primary 노드에 데이터를 쓰면 잠시 후 Secondary 노드들에도 해당 데이터가 복제되는 것을 확인할 수 있습니다.
Primary에서 db.test.insertOne({ a: 1 })
실행 후, Secondary 노드에 접속하여 db.test.find()
로 확인합니다.
// Primary 노드에서 데이터 삽입
use test
db.test. insertOne ({ a: 1 })
// Secondary 노드에서 데이터 확인
use test
db.test. find ()
Secondary 노드에서 데이터 확인
🚀
장애 복구 (Failover) 테스트하기 🔗
복제 세트의 핵심 기능인 자동 장애 복구를 테스트해 봅시다.
현재 Primary 확인
: rs.status()
를 실행하여 어떤 노드가 현재 Primary인지 확인합니다. (예: 127.0.0.1:27017
)
Primary 노드 중지
: 해당 Primary 노드를 실행 중인 터미널 창에서 Ctrl + C
를 눌러 mongod 프로세스를 강제로 종료시킵니다.
상태 변화 관찰
: 다른 노드(Secondary)에 접속된 mongosh 에서 rs.status()
를 주기적으로 실행하며 상태 변화를 관찰합니다.
기존 Primary의 상태가 (not reachable/healthy)
로 변경됩니다.
남아있는 Secondary 노드들 사이에서 선거(Election)
가 시작됩니다.
잠시 후, Secondary 중 하나가 새로운 PRIMARY
로 선출되고, 나머지 노드는 SECONDARY
역할을 유지합니다. 이 과정을 Failover
라고 합니다.
Failover 과정
기존 Primary 재시작
: 중지했던 mongod 프로세스를 다시 실행합니다. (이전 터미널에서 명령어 다시 입력)
역할 확인
: 복제 세트에 다시 참여한 노드는 이제 SECONDARY
역할을 맡게 됩니다. rs.status()
로 확인합니다.
중지했던 노드 재시작 후
이 과정을 통해 Primary 노드에 문제가 생겨도 복제 세트가 자동으로 새로운 Primary를 선출하여 서비스 중단을 최소화하는 것을 확인할 수 있습니다.
복제 세트 멤버는 보통 홀수로 구성하는 것이 권장됩니다. 왜냐하면 Primary 선출 투표 시 과반수 동의가 필요하기 때문입니다.
멤버가 짝수일 경우, 네트워크 문제 등으로 두 그룹으로 나뉘면 어느 쪽도 과반수를 확보하지 못해 새로운 Primary를 선출하지 못하는
스플릿 브레인(Split-Brain)
상황이 발생할 수 있습니다.
하지만 데이터 복제본을 저장할 서버 자원이 부족하거나 비용 문제로 멤버를 홀수로 구성하기 어려울 때,
아비터(Arbiter)
노드를 사용할 수 있습니다.
역할
- 데이터는 저장하지 않고 오직 투표권만 행사합니다. (리소스를 거의 차지하지 않음)
추가 방법
- 일반 멤버처럼 rs.addArb("호스트명:포트")
명령어로 추가합니다.
주의점
- 아비터는 데이터 복제본이 없으므로 고가용성에는 기여하지만 데이터 이중화에는 기여하지 못합니다. 또한 아비터 자체에 문제가 생기면 투표에 영향을 줄 수 있습니다. 가능하면 데이터 복제본을 가진 멤버(Secondary)를 홀수로 구성하는 것이 가장 좋습니다.
🚀
읽기 설정 (Read Preference) 🔗
기본적으로 복제 세트에서는 모든 읽기 요청이 Primary 노드로 전달됩니다. 하지만 읽기 작업 부하를 분산하거나 특정 상황에 맞는 읽기 동작을 원할 경우
읽기 설정(Read Preference)
을 지정할 수 있습니다.
primary
(기본값): 항상 Primary에서만 읽습니다. 가장 최신 데이터를 보장합니다.
primaryPreferred
: 주로 Primary에서 읽지만, Primary가 사용 불가능하면 Secondary에서 읽습니다.
secondary
: 항상 Secondary에서만 읽습니다. (Primary 부하 분산 목적)
secondaryPreferred
: 주로 Secondary에서 읽지만, 모든 Secondary가 사용 불가능하면 Primary에서 읽습니다.
nearest
: 네트워크 지연 시간이 가장 짧은 멤버(Primary 또는 Secondary)에서 읽습니다.
읽기 설정은 MongoDB 드라이버(애플리케이션 코드) 레벨에서 연결 시 지정하거나, mongosh 에서
Mongo.setReadPref()
명령어로 설정할 수 있습니다.
Secondary에서 읽을 경우, 데이터 복제 지연으로 인해 아주 약간 최신이 아닌 데이터를 읽을 수도 있다는 점(최종 일관성)을 이해해야 합니다.
오늘은 MongoDB의 고가용성을 책임지는 핵심 기능인 복제 세트(Replica Set)를 직접 구성하고 관리하는 방법을 실습을 통해 알아보았습니다.
로컬 환경에서 여러 mongod 인스턴스를 설정하고, 복제 세트를 초기화하며 멤버를 추가하는 과정을 거쳤습니다.
또한, Primary 노드 장애 시 자동으로 새로운 Primary를 선출하는 Failover 과정을 직접 확인하며 복제 세트의 강력함을 체감했습니다. 아비터 노드의 역할과 읽기 설정(Read Preference)에 대해서도 간략히 살펴보았습니다.
복제 세트를 올바르게 구성하고 관리하는 것은 MongoDB 기반 서비스의 안정성을 보장하는 데 필수적입니다. 오늘 배운 내용을 바탕으로 실제 환경에서도 안정적인 MongoDB 운영을 위한 기반을 다지시길 바랍니다.
다음 시간에는 MongoDB의 또 다른 고급 기능인
샤딩(Sharding)
에 대해 알아보겠습니다. 대규모 데이터를 여러 서버에 분산하여 처리하는 샤딩 클러스터를 어떻게 구성하고 운영하는지 배우는 시간이 될 것입니다.