MeetBack 2026.08.27 6 min read

백업 파일 생성보다 복원을 먼저 본 MySQL NFS 백업

MySQL 논리 백업을 전용 계정으로 NFS에 저장했다. 마운트 누락과 권한 오류를 확인하고, 체크섬 검사부터 별도 DB 복원까지 시험한 과정을 정리했다.

프로젝트 기간: 2026-08-18 ~ 2026-09-02
글 범위: MySQL Logical Backup과 NFS Restore 검증

MySQL 복제와 별도로 백업을 준비했다. 복제는 잘못 실행한 DELETE나 스키마 변경도 Replica로 전달한다. 두 서버가 함께 영향을 받는 사고나 이전 데이터로 되돌아가야 하는 상황에는 별도 사본이 필요했다.

복제본, 백업, RAID의 역할은 아래처럼 나눴다.

  • Replica: 최신 데이터를 따라가는 장애 전환 후보
  • 논리 백업: 과거 시점의 데이터를 복구할 자료
  • RAID5: 디스크 한 개의 장애에 대응하는 저장장치 보호

같은 데이터를 다루지만 각각 대응할 수 있는 장애가 달랐다.

Backup은 Primary 한 곳에서만 실행한다

DB1과 DB2에 같은 백업 유닛을 준비하고 평상시에는 Primary의 타이머만 켰다. 두 서버가 같은 NFS 경로에 덤프를 만들면 중복 실행이나 파일명 충돌이 생길 수 있어 백업 담당도 한 곳으로 정했다.

Replica를 승격하면 백업 타이머도 새 Primary로 옮겨야 한다. 기존 Primary가 복구돼도 바로 타이머를 켜지 않고 Replica로 다시 편입한 뒤 역할을 정하도록 했다.

mysqldump → gzip → SHA-256 흐름

백업 작업은 아래 순서로 실행하도록 만들었다.

1. 현재 server가 backup 실행 역할인지 확인
2. NFS backup filesystem이 실제 mount됐는지 확인
3. 전용 local MySQL account로 logical dump 생성
4. gzip으로 압축
5. SHA-256 checksum 생성
6. 성공 파일을 최종 이름으로 이동
7. node_exporter textfile metric 기록
8. retention 정책에 따라 오래된 파일 정리

MySQL은 localhost 전용 백업 계정에 필요한 권한만 부여했다. 비밀번호가 명령행에 노출되지 않도록 로컬 인증 방식을 사용했다.

압축으로 저장 공간과 네트워크 쓰기 양을 줄였다. 체크섬은 전송·보관 중 파일이 바뀌었는지 확인하는 데 사용했다. 다만 파일이 그대로라는 것만으로 SQL을 복원할 수 있는지는 알 수 없어 복원 시험도 따로 진행했다.

NFS가 없을 때 local directory에 쓰지 않게 했다

systemd automount는 일정 시간 사용하지 않으면 실제 마운트를 해제할 수 있다. 디렉터리는 남아 있어 백업 스크립트가 -d만 검사하면 NFS가 아닌 로컬 루트 파일시스템에 덤프를 저장할 수 있었다.

이 상태에서 백업이 실행되면 아래 문제가 생긴다.

  • NFS에 백업됐다고 생각하지만 실제 파일은 DB 서버에 남는다.
  • 덤프가 커지면 DB 서버의 루트 파일시스템이 찬다.

작업 시작 전에 경로에 접근해 자동 마운트를 동작시키고 findmnt와 파일시스템 유형으로 실제 NFS 연결을 확인했다. 전용 계정의 쓰기 권한도 시험했다. 하나라도 실패하면 덤프를 시작하지 않고 실패 지표를 남겼다.

rw Export만으로는 충분하지 않았다

처음에는 export가 rw이고 마운트도 됐는데 백업 계정의 쓰기가 거부됐다. root_squash와 디렉터리의 POSIX 권한을 함께 확인해야 했다.

DB 서버와 NFS 서버에 같은 숫자 UID/GID의 백업 전용 계정을 만들었다. 디렉터리 소유자와 접근 모드도 이 계정에 맞췄다. chmod 777이나 no_root_squash로 권한을 넓히지는 않았다.

DB backup process UID/GID
  = NFS directory owner UID/GID

변경 후에는 백업 계정으로 임시 파일을 만들고 삭제할 수 있는지 확인했다.

성공 파일만 최종 이름으로 남겼다

작업이 중간에 종료돼도 일부만 기록한 .sql.gz가 정상 백업처럼 남지 않도록 임시 이름에 먼저 썼다. 덤프, 압축, 체크섬 생성이 모두 성공한 뒤 최종 이름으로 원자적으로 이동했다.

실행 로그 외에도 아래 값을 지표로 기록했다.

  • 마지막 실행 성공 여부
  • 마지막 성공 시각
  • 소요 시간
  • 생성 파일 크기

node_exporter textfile collector를 통해 Prometheus가 값을 읽도록 했다. 백업 실패와 마지막 성공 후 너무 오래 지난 상태를 알림으로 확인할 수 있게 했다.

Restore 시험으로 실제 사용 가능성을 확인했다

생성한 덤프를 다시 DB로 만들 수 있는지 시험했다. 운영 DB에 덮어쓰지 않고 별도 시험 DB를 준비했다.

1. backup file checksum 검증
2. gzip stream 검증
3. 별도 test database 생성
4. dump restore
5. table 목록과 주요 row 확인
6. test database 제거

압축 파일을 읽고 SQL을 실행하는 과정까지 확인했다. 테이블과 주요 행을 조회하면서 계정 권한이나 문자셋에 문제가 없는지도 확인했다. mysqldump 종료 코드와 함께 실제 복원 결과를 기록했다.

Daily와 Weekly를 나눴지만 장기 정책은 남아 있다

매일 백업을 생성하고 일요일 백업은 주간 사본으로 구분하는 구조를 준비했다. 장기 보관 기간은 프로젝트 종료 후 필요한 복구 시점과 NFS 용량을 기준으로 확정할 항목으로 남겼다.

후속 운영에서는 아래 항목을 더 정해야 했다.

  • 일간·주간·월간 보관 기간
  • NFS 서버와 다른 위치의 2차 사본
  • 저장 시 암호화가 필요한 데이터 범위
  • 정기 복원 시험
  • Primary 전환 후 백업 타이머 담당 확인
  • 백업 RPO와 RTO 측정

이 구성의 한계

mysqldump는 DB가 커질수록 생성과 복원에 시간이 더 걸린다. 일관된 트랜잭션 옵션을 써도 DDL과 실제 작업 특성을 확인해야 한다. 당시 규모에서는 직접 확인하고 복원하기 쉬운 논리 백업을 선택했다. 데이터가 늘어나면 물리 백업이나 binlog를 이용한 시점 복구를 검토할 필요가 있다.

NFS 마운트와 계정 권한을 확인한 뒤에만 백업이 시작되도록 했고, 완성된 파일을 별도 DB에 복원했다. 복제와 RAID로 대응하지 못하는 상황에 사용할 사본을 준비한 작업이었다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동