| 항목 | 내용 |
|---|---|
| OS | Ubuntu 22.04 LTS (jammy) |
| 권한 | sudo 사용자 |
| 네트워크 | 인터넷 연결 필수 |
| MongoDB 버전 | 8.0 |
sudo apt update
sudo apt install -y gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg
ls -l /usr/share/keyrings/mongodb-server-8.0.gpg
➡ 파일이 존재하면 정상
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt update
sudo apt install -y mongodb-org
설치 확인:
mongod --version
sudo systemctl enable --now mongod
systemctl status mongod --no-pager
접속 테스트:
mongosh --eval 'db.runCommand({ ping: 1 })'
결과:
{ ok: 1 }
mongosh
use lab // DB자동 생성
// 기존 테이블이 있다면 삭제
db.customers.drop()
db.orders.drop()
db.customers.insertMany([
{ customerId: 1, name: "Kim", region: "seoul", tier: "gold" },
{ customerId: 2, name: "Lee", region: "busan", tier: "silver" },
{ customerId: 3, name: "Park", region: "seoul", tier: "gold" },
{ customerId: 4, name: "Choi", region: "incheon", tier: "bronze" }
])
MongoDB에서는 JOIN 대신 문서 중첩(Embedded) 구조를 사용한다.
db.orders.insertMany([
{
orderId: 1001,
status: "PAID",
orderedAt: ISODate("2026-01-01"),
customer: { name: "Kim", region: "seoul" },
amount: 120000
},
{
orderId: 1002,
status: "CART",
orderedAt: ISODate("2026-01-05"),
customer: { name: "Lee", region: "busan" },
amount: 99000
},
{
orderId: 1003,
status: "PAID",
orderedAt: ISODate("2026-01-10"),
customer: { name: "Park", region: "seoul" },
amount: 150000
}
])
SELECT name, tier
FROM customers
WHERE tier = 'gold';
db.customers.find(
// where 역할
{ tier: "gold" },
// id 제외, name,tier 포함 (select 역할)
{ _id: 0, name: 1, tier: 1 }
)
db.orders.find(
{ status: "PAID" },
{ _id: 0, orderId: 1, amount: 1, orderedAt: 1 }
).sort({ orderedAt: -1 }).limit(5)
SELECT customer, SUM(amount)
FROM orders
WHERE status = 'PAID'
GROUP BY customer;
db.orders.aggregate([
{ $match: { status: "PAID" } },
{
$group: {
_id: "$customer.name",
totalAmount: { $sum: "$amount" }
}
}
])
{ name: 1, tier: 1, region: 0 }
{ _id: 0, name: 1, tier: 1 }
_id만 예외적으로 제외 가능db.orders.find({ status: "PAID" }).explain("executionStats")
확인 포인트:
COLLSCANtotalDocsExamineddb.orders.createIndex({ status: 1, orderedAt: -1 })
db.orders.find({ status: "PAID" })
.sort({ orderedAt: -1 })
.explain("executionStats")
IXSCAN으로 변경 확인
예시(수정 가능):
mongo1 (IP: 192.168.80.110)mongo2 (IP: 192.168.80.120)mongo3 (IP: 192.168.80.130)Replica Set 이름: rs0
모든 노드: Data node (Primary/Secondary)
rs.status(), db.hello()로 상태 점검 가능sudo timedatectl set-timezone Asia/Seoul
sudo apt update
sudo apt install -y chrony
sudo systemctl enable --now chrony
timedatectl
3대 모두 동일하게 추가:
sudo tee -a /etc/hosts >/dev/null <<'EOF'
192.168.80.110 mongo1
192.168.80.120 mongo2
192.168.80.130 mongo3
EOF
확인:
getent hosts mongo1 mongo2 mongo3
예: mongo1에서
ping -c 2 mongo2
ping -c 2 mongo3
1장에서 했던 설치 방식과 동일.
sudo rm -f /etc/apt/sources.list.d/mongodb-org-*.list
sudo rm -f /usr/share/keyrings/mongodb-*.gpg
sudo apt update
sudo apt install -y gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] \
https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" \
| sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt update
sudo apt install -y mongodb-org
버전 확인:
mongod --version
mongosh --version
sudo cp /etc/mongod.conf /etc/mongod.conf.bak
/etc/mongod.conf 수정 (중요)아래 항목이 반드시 들어가야 함:
# /etc/mongod.conf
net:
port: 27017
bindIp: 0.0.0.0 # 실습용 (운영에서는 내부망 IP로 제한 권장)
replication:
replSetName: rs0
sudo systemctl enable --now mongod
sudo systemctl restart mongod
sudo systemctl status mongod --no-pager
UFW를 켜둔 환경이라면 3대 모두에서(내부망만 허용)
sudo ufw allow from 10.0.0.0/24 to any port 27017 proto tcp
sudo ufw status
mongosh
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
})
rs.status()
확인 :
stateStr가 PRIMARY 1개, SECONDARY 2개인지mongo1에서:
db.hello()
출력 포인트:
isWritablePrimary: true 인 노드가 PRIMARY다른 노드에서도 확인(각 서버에서 mongosh 실행):
mongosh --eval 'db.hello()'
(현재 PRIMARY인 서버에서 실행)
use lab
db.replica_test.insertMany([
{ msg: "write1", ts: new Date() },
{ msg: "write2", ts: new Date() }
])
db.replica_test.find()
Secondary 노드에서:
mongosh
rs.secondaryOk()
use lab
db.replica_test.find()
확인 :
아무 노드에서나:
rs.status().members.map(m => ({name:m.name, stateStr:m.stateStr}))
예: Primary가 mongo1이라고 가정
Primary 서버에서:
sudo systemctl stop mongod
예: mongo2에서
mongosh --eval 'rs.status().members.map(m => ({name:m.name, stateStr:m.stateStr}))'

확인 포인트:
새 Primary가 된 노드에서:
mongosh
use lab
db.replica_test.insertOne({ msg: "after_failover", ts: new Date() })
db.replica_test.find().sort({ ts: -1 }).limit(5)

아까 내렸던 서버(예: mongo1)에서:
sudo systemctl start mongod
다른 노드에서 상태 확인:
mongosh --eval 'rs.status().members.map(m => ({name:m.name, stateStr:m.stateStr}))'

확인 :
mongosh --eval 명령어: mongo쉘에서 다음 명령어를 실행
다른 쉘을 대상으로도 사용가능한 옵션이다.
Election에서 Primary가 되는 노드는“가장 최신 데이터 + 과반수 지지 + (설정상) 우선순위가 높은 노드”다.
아래 조건을 통과한 노드만 후보가 되고, 그중 투표로 결정.
당연하지만 가장 먼저 봅니다.
❌ 죽어 있거나
❌ 네트워크로 고립된 노드는 후보 탈락
MongoDB는 각 노드에 oplog(작업 로그)를 유지합니다.
Election 시 비교하는 것:
👉 이게 가장 결정적인 기준
Replica Set 멤버에는 priority 값이 있다.
rs.conf().members
기본값:
priority: 1
mongo1 priority: 2
mongo2 priority: 1
mongo3 priority: 0.5
⚠️ 단, priority가 높아도 데이터가 최신이 아니면 탈락
후보가 되었다고 끝이 아님.
예: 3대 구성
👉 자기 자신 1표 + 다른 노드 1표 이상
우선순위:
👉 그래서 priority를 명시적으로 주면 예측 가능
priority 높게priority: 0rs.reconfig({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017", priority: 2 },
{ _id: 1, host: "mongo2:27017", priority: 1 },
{ _id: 2, host: "mongo3:27017", priority: 0 }
]
})
rs.status().members.map(m => ({name:m.name, health:m.health, stateStr:m.stateStr}))
db.hello().isWritablePrimary
rs.secondaryOk()
- MongoDB는 문서 기반 NoSQL 데이터베이스로, JOIN 대신 중첩 구조를 사용해 대규모 데이터와 유연한 스키마를 효율적으로 처리한다.
- Replica Set은 Primary/Secondary 구조와 과반수 기반 Election을 통해 장애 발생 시 자동으로 Primary를 재선출하여 서비스 중단을 최소화한다.
- Primary 선출은 노드 생존 여부, oplog 최신성, priority 설정, 과반수 투표를 기준으로 결정되며, 이를 통해 예측 가능한 고가용성 구성이 가능하다.