본문 바로가기
DevOps/Docker

Docker Compose로 MongoDB Replica Set 자동 구성하기

by 개발하는 경제학도 2026. 2. 4.

(one-shot init 컨테이너 패턴 + docker host / container 관점 설명)

1. 배경

MongoDB Replica Set을 Docker Compose로 구성하다 보면 보통 이런 흐름을 겪는다.

  • 컨테이너는 잘 올라가는데
  • rs.initiate()는 직접 실행해야 하고
  • 재기동할 때마다 “이미 초기화된 건지” 사람이 판단해야 한다

이 상태는 실습 단계에서는 괜찮지만,
운영 관점에서는 명백한 문제다.

분산 시스템에서 “선출(election)”이 수동으로 남아 있다는 건
자동화가 덜 끝났다는 뜻이기 때문이다.

이번 글에서는:

  • docker compose up -d 한 번으로
  • MongoDB 3노드 Replica Set 기동
  • 레플리카셋 초기화 + PRIMARY 선출까지 자동으로 완료되는 구조

one-shot init 컨테이너 패턴으로 정리한다.


2. Docker Host vs Container 관점부터 정리

Replica Set을 이해하기 전에, 주소를 누가 해석하는지부터 명확히 해야 한다.

Docker Host (로컬 PC) 관점

  • 우리가 터미널에서 쓰는:
  •  
    localhost:27017
  • 이건 호스트 OS 기준 주소
  • Docker 컨테이너 내부 DNS (mongo1, mongo2)는 모른다

그래서:

  • 호스트 → 컨테이너 접근은 포트 포워딩으로 한다
 
localhost:27017 → mongo1 컨테이너의 27017

Docker Container (MongoDB) 관점

  • 컨테이너 안에서의 localhost는 자기 자신
  • MongoDB 컨테이너끼리 통신하려면:
    • localhost ❌
    • Docker 네트워크 DNS 이름 (mongo1, mongo2) ✅

즉:

  • MongoDB 서버끼리 쓰는 주소 = Docker DNS
  • 클라이언트가 최초 접속하는 주소 = host가 해석 가능한 주소

이 관점이 섞이면 Replica Set이 바로 꼬인다.


3. 전체 구조 개요

구성은 다음과 같다.

 
mongo1 ─┐ mongo2 ─┼─ Replica Set (rs0) mongo3 ─┘ mongo-init (one-shot) └─ rs.initiate() 1회 실행 후 종료

역할 분리는 명확하다.

  • mongo1/2/3
    DB 프로세스만 실행 (장기 실행 서비스)
  • mongo-init
    클러스터 초기화 작업만 수행 (Job)

4. docker-compose.yml

 
version: "3.8" services: mongo1: image: mongo:7.0 container_name: mongo1 hostname: mongo1 ports: - "27017:27017" command: ["mongod", "--replSet", "rs0", "--bind_ip_all"] volumes: - mongo1-data:/data/db healthcheck: test: ["CMD-SHELL", "mongosh --quiet --eval 'db.adminCommand({ ping: 1 }).ok' | grep 1 >/dev/null"] interval: 2s timeout: 2s retries: 30 mongo2: image: mongo:7.0 container_name: mongo2 hostname: mongo2 ports: - "27018:27017" command: ["mongod", "--replSet", "rs0", "--bind_ip_all"] volumes: - mongo2-data:/data/db healthcheck: test: ["CMD-SHELL", "mongosh --quiet --eval 'db.adminCommand({ ping: 1 }).ok' | grep 1 >/dev/null"] interval: 2s timeout: 2s retries: 30 mongo3: image: mongo:7.0 container_name: mongo3 hostname: mongo3 ports: - "27019:27017" command: ["mongod", "--replSet", "rs0", "--bind_ip_all"] volumes: - mongo3-data:/data/db healthcheck: test: ["CMD-SHELL", "mongosh --quiet --eval 'db.adminCommand({ ping: 1 }).ok' | grep 1 >/dev/null"] interval: 2s timeout: 2s retries: 30 mongo-init: image: mongo:7.0 container_name: mongo-init depends_on: mongo1: condition: service_healthy mongo2: condition: service_healthy mongo3: condition: service_healthy restart: "no" entrypoint: ["bash", "-lc"] command: > ' set -euo pipefail; echo "[mongo-init] checking replica set status..."; if mongosh --host mongo1:27017 --quiet --eval "try { rs.status().ok } catch(e) { 0 }" | grep -q "^1$"; then echo "[mongo-init] already initialized"; exit 0; fi echo "[mongo-init] initiating replica set..."; mongosh --host mongo1:27017 --quiet --eval " rs.initiate({ _id: \"rs0\", members: [ { _id: 0, host: \"mongo1:27017\" }, { _id: 1, host: \"mongo2:27017\" }, { _id: 2, host: \"mongo3:27017\" } ] }) "; echo "[mongo-init] waiting for PRIMARY..."; for i in {1..60}; do if mongosh --host mongo1:27017 --quiet --eval "rs.isMaster().ismaster" | grep -q "^true$"; then echo "[mongo-init] PRIMARY elected"; exit 0; fi sleep 1; done echo "[mongo-init] PRIMARY election timeout"; exit 1; ' volumes: mongo1-data: mongo2-data: mongo3-data:

5. 핵심 포인트 설명

1) healthcheck + depends_on

  • depends_on: service_healthy는
    • “컨테이너가 떴다”가 아니라
    • MongoDB가 요청을 받을 준비가 됐다는 의미
  • 너무 이른 rs.initiate() 호출을 방지한다

2) mongo-init은 왜 따로 두나?

mongo-init은 one-shot Job이다.

  • 할 일:
    • Replica Set 초기화
    • PRIMARY 선출 확인
  • 할 일 끝나면:
    • exit 0
    • 컨테이너 종료

이게 정상 동작이다.

init 컨테이너가 계속 살아있다면
오히려 설계가 잘못된 것이다.


3) set -euo pipefail의 의미

 
set -euo pipefail

이건 “컨테이너를 죽이기 위한 옵션”이 아니다.

  • 실패를 숨기지 말고
  • 하나라도 실패하면
  • 즉시 실패로 처리하라는 안전장치

자동화에서는 조용한 실패가 제일 위험하다.


6. 실행 및 확인

 
docker compose up -d
 
docker logs -f mongo-init
 
docker exec -it mongo1 mongosh --eval \ 'rs.status().members.map(m=>({name:m.name,state:m.stateStr,health:m.health}))'

결과 예시:

 
mongo1 PRIMARY mongo2 SECONDARY mongo3 SECONDARY

7. 정리

이 구성의 핵심은 MongoDB가 아니다.

  • DB 실행
  • 클러스터 초기화
  • 선출 확인
  • 성공/실패 기준 명확화

이 패턴은 그대로:

  • Kafka topic-init
  • DB migration Job
  • Kubernetes Job / InitContainer

로 확장된다.

오늘 MongoDB를 배운 게 아니라,
“운영 자동화 패턴 하나”를 만든 것이다.

댓글