Skip to content
Published on

백업과 복구 완전 가이드: 복구 시나리오에서 거꾸로 설계하기

공유하기
Authors

들어가며

백업에 관한 가장 흔한 오해는 "백업을 하고 있으니 안전하다"입니다. 실제로는 다릅니다. 복구해 본 적이 없는 백업은 백업이 아니라 백업이라고 믿는 파일 더미입니다. 실제 사고에서 백업이 무용지물이 되는 이유는 대개 백업을 안 해서가 아니라, 복구 절차를 한 번도 실행해 본 적이 없어서입니다.

이 블로그에는 오픈소스 백업 툴 비교가 이미 있습니다. 그 글은 도구별 기능과 장단점을 비교합니다. 이 글은 도구 선택 이전의 문제를 다룹니다. 어떤 복구 시나리오를 감당해야 하는지 먼저 정하고, 거기서 거꾸로 백업 방식을 유도하는 설계 순서가 주제입니다.

다루는 도구는 리눅스 기본 환경에 있는 것 위주입니다. rsync, tar, LVM 스냅샷, 그리고 데이터베이스 덤프입니다. 배포판 기준은 RHEL 계열 8 이상, Debian 12 및 Ubuntu 22.04 이상이며, GNU 도구 기준으로 설명합니다. BSD나 macOS의 tar, sed, date는 옵션이 다르므로 스크립트를 옮길 때 주의하세요.


1. RPO와 RTO — 모든 결정의 출발점

두 숫자가 나머지를 전부 결정합니다.

  • RPO(Recovery Point Objective): 최대 몇 분 또는 몇 시간 분량의 데이터를 잃어도 되는가. 백업 주기를 결정합니다.
  • RTO(Recovery Time Objective): 장애 발생부터 서비스 재개까지 몇 시간을 허용하는가. 백업 방식과 저장 위치를 결정합니다.

이 두 값을 정하지 않은 채 도구를 고르면 반드시 어긋납니다. RPO가 5분인데 하루 한 번 덤프를 뜨고 있거나, RTO가 1시간인데 테이프에서 8시간짜리 복원을 해야 하는 상황이 그것입니다.

RPO 요구필요한 방식
24시간일 1회 전체 또는 증분 백업
1시간시간별 증분 + 스냅샷
5분지속적 아카이빙(WAL 등) 또는 복제
0동기 복제. 백업만으로는 불가능
RTO 요구필요한 구성
며칠원격 아카이브에서 복원
수 시간로컬 디스크 백업 + 문서화된 절차
1시간 미만대기 시스템 + 정기 리허설
분 단위자동 장애 조치. 백업이 아니라 이중화

마지막 줄이 중요합니다. 백업과 고가용성은 다른 문제를 푸는 다른 도구입니다. 복제는 실수로 지운 데이터를 즉시 함께 지웁니다. 랜섬웨어도 함께 복제됩니다. 그래서 복제가 있어도 백업이 필요합니다.


2. 3-2-1 규칙과 그 현대적 보완

고전적인 3-2-1 규칙은 사본 3개, 매체 2종, 원격 1개입니다. 여기에 최근에는 두 가지가 덧붙습니다.

  • 1개는 오프라인 또는 변경 불가(immutable) — 랜섬웨어가 백업까지 암호화하는 사고 때문입니다.
  • 0개의 검증되지 않은 사본 — 복원 검증을 통과하지 않은 사본은 세지 않습니다.

실무에서 이것이 의미하는 바는 명확합니다. 백업 서버에 운영 서버의 자격 증명이 있으면 안 됩니다. 운영 서버가 침해되면 백업까지 삭제되기 때문입니다. 백업 서버가 운영 서버로 접속해 끌어오는 pull 방식이, 운영 서버가 백업 서버로 밀어 넣는 push 방식보다 안전한 이유가 이것입니다.

객체 스토리지를 쓴다면 버전 관리와 객체 잠금 기능을 켜는 것이 실질적인 방어입니다. 정확한 설정 이름과 절차는 사용하는 스토리지 제품 문서에서 확인하세요.

매체 2종이라는 조건도 현대적으로 해석해야 합니다. 예전에는 디스크와 테이프였지만, 지금은 서로 다른 실패 양상을 가진 두 저장소로 읽는 편이 맞습니다. 같은 클라우드 계정의 두 리전은 매체 2종이 아닙니다. 계정 자체가 잠기거나 결제가 끊기면 둘 다 접근할 수 없기 때문입니다. 실패 원인이 겹치지 않는지를 기준으로 판단하세요.

그리고 백업 목록에는 데이터뿐 아니라 그 데이터를 되살리는 데 필요한 모든 것이 들어가야 합니다. 설정 파일, 인증서와 개인 키, 데이터베이스 스키마, 배포 산출물, 그리고 복원 절차 문서입니다. 데이터만 백업하고 서비스를 세우는 방법을 백업하지 않아 복구가 며칠 늘어지는 사례가 실제로 많습니다.


3. rsync — 가장 많이 쓰고 가장 많이 틀리는 도구

rsync -aHAX --numeric-ids --delete --dry-run /srv/data/ backup@10.0.9.2:/backup/prod/data/
rsync -aHAX --numeric-ids --delete /srv/data/ backup@10.0.9.2:/backup/prod/data/

옵션의 정확한 의미입니다.

  • -a(--archive)는 문서상 -rlptgoD와 동일합니다. 재귀, 심볼릭 링크 보존, 권한, 시각, 그룹, 소유자, 특수 파일을 포함합니다.
  • -a에 포함되지 않는 것이 있습니다. ACL(-A), 확장 속성(-X), 하드 링크(-H)는 별도로 지정해야 합니다. 이것을 모르면 복원 후 권한이 미묘하게 달라집니다.
  • --numeric-ids는 이름 대신 숫자 UID/GID를 씁니다. 다른 시스템으로 복원할 때 소유자가 엉뚱하게 매핑되는 것을 막습니다.
  • --delete는 원본에 없는 파일을 대상에서 지웁니다.
  • -n(--dry-run)은 실제 변경 없이 시뮬레이션합니다.
  • -i(--itemize-changes)는 어떤 변경이 일어나는지 한 줄씩 보여 줍니다.

파괴적 명령 경고: --delete는 원본이 비어 있거나 마운트가 풀린 상태에서 실행되면 대상의 데이터를 전부 지웁니다. 실무에서 실제로 자주 발생하는 사고입니다. --dry-run으로 먼저 확인하고, 스크립트에는 원본이 비어 있는지 확인하는 가드를 넣으세요.

#!/usr/bin/env bash
set -euo pipefail
SRC=/srv/data
DST=backup@10.0.9.2:/backup/prod/data/

mountpoint -q "$SRC" || { echo "source not mounted, abort" >&2; exit 1; }
[ "$(ls -A "$SRC" | wc -l)" -gt 0 ] || { echo "source empty, abort" >&2; exit 1; }

rsync -aHAX --numeric-ids --delete "$SRC/" "$DST"

또 하나 자주 틀리는 것이 후행 슬래시입니다. 문서의 표현대로, 원본 경로 끝의 슬래시는 "이 디렉터리의 내용을 복사한다"는 뜻이고, 슬래시가 없으면 "이 디렉터리 자체를 복사한다"는 뜻입니다.

rsync -a /srv/data  /backup/    # /backup/data/... 가 만들어짐
rsync -a /srv/data/ /backup/    # /backup/... 에 내용이 바로 들어감

세대 관리는 하드 링크로 값싸게 구현할 수 있습니다.

rsync -aHAX --numeric-ids --delete \
  --link-dest=/backup/prod/daily.1 \
  /srv/data/ /backup/prod/daily.0/

--link-dest는 변경되지 않은 파일을 복사하는 대신 지정한 디렉터리의 파일에 하드 링크를 겁니다. 결과적으로 각 세대가 전체 스냅샷처럼 보이지만 디스크는 변경분만 차지합니다.


4. tar — 아카이브의 기본기

tar -czf /backup/etc-2026-08-15.tar.gz -C / etc
tar -tzf /backup/etc-2026-08-15.tar.gz | head
tar -xzf /backup/etc-2026-08-15.tar.gz -C /restore/

옵션은 문서 기준으로 다음과 같습니다.

  • -c(--create), -x(--extract), -t(--list), -f(--file)가 기본 조합입니다.
  • 압축은 -z(gzip), -j(bzip2), -J(xz), --zstd(zstd)입니다.
  • -C(--directory)는 작업 전에 디렉터리를 바꿉니다. 절대 경로를 아카이브에 넣지 않기 위해 반드시 쓰세요.
  • -p(--preserve-permissions)와 --numeric-owner는 복원 시 권한과 소유자를 정확히 되살립니다.
  • --acls, --xattrs, --selinux는 각각 ACL, 확장 속성, SELinux 컨텍스트를 포함합니다. SELinux를 쓰는 시스템에서 이 옵션 없이 복원하면 서비스가 뜨지 않습니다.
  • --one-file-system은 다른 파일시스템으로 넘어가지 않습니다. 마운트된 네트워크 스토리지까지 백업에 딸려 오는 사고를 막습니다.
  • --exclude, --exclude-from으로 제외 패턴을 지정합니다.
  • --listed-incremental은 증분 백업을 위한 스냅샷 파일을 관리합니다.
  • --strip-components는 복원 시 앞쪽 경로 요소를 제거합니다.

시스템 설정 백업의 실용적인 형태는 이렇습니다.

sudo tar -czf /backup/etc-$(date +%F).tar.gz \
  --acls --xattrs --selinux --numeric-owner \
  --exclude='/etc/shadow-' \
  -C / etc

복원 시 경로를 반드시 확인하세요. -C /로 바로 풀면 현재 시스템 설정을 덮어씁니다. 안전한 절차는 임시 경로로 풀어 내용을 확인한 뒤 필요한 파일만 옮기는 것입니다.

mkdir -p /restore/etc-check
sudo tar -xzf /backup/etc-2026-08-15.tar.gz -C /restore/etc-check
diff -r /restore/etc-check/etc/nginx /etc/nginx | head -40

GNU tar와 BSD tar(bsdtar)는 옵션 처리와 일부 동작이 다릅니다. macOS에서 만든 아카이브를 리눅스에서 풀 때 확장 속성 관련 파일이 함께 나오는 문제가 대표적입니다. 백업 스크립트는 실제 운영 배포판에서 검증하세요.


5. 일관성 — 백업 도중 변경되는 데이터

파일을 복사하는 동안 애플리케이션이 그 파일을 쓰고 있으면, 백업은 어느 시점의 상태도 아닌 잡탕이 됩니다. 특히 데이터베이스에서 치명적입니다.

해법은 세 가지 층위가 있습니다.

층위 1 — 애플리케이션 수준 덤프. 가장 확실하고 이식성이 좋습니다.

pg_dump -Fc -d appdb -f /backup/appdb-2026-08-15.dump
mysqldump --single-transaction --routines --triggers appdb > /backup/appdb.sql

--single-transaction은 트랜잭션 격리로 일관된 스냅샷을 얻습니다. 각 데이터베이스의 정확한 옵션과 제약은 해당 제품 문서에서 확인하세요.

층위 2 — 파일시스템 스냅샷. LVM, Btrfs, ZFS가 지원합니다. 스냅샷을 뜨고 그것을 백업하면 정지 시점이 고정됩니다.

sudo lvcreate --size 10G --snapshot --name snap_data /dev/vg0/data
sudo mount -o ro /dev/vg0/snap_data /mnt/snap
sudo tar -czf /backup/data.tar.gz -C /mnt/snap .
sudo umount /mnt/snap
sudo lvremove -y /dev/vg0/snap_data

중요한 한계가 있습니다. LVM 스냅샷은 블록 수준 시점 고정일 뿐, 애플리케이션이 메모리에 들고 있던 데이터까지 반영하지 않습니다. 데이터베이스라면 스냅샷 직전에 체크포인트를 강제하거나 쓰기를 잠깐 멈추는 절차가 필요합니다. 또한 스냅샷 볼륨이 가득 차면 스냅샷이 무효화되므로 크기를 넉넉히 잡아야 합니다.

sudo lvs -o lv_name,lv_size,data_percent,snap_percent

층위 3 — 지속적 아카이빙. RPO를 분 단위로 줄여야 할 때 씁니다. 데이터베이스의 WAL이나 바이너리 로그를 계속 아카이브해 두고 임의 시점으로 복구합니다. 설정과 복구 절차는 제품별로 다르므로 해당 문서를 따르세요.


6. 검증 — 복구 리허설 없이는 백업이 아니다

검증은 세 단계로 나눠 생각하면 빠짐없이 할 수 있습니다.

1단계 — 백업이 존재하는가. 파일 크기가 0이 아니고, 최신 시각인지 확인합니다. 의외로 이 단계에서 걸리는 문제가 많습니다.

ls -lh /backup/prod/ | tail -5
find /backup/prod -type f -mtime -1 | wc -l

2단계 — 백업이 읽히는가. 압축 무결성과 아카이브 목록을 확인합니다.

gzip -t /backup/etc-2026-08-15.tar.gz && echo 'gzip OK'
tar -tzf /backup/etc-2026-08-15.tar.gz > /dev/null && echo 'archive OK'
sha256sum -c /backup/checksums.sha256

3단계 — 실제로 복구되는가. 이것만이 진짜 검증입니다. 다른 호스트나 임시 환경에 복원해 서비스를 띄워 보고, 데이터가 기대한 시점의 것인지 확인합니다.

리허설 체크리스트는 다음과 같이 두면 좋습니다.

  • 백업 저장소에 접근할 자격 증명이 운영 서버 밖에 보관되어 있는가
  • 복원 절차 문서가 복원 담당자의 노트북이 아닌 곳에 있는가
  • 복원에 필요한 암호화 키가 별도로 보관되어 있는가
  • 리허설에 걸린 실제 시간이 RTO 안에 들어오는가
  • 복원 후 애플리케이션이 기동하고 정합성 검사를 통과하는가

암호화 키 보관은 특히 자주 빠집니다. 백업을 암호화해 두고 키를 백업 대상 서버에만 두면, 그 서버가 사라졌을 때 백업은 무의미한 이진 파일이 됩니다.

리허설 주기는 분기 1회가 현실적인 최소선입니다. 그리고 리허설은 백업을 만든 사람이 아닌 다른 사람이 문서만 보고 수행해야 절차의 빈틈이 드러납니다.


7. 백업이 조용히 망가지는 다섯 가지 방식

방식 1 — 백업 대상에서 빠져 있습니다. 새 볼륨을 추가했는데 백업 스크립트에는 넣지 않은 경우입니다. 대상 목록을 인프라 정의와 함께 관리해 자동으로 반영되게 하세요.

방식 2 — 성공 로그만 보고 실패를 못 봅니다. cron은 기본적으로 조용합니다. 종료 코드를 확인하고 실패를 알리는 장치가 필요합니다.

#!/usr/bin/env bash
set -euo pipefail
trap 'echo "backup FAILED at line $LINENO" >&2; exit 1' ERR

rsync -aHAX --numeric-ids --delete /srv/data/ /backup/prod/data/
echo "backup OK $(date -Is)"

set -euo pipefailtrap ... ERR 조합이 최소 방어선입니다. 성공 시각을 파일이나 모니터링에 기록해 "최근 성공이 24시간 이내인가"를 감시하는 편이 훨씬 낫습니다.

방식 3 — 백업 대상에 랜섬웨어가 함께 복제됩니다. 세대 관리와 변경 불가 저장소가 방어책입니다. 최신 백업만 유지하는 구성은 이 상황에서 무력합니다.

방식 4 — 복원 대상 시스템의 환경이 다릅니다. UID/GID 매핑, SELinux 컨텍스트, 커널 버전, 파일시스템 기능이 다르면 복원은 되지만 서비스가 안 뜹니다. --numeric-ids--selinux가 여기에 대한 대비입니다.

방식 5 — 백업이 운영 성능을 해쳐 백업 창이 줄어듭니다. 그러다 결국 주기를 늘리고, 어느 순간 RPO를 만족하지 못하게 됩니다. 부하를 낮춰 실행하세요.

ionice -c 3 nice -n 19 rsync -aHAX --numeric-ids --bwlimit=50M /srv/data/ /backup/prod/data/

--bwlimit은 네트워크 대역폭을, ionice -c 3은 디스크 우선순위를 낮춥니다. 백업이 운영을 방해하지 않으면 주기를 줄일 여유가 생깁니다.


퀴즈: 실력을 확인해 보세요

퀴즈 1: 실시간 복제를 하고 있으니 백업이 필요 없다는 주장에 어떻게 답하나요?

정답: 복제는 실수와 악성 변경을 그대로 전파하므로 백업을 대체할 수 없습니다

설명: 복제는 하드웨어 장애와 가용성 문제를 해결합니다. 반면 백업은 시간을 되돌리는 문제를 해결합니다. 테이블을 잘못 지우면 복제본에서도 즉시 지워지고, 랜섬웨어 암호화도 그대로 복제됩니다.

둘은 서로 다른 위험에 대응하는 별개 장치이며, RTO가 분 단위로 요구되면 복제가, 되돌리기가 필요하면 백업이 답입니다. 대부분의 서비스는 둘 다 필요합니다.

퀴즈 2: rsync 백업 스크립트에서 가장 위험한 옵션 조합과 방어책은?

정답: --delete와 원본 미마운트 상태의 조합이며, 사전 가드와 dry-run이 방어책입니다

설명: 원본이 마운트되지 않아 빈 디렉터리인 상태에서 --delete가 실행되면 대상의 모든 파일이 삭제됩니다.

mountpoint -q /srv/data || exit 1
[ "$(ls -A /srv/data | wc -l)" -gt 0 ] || exit 1
rsync -aHAX --numeric-ids --delete --dry-run /srv/data/ /backup/prod/data/

가드를 먼저 두고, 스크립트를 바꿀 때마다 --dry-run으로 확인하는 습관이 필요합니다. -i 옵션을 함께 쓰면 어떤 변경이 일어날지 한 줄씩 확인할 수 있습니다.

퀴즈 3: rsync -a만 쓰면 무엇이 빠지고, 그것이 왜 문제가 되나요?

정답: ACL, 확장 속성, 하드 링크가 빠지며 복원 후 권한과 보안 컨텍스트가 달라집니다

설명: 문서에 따르면 -a-rlptgoD와 동일하고 ACL(-A), 확장 속성(-X), 하드 링크(-H)는 포함하지 않습니다.

rsync -aHAX --numeric-ids /srv/data/ /backup/prod/data/

SELinux 컨텍스트는 확장 속성으로 저장되므로 -X 없이 복원하면 RHEL 계열에서 서비스가 접근을 거부당합니다. 하드 링크가 많은 백업 저장소를 복사할 때 -H가 없으면 용량이 몇 배로 불어납니다.

퀴즈 4: 데이터베이스 파일을 rsync로 복사한 백업이 있습니다. 신뢰할 수 있을까요?

정답: 신뢰할 수 없습니다. 복사 도중 변경이 일어나 일관성이 깨집니다

설명: 파일을 순차적으로 읽는 동안 앞부분과 뒷부분이 서로 다른 시점의 상태가 됩니다. 데이터베이스는 이런 아카이브에서 기동하지 못하거나, 더 나쁘게는 기동한 뒤 손상된 데이터를 노출합니다.

올바른 방법은 애플리케이션 수준 덤프나 정지 시점을 고정한 스냅샷입니다.

pg_dump -Fc -d appdb -f /backup/appdb.dump
mysqldump --single-transaction appdb > /backup/appdb.sql

스냅샷을 쓴다면 스냅샷 직전에 체크포인트나 쓰기 정지 절차가 필요합니다.

퀴즈 5: 백업은 매일 성공했는데 복구에 12시간이 걸려 RTO를 넘겼습니다. 무엇이 잘못됐나요?

정답: RTO를 기준으로 백업 방식과 저장 위치를 설계하지 않았습니다

설명: RPO는 주기를, RTO는 방식과 위치를 결정합니다. 원격 아카이브에서 수백 기가바이트를 내려받아 복원하는 구성은 RPO는 만족해도 RTO는 만족하지 못합니다.

대응은 계층화입니다. 최근 며칠분은 로컬 디스크에 두어 빠르게 복원하고, 장기 보관본만 원격에 둡니다. 그리고 리허설로 실제 소요 시간을 측정해야 합니다. 측정하지 않은 RTO는 희망 사항일 뿐입니다.

퀴즈 6: 운영 서버가 침해되었을 때 백업까지 삭제되지 않게 하려면 구조를 어떻게 잡나요?

정답: 백업 서버가 운영 서버에서 끌어오는 pull 방식으로 만들고, 최소 한 사본을 변경 불가 저장소에 둡니다

설명: 운영 서버가 백업 저장소에 쓰기 권한을 가지고 있으면, 그 서버를 장악한 공격자도 같은 권한을 가집니다. pull 방식에서는 운영 서버에 백업 저장소 자격 증명이 아예 없습니다.

추가로 객체 스토리지의 버전 관리와 객체 잠금, 또는 오프라인 사본을 조합합니다. 세대 관리도 필수입니다. 최신본만 유지하는 구성은 암호화된 데이터가 그대로 백업을 덮어씁니다.


마치며

백업 설계는 도구에서 시작하면 반드시 어긋납니다. "어떤 사고에서 무엇을 얼마나 빨리 되돌려야 하는가" 에서 시작해야 합니다. 그 답이 RPO와 RTO이고, 나머지는 거기서 유도됩니다.

그리고 마지막 한 줄만 기억하면 됩니다. 복구해 본 적 없는 백업은 백업이 아닙니다. 이번 분기 안에 복구 리허설 일정을 잡으세요. 백업을 만든 사람이 아닌 다른 사람이, 문서만 보고, 시간을 재면서 하는 리허설이어야 합니다. 그 한 번이 실제 사고에서 몇 시간을 아껴 줍니다.


참고 자료


이어서 읽기