[도커 30강] 25강. 백업/복구 전략: DB 볼륨 다루기

[도커 30강] 25강. 백업/복구 전략: DB 볼륨 다루기

서비스를 운영하다 보면 장애 자체보다 더 무서운 순간이 있습니다. 바로 "데이터가 사라졌을 때"입니다. 컨테이너는 쉽게 지우고 다시 만들 수 있지만, 데이터베이스 데이터는 한 번 잃으면 비즈니스 피해로 바로 이어집니다. 그래서 도커 환경에서 DB를 쓸 때는 성능 튜닝보다 먼저 백업/복구 전략을 설계해야 합니다.

이번 강의에서는 "볼륨이 있으니 안전하다"라는 흔한 오해를 넘어, 실제 운영에서 필요한 백업 단위·복구 절차·검증 포인트를 정리합니다. 핵심은 단순합니다. 백업은 파일을 복사하는 행위가 아니라, 복구 가능한 상태를 증명하는 운영 절차여야 합니다.


핵심 개념

  • 도커 볼륨은 영속 저장소이지 백업이 아닙니다. 컨테이너를 재생성해도 데이터가 남는다는 뜻일 뿐, 실수 삭제/손상/랜섬웨어/운영자 오류까지 막아주지는 않습니다.
  • DB 백업은 크게 **논리 백업(logical backup)**과 **물리 백업(physical backup)**으로 나눕니다. 학습/중소규모 서비스에서는 먼저 논리 백업(pg_dump, mysqldump)을 안정적으로 자동화하는 것이 현실적입니다.
  • 복구 전략은 **RPO(허용 가능한 데이터 손실 시점)**와 **RTO(허용 가능한 복구 시간)**를 기준으로 설계해야 합니다. 매일 1회 백업은 간단하지만, 최대 24시간 데이터 손실을 감수한다는 의미입니다.
  • 운영에서 중요한 것은 백업 파일 개수보다 복구 리허설 성공률입니다. 정기적으로 실제 복구를 해보지 않으면, 사고 시점에 백업이 깨져 있음을 처음 알게 됩니다.
  • 볼륨 기반 운영에서는 "볼륨 스냅샷 + DB 덤프"를 함께 가져가는 혼합 전략이 실무적으로 가장 안정적입니다.

기본 사용

예제 1) Postgres 컨테이너 + named volume 구성

mkdir -p ~/docker100/lesson25 && cd ~/docker100/lesson25

cat > compose.db.yaml <<'YAML'
services:
  postgres:
    image: postgres:16-alpine
    container_name: l25-postgres
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: apppass
      POSTGRES_DB: appdb
    volumes:
      - pg_data:/var/lib/postgresql/data
    ports:
      - "5433:5432"

volumes:
  pg_data:
YAML

docker compose -f compose.db.yaml up -d
docker compose -f compose.db.yaml ps
docker volume ls | grep pg_data || true

설명:

  • pg_data라는 named volume이 데이터 경로(/var/lib/postgresql/data)에 연결됩니다.
  • 이 상태에서 컨테이너를 재생성해도 데이터는 남습니다. 그러나 이건 영속성이지 백업본이 아닙니다.
  • 먼저 데이터 생성 → 백업 → 복구 테스트 흐름을 반드시 한 번 완주해보세요.

예제 2) 논리 백업 생성 및 보관

# 샘플 데이터 생성
docker exec -i l25-postgres psql -U app -d appdb <<'SQL'
create table if not exists orders (
  id serial primary key,
  item text not null,
  amount int not null,
  created_at timestamptz default now()
);
insert into orders(item, amount) values ('keyboard', 2), ('mouse', 5);
SQL

# 백업 디렉터리 준비
mkdir -p backups

# custom format 백업 생성
docker exec l25-postgres pg_dump -U app -d appdb -Fc > backups/appdb_$(date +%Y%m%d_%H%M%S).dump

ls -lh backups

설명:

  • -Fc 형식은 pg_restore 시 테이블 단위 복구, 병렬 복구 등 선택지가 많아 실무에서 유리합니다.
  • 백업 파일은 컨테이너 내부가 아니라 호스트/원격 스토리지로 즉시 내보내야 합니다.
  • 백업 파일명에 타임스탬프를 넣어 세대 관리(보존 기간)를 명확히 하세요.

예제 3) 장애 가정 후 복구 절차 검증

# (가정) 테이블 데이터 손상/삭제 상황 재현
docker exec -i l25-postgres psql -U app -d appdb <<'SQL'
delete from orders;
select count(*) from orders;
SQL

# 최신 백업 파일 선택
LATEST=$(ls -1 backups/*.dump | tail -n 1)
echo "restore from: $LATEST"

# 복구용 DB 생성 후 데이터 복원 검증
docker exec -i l25-postgres psql -U app -d postgres -c "drop database if exists appdb_restore;"
docker exec -i l25-postgres psql -U app -d postgres -c "create database appdb_restore;"
cat "$LATEST" | docker exec -i l25-postgres pg_restore -U app -d appdb_restore

# 복구 결과 확인
docker exec -i l25-postgres psql -U app -d appdb_restore -c "select * from orders order by id;"

설명:

  • 운영 DB를 바로 덮어쓰기 전에, 별도 DB(appdb_restore)로 복구 검증을 먼저 수행하는 것이 안전합니다.
  • 성공 기준은 단순 쿼리 1개가 아니라, 핵심 테이블 row 수·인덱스·애플리케이션 연결까지 확인하는 것입니다.
  • 이 절차가 문서로 정리되어 있어야 당직자 교대 시에도 대응 품질이 유지됩니다.

예제 4) 볼륨 단위 백업(파일 아카이브) 보조 전략

# 컨테이너 중지
docker compose -f compose.db.yaml stop postgres

# 임시 컨테이너로 볼륨 내용을 tar로 백업
docker run --rm \
  -v pg_data:/data \
  -v "$PWD/backups":/backup \
  alpine:3.20 \
  sh -c 'cd /data && tar czf /backup/pg_data_$(date +%Y%m%d_%H%M%S).tar.gz .'

# 다시 기동
docker compose -f compose.db.yaml start postgres
ls -lh backups | grep pg_data || true

설명:

  • 파일 단위 백업은 덤프 백업을 보완하는 용도로 유효합니다.
  • 다만 DB 엔진 특성상 "실행 중 파일 복사"는 일관성 문제가 생길 수 있어, 정책 없는 라이브 복사는 피해야 합니다.
  • 운영에서는 스냅샷 기능(EBS/LVM/ZFS 등) + DB 엔진 권장 방식 병행이 일반적입니다.

자주 하는 실수

실수 1) 볼륨이 있으니 백업은 필요 없다고 생각

  • 원인: 컨테이너 삭제 후에도 데이터가 남는 경험을 "안전"으로 과대해석합니다.
  • 해결: 영속성과 백업은 분리해서 설계하세요. 최소 일 1회 덤프 + 원격 보관부터 시작합니다.

실수 2) 백업 파일을 같은 호스트 디스크에만 보관

  • 원인: 구현이 가장 간단하기 때문입니다.
  • 해결: 같은 머신 장애를 대비해 최소 1개 사본은 다른 스토리지(오브젝트 스토리지/NAS/다른 서버)로 복제합니다.

실수 3) 복구 테스트 없이 "백업 성공 로그"만 믿기

  • 원인: 자동화 로그가 초록색이면 끝났다고 판단합니다.
  • 해결: 주 1회라도 샘플 복구 리허설을 자동화하고, 실패 시 즉시 알림이 오게 구성합니다.

실수 4) 전체 복구 절차를 문서화하지 않음

  • 원인: 담당자가 머리로 기억하고 있다고 생각합니다.
  • 해결: "누가 해도 같은 결과"가 나오도록 명령어, 점검 쿼리, 롤백 방법까지 런북으로 남깁니다.

실무 패턴

실무에서는 백업 전략을 "도구 선택"보다 "복구 목적"에서 출발합니다. 먼저 서비스별 RPO/RTO를 정하고, 그 수치에 맞춰 주기와 방식, 보관 기간을 설계합니다. 예를 들어 사내 관리 도구는 RPO 24시간도 허용될 수 있지만, 결제 데이터는 5~15분 수준이 필요할 수 있습니다.

가장 많이 쓰는 기본 패턴은 다음과 같습니다. (1) 매일 새벽 전체 논리 백업, (2) 낮 시간대 증분 성격의 추가 백업, (3) 백업 파일 원격 복제, (4) 주간 복구 리허설 자동화입니다. 여기에 백업 메타데이터(파일 크기, 해시, 생성 시각, 덤프 버전)를 같이 저장하면 문제 추적이 훨씬 쉬워집니다.

팀 협업 관점에서는 "백업 성공"의 정의를 통일해야 합니다. 파일 생성만으로 성공이 아니라, 복구 후 핵심 쿼리 검증까지 통과해야 성공으로 봐야 합니다. 또한 권한 최소화 원칙에 따라 백업 계정은 읽기 중심 권한만 주고, 백업 저장소 접근도 IAM/토큰 만료 정책으로 통제하는 것이 안전합니다.

운영 장애 대응에서는 복구 우선순위도 중요합니다. 모든 테이블을 동시에 완벽 복구하려고 하면 시간이 늘어납니다. 핵심 서비스 재가동에 필요한 스키마/테이블을 먼저 올리고, 비핵심 데이터는 후순위로 복원하는 단계적 접근이 RTO를 줄입니다.

마지막으로, 백업은 "한 번 잘 만든 스크립트"가 아니라 지속적으로 점검하는 제품입니다. DB 버전 업그레이드, 스키마 변경, 데이터 증가에 따라 백업 시간과 파일 크기, 복구 속도가 계속 변합니다. 월 단위로 지표를 보고 정책을 조정해야 실제 운영 품질이 올라갑니다.

오늘의 결론

한 줄 요약: 도커 DB 운영의 안전은 볼륨 자체가 아니라, 복구 가능한 백업 절차를 주기적으로 검증하는 습관에서 나온다.

연습문제

  1. 현재 사용 중인 DB 컨테이너에서 논리 백업 스크립트를 만들고, 파일명 규칙(서비스명+시각+버전)을 정해보세요.
  2. 백업 파일을 다른 저장소로 복제한 뒤, 별도 복구 DB에서 핵심 테이블 3개를 검증하는 자동 스크립트를 작성해보세요.
  3. 장애 시나리오(실수 삭제/호스트 장애/백업 손상)를 3개 정하고, 각 시나리오의 목표 RPO/RTO와 대응 절차를 표로 정리해보세요.

이전 강의 정답

24강 연습문제 해설:

  • 네트워크 분리의 핵심은 "연결 가능"이 아니라 "연결 필요" 기준입니다. frontend, backend를 나눌 때 API만 이중 연결하고 DB는 backend 전용으로 두는 설계가 기본 정답입니다.
  • 내부 서비스의 ports 제거는 보안의 출발점입니다. 외부 접근이 꼭 필요하면 상시 공개 대신 bastion/VPN/임시 터널 같은 통제 경로를 선택해야 합니다.
  • internal: true 적용 후에는 반드시 운영 도구(백업/모니터링) 통신 경로를 함께 점검해야 합니다. 보안 강화만 보고 적용하면 운영 장애를 스스로 만들 수 있습니다.

실습 환경/재현 정보

  • OS: macOS 15+ 또는 Ubuntu 22.04+
  • Docker 버전: Docker Engine/Desktop 25.x 이상
  • DB 이미지: postgres:16-alpine
  • 실행 순서:
    1. Compose로 Postgres + named volume 기동
    2. 샘플 데이터 생성 후 pg_dump -Fc로 논리 백업
    3. 데이터 삭제 시나리오 재현
    4. 별도 복구 DB(appdb_restore)에 pg_restore 수행
    5. row 수/핵심 쿼리 검증 후 런북 업데이트
  • 재현 체크:
    • 백업 파일이 컨테이너 외부 경로에 생성되는가?
    • 복구 DB에서 핵심 테이블 데이터가 정상 조회되는가?
    • 복구 절차가 문서화되어 당직자가 그대로 실행 가능한가?
    • 백업 파일 원격 복제와 보존 기간 정책이 있는가?
    • 정기 복구 리허설 일정(자동/수동)이 정의되어 있는가?