백업 및 복구

MongoDB가 시스템의 유일한 영속 저장소이기 때문에, MongoDB 백업을 잃으면 데이터를 복구할 수 없습니다. 이 문서는 정기 백업 절차와 장애 발생 시 복구 절차를 정리합니다.

1. 백업 대상

대상저장 위치영속성백업 필요 여부
MongoDB (prd_heka)MongoDB 파드 PVC영구필수
Redis(Valkey) 큐Valkey 파드휘발성 (메시지 큐)불필요 — 재실행 가능
청구서 PDF / CSV 임시 파일각 워커 파드의 ./tmp/*휘발성불필요 — 청구서 원본 데이터가 MongoDB에 있음
회사 직인 / 로고 업로드 파일MongoDB 또는 외부 스토리지배포 환경에 따라 다름외부 스토리지 사용 시 별도 백업 필요
애플리케이션 설정 (.env, Secret)Kubernetes Secret / 외부 볼트영구필수 (MongoDB 백업과 별개로 관리)

2. MongoDB 백업

2.1 기본 도구

  • mongodump / mongorestore (MongoDB Database Tools)
  • 본 시스템은 Percona Server for MongoDB(PSMDB)를 사용하며, 위 도구는 그대로 호환됩니다.

2.2 백업 주기

참고: 실제 주기·보관 기간·저장 위치는 운영 정책에 따라 다릅니다. 현재 환경에 적용된 정책은 별도 전달 문서를 참조하세요. 아래는 권장 기본값입니다.

주기방식보관
일간전체 mongodump14일
주간전체 mongodump3개월
월간전체 mongodump1년

백업 파일은 클러스터 외부 저장소(예: 오브젝트 스토리지)에 보관합니다. 같은 클러스터의 PVC에만 두면 전체 장애 시 유실됩니다.

2.3 수동 전체 백업

Primary 파드에 포트포워딩 후 로컬에서 수행합니다. 백업 자체는 read 작업이지만, Service는 secondary로 라우팅될 수 있어 directConnection=true 와 궁합이 맞지 않으므로 primary 파드(mongodb-rs-0)를 직접 타겟 하는 것을 기본 규칙으로 합니다.

# 포트포워딩 — primary 파드 직접 타겟 (Service 사용 금지)
kubectl -n <NAMESPACE> port-forward pod/mongodb-rs-0 27017:27017

# (선택) 접속 후 primary 여부 확인
#   mongosh "mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true"
#   > db.hello().isWritablePrimary  // true 기대. 아니면 rs.status() 로 primary 파드 찾아 재연결.

# 덤프 (날짜별 디렉터리)
OUT_DIR="./mongo-backup-$(date +%Y%m%d-%H%M)"
mongodump \
  --uri="mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true" \
  --db=prd_heka \
  --out="${OUT_DIR}" \
  --gzip

# 외부 스토리지로 업로드 (예시)
tar czf "${OUT_DIR}.tar.gz" "${OUT_DIR}"
# → 오브젝트 스토리지 업로드 명령 (운영 정책에 맞게)

2.4 컬렉션 단위 백업

대상 컬렉션만 백업할 때. 특히 위험한 수정 작업 직전에 권장합니다.

mongodump \
  --uri="mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true" \
  --db=prd_heka \
  --collection=nhn_invoice \
  --query='{"date":"2026-04"}' \
  --out=./pre-fix-$(date +%Y%m%d-%H%M) \
  --gzip

참고: --query 는 JSON(extended JSON) 형식입니다. 이스케이프에 주의.


3. 복구

3.1 전체 복구

클러스터 전체가 날아가 새 DB로 복구할 때.

# 1. 백업 파일 다운로드 및 압축 해제
tar xzf mongo-backup-YYYYMMDD-HHMM.tar.gz

# 2. 복구 대상 MongoDB primary 파드로 포트포워딩
#    복구는 write 작업이므로 Service 대신 primary 파드(mongodb-rs-0)를 반드시 직접 타겟합니다.
#    Service로 연결하면 secondary에 물려 mongorestore 가 "not master" 에러로 실패할 수 있습니다.
kubectl -n <NAMESPACE> port-forward pod/mongodb-rs-0 27017:27017

# 2-1. (권장) primary 여부 확인
#    mongosh "mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true"
#    > db.hello().isWritablePrimary  // true 일 때만 아래 복구 진행

# 3. 복구
mongorestore \
  --uri="mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true" \
  --db=prd_heka \
  --gzip \
  --drop \
  ./mongo-backup-YYYYMMDD-HHMM/prd_heka
  • --drop: 복구 전에 기존 컬렉션을 비웁니다. 신규 클러스터라면 생략 가능.
  • 복구 후 애플리케이션(API/Worker/Scheduler) 파드를 재기동 합니다. 애플리케이션 시작 시 자동으로 인덱스가 재생성됩니다 (common/database/base.py).

3.2 컬렉션 단위 복구

특정 컬렉션만 전체 덤프에서 되돌립니다.

mongorestore \
  --uri="mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true" \
  --db=prd_heka \
  --collection=nhn_invoice \
  --gzip \
  --drop \
  ./mongo-backup-YYYYMMDD-HHMM/prd_heka/nhn_invoice.bson.gz

3.3 문서 단위 복구

몇 건만 되돌려야 할 때는 별도 DB로 복구한 뒤 수동 복사하는 것이 안전합니다.

# 1. 백업을 임시 DB로 복구
mongorestore \
  --uri="mongodb://<USER>:<PASSWORD>@localhost:27017/?authSource=admin&directConnection=true" \
  --nsFrom='prd_heka.*' \
  --nsTo='restore_staging.*' \
  --gzip \
  ./mongo-backup-YYYYMMDD-HHMM
// 2. mongosh 에서 필요한 문서만 운영 DB로 복사
const src = db.getSiblingDB('restore_staging').nhn_invoice;
const dst = db.getSiblingDB('prd_heka').nhn_invoice;

src.find({ invoice_id: '<INVOICE_ID>' }).forEach(doc => {
  dst.replaceOne({ _id: doc._id }, doc, { upsert: true });
});

// 3. 작업 후 임시 DB 제거
db.getSiblingDB('restore_staging').dropDatabase();

주의: 문서를 되돌리면 그 이후 발생한 변경이 덮어씌워집니다. 반드시 변경 이력을 확인하고 이해관계자와 공유하세요.


4. 복구 후 검증 체크리스트

복구 작업 후 다음을 순서대로 확인합니다.

  • db.stats() 의 컬렉션 개수 · 문서 수가 백업 시점과 일치하는가?
  • db.user.countDocuments({}) 등 주요 컬렉션 건수 재확인.
  • 애플리케이션 파드 모두 재기동 후 Ready 상태인가?
  • API 헬스체크 엔드포인트가 200을 응답하는가?
  • 로그인, 청구서 조회 등 기본 시나리오가 동작하는가?
  • 스케줄러 파드 기동 후 다음 cron 시각에 작업이 발사되는가? (또는 수동 send() 로 확인)
  • 워커 파드 로그와 error_log 에 복구 직후 비정상 에러가 쌓이지 않는가?
  • 인덱스 재생성이 완료됐는가? (db.<collection>.getIndexes() 로 확인)

5. 설정/시크릿 백업

MongoDB와 별개로 다음을 백업해야 합니다.

대상백업 방법
Kubernetes Secret (.env 값, NHN 키, SMTP 비밀번호)외부 볼트(1Password, Vault 등)에 사본 보관
Ingress/Cert-Manager 인증서kubectl get secret -o yaml 덤프 또는 발급 기관에서 재발급 가능 여부 확인
Kubernetes 매니페스트(Helm values, manifest)Git 저장소로 버전 관리

운영 시크릿이 날아가면 복구된 DB가 있어도 서비스가 기동되지 않습니다.


6. 주기 점검 체크리스트

주기항목
매주백업 파일이 예정대로 생성되고 외부 스토리지에 업로드되는지
매월최근 백업 파일 1개를 실제 복구 테스트(별도 DB 이름으로)
분기재해복구 리허설: 전체 클러스터 재구축 시나리오를 문서 기반으로 검증