infra
Platform

모듈 맵

[Infra Ops] 파일/설정/DB 백업과 재해복구 기초

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 50 / 52

[Infra Ops] 파일/설정/DB 백업과 재해복구 기초

파일/설정/DB 백업 전략, rsync/dump 백업, 스냅샷, 복구 절차, RTO/RPO, DR 개념까지

🚨INCIDENT ALERT
HIGH

서비스 운영 중 갑자기 DB 서버 디스크가 가득 차서 MySQL이 멈췄습니다. 백업이 있다고 생각했는데 담당자가 퇴사하면서 백업 스크립트가 실제로 돌아가고 있는지 아무도 모르는 상태입니다. 복구를 시도하려 해도 마지막 백업이 3개월 전 것뿐입니다.

백업은 "있으면 좋은 것"이 아닙니다. 없으면 복구 자체가 불가능합니다. 이 모듈에서는 파일·설정·DB를 어떻게 백업하고, 실제로 복구가 되는지 검증하는 방법까지 다룹니다.

이번 챕터에서 배울 것
  • 1RTO와 RPO 개념을 설명하고 우리 서비스 기준을 정의할 수 있다
  • 2Full / Incremental / Differential 백업 차이를 알고 전략을 선택할 수 있다
  • 3tar + rsync로 파일/설정 백업 스크립트를 작성하고 실행할 수 있다
  • 4mysqldump --single-transaction으로 무중단 DB 백업을 수행할 수 있다
  • 5백업에서 실제 복구 테스트를 수행하고 복구 성공을 검증할 수 있다
  • 6DR(재해복구) 개념과 Active-Passive / Active-Active 구성 차이를 설명할 수 있다

백업의 기본 개념

💡개념

RTO, RPO, 그리고 백업 유형

서버 디스크가 고장났습니다. "백업 있죠?"라는 질문에 "있긴 한데 일주일 전 것입니다"라고 답했습니다. 1주일치 주문 데이터가 사라질 수 있는 상황입니다. 복구까지 몇 시간이 걸리냐는 질문에는 답을 못 했습니다. 백업 주기와 복구 목표를 숫자로 정해두지 않으면, 막상 사고가 났을 때 "충분한가"를 판단할 기준이 없습니다. RTO와 RPO가 그 기준입니다.

백업 전략을 세우기 전에 두 가지 숫자를 먼저 정해야 합니다. RTO와 RPO입니다. 이 숫자를 정하지 않으면 "얼마나 자주", "어떤 방식으로" 백업해야 하는지 결정할 수 없습니다.

RTO, RPO, 그리고 백업 유형

RTO (Recovery Time Objective)
  장애 발생 후 서비스 복구까지 허용되는 최대 시간
  예: "RTO 4시간" → 장애 후 4시간 이내 서비스 재개 필요

RPO (Recovery Point Objective)
  복구 시 허용 가능한 데이터 손실 기간
  예: "RPO 1시간" → 최대 1시간 치 데이터 손실 감수 가능
       → 1시간마다 백업이 필요하다는 의미

백업 유형 비교:

유형내용용량복구 방법적합한 상황
Full (전체)전체 데이터크다Full 하나로 복구주 1회
Incremental (증분)마지막 백업 이후 변경분매우 작다Full + 모든 Incremental 순서대로매일 백업, 용량 제한 시
Differential (차등)마지막 Full 이후 변경분중간Full + 최신 Differential복구 단순성 vs 용량 균형

실무에서 가장 많이 쓰는 조합:

주 1회 Full 백업 (일요일 새벽)
일별 Incremental 백업 (월~토 새벽)
월별 Full 백업 → 별도 장기 보관

보관 정책 예시:
  일별 백업 → 7일 보관
  주별 백업 → 4주 보관
  월별 백업 → 12개월 보관

파일/설정 백업

💡개념

tar과 rsync로 설정 파일 백업하기

서버 장애 후 재구성할 때 가장 먼저 필요한 것이 설정 파일입니다. /etc/nginx/, /opt/tomcat/conf/, /etc/ssh/sshd_config — 이것들이 없으면 서버를 새로 만들어도 서비스가 올라오지 않습니다.

로컬 터미널
# 설정 파일 tar 백업 (날짜 포함 파일명)
sudo tar -czf /backup/config_$(date +%Y%m%d).tar.gz \
  /etc/nginx/ \
  /opt/tomcat/conf/ \
  /etc/ssh/sshd_config \
  /etc/systemd/system/

# 백업 파일 확인
ls -lh /backup/config_*.tar.gz

# 백업 내용 목록 확인 (복구 전 검증)
tar -tzf /backup/config_20260530.tar.gz | head -20

rsync로 원격 서버에 백업 (권장):

로컬 터미널
# 원격 서버로 애플리케이션 디렉터리 동기화
# --delete: 원본에서 삭제된 파일을 대상에서도 삭제
# -avz: archive(권한/타임스탬프 유지) + verbose + gzip 압축
rsync -avz /opt/app/ backup-server:/backup/app/ --delete

# 설정 파일 원격 백업
rsync -avz /etc/nginx/ backup-server:/backup/nginx-conf/

# SSH 키 인증 사용 (비밀번호 없이 자동 실행)
rsync -avz -e "ssh -i /root/.ssh/backup_key" /opt/app/ backup-server:/backup/app/

백업 자동화 (crontab):

로컬 터미널
# crontab -e 로 추가
# 매일 새벽 2시 설정 파일 백업 + rsync
0 2 * * * tar -czf /backup/config_$(date +\%Y\%m\%d).tar.gz /etc/nginx/ /opt/tomcat/conf/ && rsync -avz /backup/ backup-server:/backup/$(hostname)/

# 7일 지난 로컬 백업 삭제 (용량 관리)
0 3 * * * find /backup/ -name "config_*.tar.gz" -mtime +7 -delete

DB 백업

💡개념

MySQL 무중단 백업 — mysqldump

서비스를 내리고 MySQL을 백업하라는 지시를 받았습니다. 30분이면 된다고 했는데, 새벽 트래픽이 있어 서비스 중단이 불가능합니다. 그렇다고 서비스가 살아있는 상태에서 백업하면 트랜잭션 도중에 파일이 잘려 일관성이 깨질 수 있습니다. --single-transaction 옵션이 이 문제를 해결합니다. InnoDB 스토리지 엔진의 특성을 이용해 잠금 없이 일관된 스냅샷을 만들어 줍니다.

DB 백업은 파일 백업보다 까다롭습니다. 서비스가 운영 중인 상태에서 데이터 일관성을 유지하며 백업해야 하기 때문입니다. --single-transaction이 그 핵심 옵션입니다.

DB 클라이언트
# MySQL 전체 DB 무중단 백업 (InnoDB 기준)
mysqldump \
  --single-transaction \
  --all-databases \
  --routines \
  --triggers \
  -u root -p \
  > /backup/mysql_$(date +%Y%m%d_%H%M).sql

# 특정 DB만 백업
mysqldump --single-transaction -u root -p mydb > /backup/mydb_$(date +%Y%m%d).sql

# 압축 저장 (용량 절감)
mysqldump --single-transaction --all-databases -u root -p | gzip > /backup/mysql_$(date +%Y%m%d).sql.gz

# 백업 파일 크기 확인
ls -lh /backup/mysql_*.sql.gz

DB 복구:

DB 클라이언트
# SQL 파일에서 복구
mysql -u root -p < /backup/mysql_20260530.sql

# 압축 파일에서 복구
gunzip -c /backup/mysql_20260530.sql.gz | mysql -u root -p

# 특정 DB만 복구
mysql -u root -p mydb < /backup/mydb_20260530.sql

# 복구 후 확인
mysql -u root -p -e "SHOW DATABASES;"
mysql -u root -p mydb -e "SHOW TABLES;"

PostgreSQL 백업:

로컬 터미널
# pg_dump (PostgreSQL)
pg_dump -U postgres mydb > /backup/postgres_mydb_$(date +%Y%m%d).sql

# 전체 클러스터 백업
pg_dumpall -U postgres > /backup/postgres_all_$(date +%Y%m%d).sql

# 복구
psql -U postgres mydb < /backup/postgres_mydb_20260530.sql

스냅샷 백업

💡개념

VM 스냅샷과 LVM 스냅샷

OS 업그레이드 작업을 시작했는데 중간에 패키지 충돌로 시스템이 불안정해졌습니다. tar 백업을 복원하려면 OS를 재설치하고 파일을 하나씩 복사해야 합니다. 작업 전에 VM 스냅샷이 있었다면, 클릭 한 번에 작업 이전 상태로 돌아올 수 있습니다. 스냅샷은 파일 백업과 다르게 디스크 전체 상태를 특정 시점에 동결합니다. 시스템 변경 작업 전에 반드시 찍어야 하는 이유입니다.

스냅샷은 특정 시점의 디스크 상태를 통째로 보존합니다. 파일 단위가 아닌 블록 레벨 복사라 복구가 빠르지만, 공간 효율은 낮습니다.

로컬 터미널
# LVM 스냅샷 (Linux 볼륨 관리자)
# 현재 LVM 구성 확인
lvdisplay
vgdisplay

# 스냅샷 생성 (10GB 용량 할당)
sudo lvcreate -L 10G -s -n data_snap /dev/vg0/data

# 스냅샷을 마운트해서 파일 확인
sudo mkdir /mnt/snap
sudo mount /dev/vg0/data_snap /mnt/snap
ls /mnt/snap/

# 스냅샷 삭제 (더 이상 필요 없을 때)
sudo umount /mnt/snap
sudo lvremove /dev/vg0/data_snap

AWS EBS 스냅샷 (클라우드 환경):

로컬 터미널
# EBS 스냅샷 생성 (AWS CLI)
aws ec2 create-snapshot \
  --volume-id vol-12345678 \
  --description "Daily backup $(date +%Y%m%d)"

# 스냅샷 목록 확인
aws ec2 describe-snapshots --owner-ids self

# 오래된 스냅샷 삭제 (30일 이전)
aws ec2 describe-snapshots --owner-ids self \
  --query "Snapshots[?StartTime<='$(date -d '30 days ago' +%Y-%m-%d)'].SnapshotId" \
  --output text | xargs -I {} aws ec2 delete-snapshot --snapshot-id {}

RTO/RPO 비교 — DR 전략 선택 기준

실습 — 백업 스크립트 실행과 복구 테스트

1설정 파일 백업 + MySQL dump + rsync 통합 스크립트

아래 백업 스크립트를 /opt/scripts/backup.sh로 저장하고 실행합니다. 실무에서 쓰는 백업 스크립트의 기본 구조입니다.

로컬 터미널
#!/bin/bash
# /opt/scripts/backup.sh

BACKUP_DIR="/backup/$(date +%Y%m%d)"
LOG_FILE="/var/log/backup.log"

mkdir -p "$BACKUP_DIR"

echo "[$(date)] 백업 시작" >> "$LOG_FILE"

# 1. 설정 파일 백업
tar -czf "$BACKUP_DIR/config.tar.gz" \
  /etc/nginx/ \
  /opt/tomcat/conf/ 2>> "$LOG_FILE"
echo "[$(date)] 설정 파일 백업 완료" >> "$LOG_FILE"

# 2. MySQL 백업 (비밀번호는 /root/.my.cnf에 저장)
mysqldump --single-transaction --all-databases 2>> "$LOG_FILE" | \
  gzip > "$BACKUP_DIR/mysql.sql.gz"
echo "[$(date)] MySQL 백업 완료" >> "$LOG_FILE"

# 3. 원격 rsync (backup-server가 접근 가능한 경우)
# rsync -avz "$BACKUP_DIR/" backup-server:/backup/$(hostname)/ 2>> "$LOG_FILE"

echo "[$(date)] 백업 완료: $BACKUP_DIR" >> "$LOG_FILE"

# 7일 이전 백업 삭제
find /backup/ -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>> "$LOG_FILE"
sudo bash /opt/scripts/backup.sh
🔍확인 포인트
  • /backup/YYYYMMDD/ 디렉터리가 생성됐는지 확인 (ls -la /backup/)
  • config.tar.gzmysql.sql.gz 파일이 0바이트 이상인지 확인 (ls -lh /backup/YYYYMMDD/)
  • /var/log/backup.log백업 완료 메시지가 기록됐는지 확인
2복구 테스트 — 백업 파일에서 실제 복구 (개발환경)

백업이 있다고 안심하면 안 됩니다. 복구가 실제로 되는지 정기적으로 테스트해야 합니다. 개발 환경의 별도 DB(test_restore)에 복구해 봅니다.

DB 클라이언트
# 테스트용 DB 생성
mysql -u root -p -e "CREATE DATABASE test_restore;"

# 백업에서 복구
gunzip -c /backup/$(date +%Y%m%d)/mysql.sql.gz | mysql -u root -p test_restore

# 복구 결과 확인
mysql -u root -p test_restore -e "SHOW TABLES;"
mysql -u root -p test_restore -e "SELECT COUNT(*) FROM users;"  # 레코드 수 확인

# 설정 파일 복구 테스트
tar -tzf /backup/$(date +%Y%m%d)/config.tar.gz     # 내용 확인
tar -xzf /backup/$(date +%Y%m%d)/config.tar.gz -C /tmp/restore_test/  # 임시 경로에 풀기
ls /tmp/restore_test/etc/nginx/                     # 파일 존재 확인

# 테스트용 DB 삭제
mysql -u root -p -e "DROP DATABASE test_restore;"
gunzip -c /backup/$(date +%Y%m%d)/mysql.sql.gz | mysql -u root -p test_restore
🔍실행 후 확인할 것
  • 백업 스크립트 실행 후 /backup/YYYYMMDD/ 디렉터리가 생성됐는가
  • config.tar.gz와 mysql.sql.gz 파일이 0바이트 이상으로 생성됐는가
  • /var/log/backup.log에 '백업 완료' 메시지가 기록됐는가
  • 복구 테스트에서 test_restore DB에 테이블이 정상 생성됐는가
  • 레코드 수가 원본과 일치하는가 (COUNT(*) 비교)

DR(재해복구) 개념

💡개념

Active-Passive DR과 Active-Active DR

백업은 "데이터 보호"이고, DR(Disaster Recovery)은 "서비스 연속성"입니다. 서버 건물이 화재로 소실됐을 때 다른 곳에서 서비스를 재개하는 것이 DR의 목표입니다.

DR 구성 유형:

유형구성RTORPO비용특징
Active-Passive주 센터 운영 중, 백업 센터 대기수 시간수 시간~1일중간가장 일반적
Active-Active두 센터 모두 운영, 트래픽 분산수 분거의 0높음대형 서비스
Cold Standby백업 센터 꺼진 상태수일마지막 백업낮음소규모/비핵심
Active-Passive DR 흐름:
  평상시: 주 센터 운영, 백업 센터에 rsync/복제로 데이터 동기화
  장애시: DNS를 백업 센터 IP로 변경
         → 서비스 재개 (RTO: DNS TTL + 기동 시간)

Active-Active DR 흐름:
  평상시: 두 센터 모두 트래픽 처리 (로드밸런서로 분산)
  장애시: 한 센터 장애 → 자동으로 다른 센터로 100% 전환
         → 서비스 중단 거의 없음 (RTO: 수 초~수 분)

복구 테스트가 중요한 이유:

실제 사례:
  백업은 3년간 매일 실행
  → 어느 날 복구 시도
  → mysqldump 파일이 깨져 있음 (디스크 용량 부족으로 중간에 잘린 파일)
  → 실제 복구 불가

예방:
  월 1회 개발환경에서 복구 테스트 실행
  복구 성공 여부를 로그로 기록
  정기 리포트로 백업 상태 모니터링

트러블슈팅

원인: 백업 중 디스크 용량 부족, 네트워크 단절, 또는 mysqldump 실행 중 DB 연결 끊김으로 파일이 중간에 잘립니다. 파일 크기는 있어도 내용이 불완전한 경우입니다.

로컬 터미널
# 백업 파일 무결성 확인
gunzip -t /backup/mysql_20260530.sql.gz
# 출력이 없으면 파일 정상, gzip: ... unexpected end of file 이면 파일 손상

# SQL 파일 끝부분 확인 (정상적인 mysqldump 결과의 마지막 줄)
gunzip -c /backup/mysql_20260530.sql.gz | tail -5
# 정상: -- Dump completed on 2026-05-30 02:15:31

# 백업 스크립트에 검증 추가
mysqldump --single-transaction --all-databases | gzip > /backup/mysql.sql.gz
if [ ${PIPESTATUS[0]} -ne 0 ]; then
  echo "백업 실패!" | mail -s "BACKUP FAILED" admin@example.com
fi

# 디스크 용량 사전 확인 (백업 전)
df -h /backup
# Use% 80% 이상이면 백업 전 정리 필요

예방: 백업 스크립트에 종료 코드 체크와 파일 크기 최소 임계값 검증을 추가합니다. 월 1회 복구 테스트를 캘린더에 등록해 습관화합니다.

원인: 로그 테이블(audit_log, access_log 등)이 수백만 건 쌓인 상태에서 전체 dump를 받으면 수십 GB가 되는 경우가 있습니다. 이 테이블들은 백업이 필요 없는 경우가 많습니다.

DB 클라이언트
# 테이블별 크기 확인 (큰 테이블 찾기)
mysql -u root -p -e "
SELECT table_schema, table_name,
       ROUND(data_length/1024/1024, 2) AS data_MB,
       ROUND(index_length/1024/1024, 2) AS index_MB
FROM information_schema.tables
ORDER BY data_length DESC
LIMIT 20;"

# 특정 테이블 제외하고 백업
mysqldump --single-transaction \
  --ignore-table=mydb.audit_log \
  --ignore-table=mydb.access_log \
  mydb | gzip > /backup/mydb_no_logs_$(date +%Y%m%d).sql.gz

# 데이터 없이 스키마만 백업 (로그 테이블은 구조만)
mysqldump --no-data mydb audit_log > /backup/audit_log_schema.sql

# 로그 테이블 주기적 정리 (30일 이전 데이터 삭제)
mysql -u root -p mydb -e "
DELETE FROM audit_log WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);"

예방: 백업 전략 수립 시 테이블별 백업 필요성을 분류합니다. 로그·임시·캐시 테이블은 제외 목록에 넣고 문서화합니다.

💼
실무 맥락
현업 패턴

실제 업무에서 이 흐름이 생명줄이 되는 상황:

백업은 평소에는 존재 자체를 잊게 만드는 것이 잘 된 백업입니다. 그러나 사고가 났을 때 없으면 모든 것이 끝납니다. 실무에서 주니어가 바로 적용할 수 있는 최소 기준:

로컬 터미널
# 1. 지금 당장 실행: 우리 서버에 백업이 돌고 있는가 확인
crontab -l | grep backup
ls -lt /backup/ | head -10  # 가장 최근 백업 날짜 확인

# 2. 백업이 없다면: 최소 스크립트라도 crontab에 등록
# 3. 백업이 있다면: 복구 테스트 한 번이라도 해보기

# 복구 테스트 기록 템플릿
echo "$(date): 복구 테스트 성공 — DB 레코드 수 일치, 설정 파일 정상" >> /var/log/restore_test.log

RTO/RPO를 서비스 오너(개발 리더, 기획팀)와 합의해두는 것도 인프라팀의 역할입니다. "4시간 내 복구"를 목표로 한다면 백업 주기, 복구 절차, DR 구성이 모두 그에 맞게 설계되어야 합니다. 다음 모듈에서는 온프레미스와 클라우드 인프라의 차이, AWS EC2/RDS/S3/CloudWatch 운영 실무를 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

RTO와 RPO의 차이를 올바르게 설명한 것은?

Q2

전체 백업(Full)과 증분 백업(Incremental)을 비교한 것으로 올바른 것은?

Q3

MySQL 백업 시 --single-transaction 옵션이 필요한 이유는?

Q4

백업 파일을 같은 서버에만 보관하는 것이 위험한 이유는?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops고급 · 90
[Infra Ops] 장애/배포/연계/보안 케이스 실전 종합
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점