이 블로그에 백업 글을 네 개 썼다. 3-2-1 전략, 하드링크 스냅샷, NAS 역할 분담, USB 디스크 함정까지. 그런데 전부 “어떻게 백업하나”에 관한 글이었다. 정작 가장 중요한 질문은 따로 있다. 그 백업, 복구돼? 복구를 한 번도 해보지 않은 백업은 백업이 아니라 “백업이 있다는 믿음”이다. 그리고 그 믿음이 가장 위험하다.
매일 도는 백업이 조용히 실패하는 법
백업 스크립트의 초록불(정상 종료)은 “데이터가 안전하다”가 아니라 “스크립트가 끝까지 실행됐다”만 보장한다. 이 둘 사이에는 생각보다 넓은 틈이 있고, 그 틈에서 백업은 에러 한 줄 없이 망가진다.
- 빈 백업: 원본 경로에 오타가 있거나, 백업 직전 원본 디스크가 마운트 해제됐다면, rsync는 “비어 있는 원본을 충실히 복사”하고 정상 종료한다. 로그는 초록불, 내용은 0바이트.
- 과한 제외:
--exclude패턴이 너무 넓어서 정작 중요한 폴더까지 걸러낸 경우. 백업은 빠르고 가볍게 끝나지만, 복구할 것이 거기 없다. - 권한·소유권 불일치: 파일은 복원됐는데 소유자·권한이 어긋나 서비스가 그 파일을 읽지 못한다. 데이터는 살았는데 서비스는 안 뜬다.
- 일관성 깨진 DB: 실행 중인 데이터베이스 파일을 그대로 복사하면, 쓰기 도중의 어중간한 상태가 박제된다. 복원해도 DB가 손상 판정으로 기동을 거부할 수 있다.
이 네 가지의 공통점은 백업 시점에는 아무 신호도 없다는 것이다. 오직 복구를 시도해야만 드러난다. 그래서 복구는 “재해가 났을 때 하는 일”이 아니라 “재해 전에 미리 해보는 일”이어야 한다.
복구 리허설은 3개 층이다
복구를 “백업을 되돌리는 단일 행동”으로 뭉뚱그리면 안 된다. 난이도와 검증 범위가 전혀 다른 세 개의 층이 있고, 아래층 통과가 위층을 보장하지 않는다.
1층 — 파일 복구
스냅샷에서 파일 하나를 꺼내는 일이다. 하드링크 스냅샷 방식이라면 복구랄 것도 없이 그냥 복사다.
ls /mnt/hdd4t/backup/daily/ # 날짜 목록
cp /mnt/hdd4t/backup/daily/2026-06-01/config/.bashrc ~/restore-test/
검증 포인트: 꺼낸 파일이 그 날짜의 내용이 맞는지, 그리고 깨지지 않았는지. 가장 쉽고, 그래서 가장 자주 해야 하는 층이다.
2층 — 서비스 복구
서비스 하나의 데이터 폴더를 복원해서 그 서비스가 실제로 뜨는지까지 확인한다. 여기서부터 1층이 못 잡는 것들이 드러난다 — 권한·소유권, DB 일관성, 빠진 설정 파일. 임시 디렉토리에 데이터를 복원하고, compose 파일의 마운트 경로를 그쪽으로 돌린 별도 인스턴스를 띄워보는 식이다. “파일이 있다”와 “서비스가 그 파일로 기동한다”는 다른 명제다.
3층 — 전체(베어메탈) 복구
디스크가 통째로 죽은 상황을 가정한다. 새 디스크에 OS를 깔고, 백업에서 서비스 정의·설정·크론·방화벽 규칙까지 다시 세워 서버를 처음부터 재건하는 층이다. 가장 어렵고 가장 드물게 하지만, 진짜 재해는 항상 이 층이다. 여기서 비로소 “백업에 OS 복구에 필요한 것이 다 들어 있었나”라는 질문의 답이 나온다.
분기별 복구 리허설 체크리스트
리허설은 빈도를 층마다 다르게 잡는 것이 현실적이다. 자주 하는 1층이 일상 점검이라면, 드물게 하는 3층은 연례 행사다.
| 층 | 권장 빈도 | 확인 항목 |
|---|---|---|
| 1층 파일 | 월 1회 | 임의 날짜 스냅샷에서 파일 꺼내 내용·무결성 확인 |
| 2층 서비스 | 분기 1회 | 데이터 복원 후 서비스 기동, DB 읽기, 로그인까지 성공 |
| 3층 전체 | 반기~연 1회 | 빈 디스크/VM에 OS + 서비스 + 설정 + 크론 전부 재건 |
각 리허설마다 딱 하나만 기록하자: 소요 시간. “복구에 얼마나 걸리는가(RTO)“는 재해 한복판에서 알면 너무 늦다. 평온할 때 재본 그 숫자가, 진짜 장애 때 “지금 복구 중이니 3시간 뒤 정상화”라고 말할 수 있는 근거가 된다.
복구를 가로막는 함정들
리허설을 처음 해보면 대개 같은 곳에서 막힌다.
- DB는 파일만으로 안 돈다: 데이터베이스는 실행 중 복사본이 아니라 덤프(예: PostgreSQL의
pg_dump)로 백업하고 그 덤프로 복원하는 것이 정석이다. PostgreSQL 공식 문서도 일관된 백업의 기본으로 논리 덤프와 일관성 있는 파일시스템 스냅샷을 구분해 설명한다(PostgreSQL 백업 문서). - 복구에 필요한 것이 백업에 없다:
.env, 암호화 키, 인증서 같은 “비밀”을 보안상 백업에서 빼두었다면, 정작 복구 시 그 서비스를 못 세운다. 비밀은 별도 경로로라도 반드시 백업 대상에 포함해야 한다. - 권한·소유권:
rsync -a(아카이브 모드)는 권한·소유권·타임스탬프를 보존하지만(rsync 매뉴얼), 복원 대상 사용자나 UID가 바뀌면 어긋난다. 복원 후 서비스 계정이 실제로 읽을 수 있는지 확인한다. - 복구 매체 자체의 신뢰성: 백업이 USB 브리지 뒤의 외장 디스크에 있다면, 그 연결 계층의 불안정성이 곧 복구 성공률이다(USB 디스크 함정). 복구하려고 꽂은 디스크가 그날 인식이 안 되면 백업은 없는 것과 같다.
- 부팅까지가 복구다: 3층에서는 부트로더와
fstab도 복구 대상이다. 외장 디스크 항목에nofail이 없으면, 복원한 서버가 그 디스크 부재로 부팅에서 멈춘다(재부팅 후 자동 복구에서 다룬 그 옵션이다).
마무리: 백업의 완성은 복구다
솔직히 적으면, 나도 오랫동안 1층조차 정기적으로 하지 않았다. 백업이 도는 초록불만 확인하고 안심했다. 이 글을 쓰며 실제로 1층을 해봤다 — 내 백업 디스크의 daily 스냅샷에서 설정 파일 하나를 복원 디렉토리로 꺼내보니 정상으로 나왔고, stat으로 그 파일의 하드링크 수를 보니 24였다. 스냅샷 23개와 latest가 디스크 위의 한 실체를 함께 가리키고 있다는 뜻이다. 다만 거기까지였다. 2층(서비스 통째 기동)과 3층(베어메탈 재건)은 아직 리허설하지 않았고, 그게 내 다음 숙제다. 한 가지 위안은 백업이 docker compose 스택과 실행 중 컨테이너 목록, 크론 작업까지 함께 떠 가도록 만들어 둔 것 — 3층 복구의 재료는 갖춰져 있다. 재료가 있는 것과 실제로 조립해본 것은 다르다는 걸 알면서도, 그 차이를 메우는 것이 다음 분기의 일이다.
백업은 데이터를 복사하는 순간이 아니라 데이터를 되찾는 순간에 완성된다. 초록불을 믿지 말고, 분기에 한 번은 꺼내보자. 그리고 그 리허설 결과조차 텔레그램 알림으로 자동 보고받게 만들면, “복구되는 백업”을 유지하는 일이 의지가 아니라 습관이 된다.