본문 바로가기
서버 인프라 실무

백업은 존재할 때만 복구된다: rsync/cron으로 무중단 증분 백업(로컬·원격·보안·검증 루틴)

by yamoojin83 2025. 10. 23.

백업은 존재할 때만 복구된다: rsync/cron으로 무중단 증분 백업(로컬·원격·보안·검증 루틴)

“백업은 있는데 복구가 안 된다”는 말, 현업에선 흔합니다. 정기성·무중단·검증 세 가지가 빠지면 백업은 착시일 뿐입니다. 이 글은 Ubuntu 24.04 기준으로 rsynccron만으로 증분 스냅샷을 만들고, 원격지에 암호화 백업까지 자동화하는 루틴을 제시합니다. 복붙 가능한 스크립트와 복구 리허설, 삭제/랜섬웨어 대응 팁까지 포함합니다.


1) 전략 결정: 무엇을, 얼마나 자주, 어디에

  • 무엇: 애플리케이션 설정(/etc/<app>), 사용자 데이터(/srv/data), DB 덤프 파일.
  • 주기: 변경 빈도에 따라 일일 증분 + 주간 전체 조합이 현실적.
  • 어디: 같은 서버(로컬 스냅샷) + 다른 디스크/서버(원격)로 2중화.
  • 검증: 백업 완료 후 무결성/복구 리허설 자동 체크.

2) rsync 설치와 핵심 옵션

sudo apt update
sudo apt install -y rsync
rsync --version

핵심 옵션 요약:

  • -a: archive(권한/소유자/타임스탬프 유지, 재귀)
  • -A/-X: ACL/확장속성(필요 시)
  • --delete: 소스에서 삭제된 파일을 대상에서도 삭제(주의)
  • --link-dest: 하드링크로 증분 스냅샷(변경 없는 파일은 이전 스냅샷과 링크 공유)
  • -z: 전송 중 압축(원격 전송 시)
  • -e ssh: SSH로 전송

3) 로컬 스냅샷(일자별 폴더) 만들기

하드링크 기반 스냅샷은 공간 효율이 매우 뛰어납니다. 변경 파일만 추가 저장되고, 변경 없는 파일은 이전 스냅샷과 inode를 공유합니다.

# 예시: /srv/data 를 /backup에 일자별 스냅샷 생성
sudo mkdir -p /backup/snapshots
sudo chown -R root:root /backup
sudo chmod -R 750 /backup

# /usr/local/sbin/backup_local.sh
#!/usr/bin/env bash
set -euo pipefail

SRC="/srv/data"
DST="/backup/snapshots"
TODAY=$(date +%F)
LATEST=$(ls -1 $DST | tail -n 1 || true)

mkdir -p "$DST/$TODAY"

if [[ -n "$LATEST" && -d "$DST/$LATEST" ]]; then
  rsync -aAX --delete \
    --link-dest="$DST/$LATEST" \
    "$SRC/" "$DST/$TODAY/"
else
  rsync -aAX --delete "$SRC/" "$DST/$TODAY/"
fi

# 오래된 스냅샷 정리(예: 30일 보관)
find "$DST" -maxdepth 1 -mindepth 1 -type d -mtime +30 -print -exec rm -rf {} \;
sudo chmod +x /usr/local/sbin/backup_local.sh
sudo /usr/local/sbin/backup_local.sh

검증:

ls -l /backup/snapshots | tail
du -sh /backup/snapshots/*

4) 원격 서버로 암호화 백업(SSH+gpg)

원격지 전송은 가로채임/유출에 대비해 전송 암호화(SSH)저장 암호화(gpg)를 함께 고려합니다. 여기서는 단순하고 강력한 tar + gpg + rsync 조합을 씁니다.

# GPG 수신자 키(원격 보관용) 준비 - 운영팀 키를 사용
# 로컬 서버에 공개키만 배치(개인키 없음)
gpg --list-keys

# /usr/local/sbin/backup_remote.sh
#!/usr/bin/env bash
set -euo pipefail

SRC_DIRS="/etc /srv/data"
TMP="/tmp/backup.$(date +%s)"
ARCHIVE="$TMP/archive.tar"
ENC="$TMP/archive.tar.gpg"

REMOTE_USER="backup"
REMOTE_HOST="backup.example.com"
REMOTE_DIR="/vault/$(hostname -s)"
GPG_RECIPIENT="ops@company.com"

mkdir -p "$TMP"
tar -cf "$ARCHIVE" $SRC_DIRS
gpg --yes --batch -r "$GPG_RECIPIENT" -o "$ENC" -e "$ARCHIVE"

# 원격 디렉터리 준비 후 전송(증분 대신 일자 파일명 권장)
DATE=$(date +%F)
ssh -o StrictHostKeyChecking=accept-new ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${REMOTE_DIR}"
rsync -az -e ssh "$ENC" ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/backup_${DATE}.tar.gpg

rm -rf "$TMP"
sudo chmod +x /usr/local/sbin/backup_remote.sh
sudo /usr/local/sbin/backup_remote.sh

대안: restic, borg 같은 전용 백업 도구는 중복제거/암호화/색인/복구가 더 편리합니다. 기본기는 rsync로, 요구가 커지면 전용툴로 전환하세요.


5) DB는 파일 복사 말고 “덤프”

운영 DB는 pg_dump(PostgreSQL)나 mysqldump(MySQL/MariaDB)로 논리 백업을 만든 후 파일 백업에 포함해야 복구가 쉬워집니다.

# PostgreSQL 예시(컨테이너 내부 or 호스트)
PG_DB=appdb
PG_USER=app
DUMP_DIR=/backup/db
mkdir -p $DUMP_DIR
pg_dump -U $PG_USER -d $PG_DB | gzip > $DUMP_DIR/${PG_DB}_$(date +%F).sql.gz

# MySQL/MariaDB 예시
mysqldump -u root -p --databases appdb | gzip > $DUMP_DIR/appdb_$(date +%F).sql.gz

덤프 파일은 원격 암호화 백업에도 포함하세요.


6) cron으로 스케줄링(충돌 방지 포함)

중복 실행을 막기 위해 잠금 파일을 사용하는 것이 안전합니다.

# /usr/local/sbin/backup_daily.sh
#!/usr/bin/env bash
set -euo pipefail
LOCK="/var/lock/backup_daily.lock"

exec 9> "$LOCK" || exit 1
if ! flock -n 9; then
  echo "Another backup is running."; exit 0
fi

# 1) DB 덤프
/usr/local/sbin/db_dump.sh

# 2) 로컬 스냅샷
/usr/local/sbin/backup_local.sh

# 3) 원격 전송
/usr/local/sbin/backup_remote.sh
sudo chmod +x /usr/local/sbin/backup_daily.sh
# 매일 새벽 3시(부하 적은 시간) 실행
echo "0 3 * * * root /usr/local/sbin/backup_daily.sh >> /var/log/backup.log 2>&1" | \
  sudo tee /etc/cron.d/backup-daily

7) 삭제/랜섬웨어 대응: 버전 보관과 쓰기 경로 분리

  • --delete는 양날의 검. 실수/랜섬웨어 삭제가 백업에도 전파됩니다. 스냅샷 방식으로 과거 버전을 보존하세요.
  • 원격 보관소는 쓰기 권한 최소화. 운영 서버에서 읽을 수 없어야 이상적(단방향 푸시).
  • 클라우드 스토리지 사용 시 객체 잠금(Object Lock)·버전관리 옵션을 고려.

8) 복구 리허설 스크립트(정기 실행 권장)

백업은 복구 테스트를 해야 진짜입니다. 샌드박스 디렉터리로 복구 연습을 자동화하세요.

# /usr/local/sbin/restore_rehearsal.sh
#!/usr/bin/env bash
set -euo pipefail

SRC_SNAPSHOT=$(ls -1 /backup/snapshots | tail -n 1)
TEST_DIR="/tmp/restore_test.$(date +%s)"
mkdir -p "$TEST_DIR"

rsync -a "/backup/snapshots/${SRC_SNAPSHOT}/" "$TEST_DIR/"

# DB 복구 리허설(논리 검사만, 실제 DB에 적용하지 않음)
if ls /backup/db/*.sql.gz >/dev/null 2>&1; then
  gunzip -c /backup/db/$(ls -1 /backup/db | tail -n 1) | head -n 50 > "$TEST_DIR/db_head.sql"
  # 간단 문법 체크(예: PostgreSQL 구문) - 실제 환경에 맞게 조정
fi

echo "OK: restored to $TEST_DIR"
sudo chmod +x /usr/local/sbin/restore_rehearsal.sh
# 매주 일요일 새벽 4시
echo "0 4 * * 0 root /usr/local/sbin/restore_rehearsal.sh >> /var/log/backup.log 2>&1" | \
  sudo tee /etc/cron.d/backup-verify

9) 모니터링/알림(실패를 알아차리기)

  • 로그: /var/log/backup.log를 중앙 수집(filebeat/Promtail) 또는 메일 알림에 연동.
  • 종료코드: 스크립트는 set -euo pipefail로 실패 시 즉시 종료 → cron 메일로 실패 알림.
  • 메트릭화: 백업 크기/기간/성공 여부를 Prometheus node_exporter textfile로 노출.
# /var/lib/node_exporter/textfile_collector/backup.prom (예시)
backup_last_success_timestamp_seconds $(date +%s)
backup_last_size_bytes $(du -sb /backup/snapshots | awk '{print $1}')

10) 자주 겪는 문제와 해결

  • 권한 오류: -aAX 사용 시 root 권한 필요. 백업 대상에 특수 권한/ACL이 있으면 -A -X 포함.
  • 대상 경로 오타로 대량 삭제: 항상 --dry-run으로 예행연습 후 적용. --delete는 신중.
  • 원격 전송 느림: -z 압축, --partial·--inplace 검토. 병목은 네트워크/디스크 IO 분석.
  • 용량 폭증: 스냅샷 보관 일수 줄이기, 대용량 로그/캐시 제외(--exclude 목록 관리).
# 예: 제외 목록 사용
cat > /etc/backup.exclude <<'EX'
/srv/data/cache/
/srv/data/tmp/
/srv/data/logs/*.old
EX

# rsync 호출시
rsync -aAX --delete --exclude-from=/etc/backup.exclude ...

11) 최종 체크리스트

  • 로컬 하드링크 스냅샷(일자별) 정상 생성, 30일 보관 정책 동작
  • DB 논리 백업 포함(일자 파일명), 원격지 암호화 보관
  • cron 스케줄 + 잠금 파일로 중복 실행 방지
  • 복구 리허설 주간 자동화, 로그/알림 연결
  • 삭제/랜섬웨어 대비: --delete 신중, 버전 보관/쓰기 최소화

여기까지면 “돌아가는 백업”이 아니라 복구 가능한 백업을 갖추게 됩니다. 다음 글에서는 logrotate로 로그 보존·용량 관리를 자동화하고, systemd와의 연동으로 애플리케이션 로그를 안정적으로 다루는 방법을 이어서 다룹니다.