2026년 5월 13일 수요일
글 목록
Lv.2 초급PostgreSQL
20분 읽기Lv.2 초급
시리즈PostgreSQL 백업/복구 완전 정복 · 파트 3/6시리즈 허브 보기

PostgreSQL 백업/복구 Part 3 — pg_basebackup과 WAL 아카이빙

PostgreSQL 백업/복구 Part 3 — pg_basebackup과 WAL 아카이빙

Pg_dump는 PITR을 직접 지원하지 않는다. 프로덕션 재해 복구의 핵심은 Base Backup과 WAL 아카이빙의 조합이다. Base Backup은 특정 시점의 스냅샷이고, WAL 아카이빙은 그 이후의 시간을 복구 가능하게 만드는 기록이다. pg_basebackup의 옵션 선택이 복구 가능성을 좌우하며, PostgreSQL 17+의 네이티브 증분 백업(--incremental)은 대형 DB 백업 비용을 낮추는 중요한 변화다.

시리즈 구성

  • Part 1 — 백업의 기초와 전략
  • Part 2 — 논리적 백업 — pg_dump & pg_dumpall 실전 활용
  • Part 3 — 물리적 백업 — pg_basebackup과 WAL 아카이빙 (현재 편)
  • Part 4 — PITR(Point-in-Time Recovery) 구현 가이드 (연재 예정)
  • Part 5 — 엔터프라이즈 백업 도구 비교 — pgBackRest vs Barman vs WAL-G (연재 예정)
  • Part 6 — 백업 자동화 & 모니터링, 그리고 복구 훈련 (연재 예정)

목차

  1. 들어가며 — 물리적 백업이 필요한 이유
  2. WAL — 물리적 백업의 핵심 개념
  3. 전체 아키텍처 — Base Backup + WAL 아카이빙
  4. WAL 아카이빙 설정
  5. pg_basebackup — 물리적 베이스 백업
  6. 증분 백업 — PostgreSQL 17+
  7. 관련 파라미터 튜닝 가이드
  8. WAL 아카이빙 모니터링
  9. 물리적 백업 자동화 스크립트
  10. 논리 vs 물리 — 언제 무엇을 쓸까
  11. 마치며

1. 들어가며 — 물리적 백업이 필요한 이유

Part 2에서 pg_dump의 강점과 한계를 살펴봤다. 논리적 백업은 이식성과 선택적 복구에 탁월하지만, 프로덕션 재해 복구의 핵심 요구사항인 PITR(Point-in-Time Recovery)을 직접 지원하지 않는다.

물리적 백업이 반드시 필요한 상황을 생각해보자.

"오늘 오후 2시에 누군가 중요 테이블을 DROP했습니다. 마지막 pg_dump 백업은 어제 새벽 2시입니다. 12시간치 데이터가 손실됐습니다."

물리적 백업과 WAL 아카이빙 조합이 있었다면, 오후 1시 59분 시점으로 정확하게 복구할 수 있다. 이것이 PITR이며, 물리적 백업의 존재 이유다.


2. WAL — 물리적 백업의 핵심 개념

WAL(Write-Ahead Log)은 PostgreSQL이 데이터 파일에 변경을 적용하기 전 모든 변경 사항을 먼저 기록하는 트랜잭션 로그다. 클러스터 데이터 디렉토리의 pg_wal/ 서브디렉토리에 기본 16MB 단위 세그먼트 파일로 저장된다.

WAL의 역할은 두 가지다.

충돌 안전성(Crash Safety): 시스템이 비정상 종료됐을 때 WAL을 재생해 데이터베이스를 일관된 상태로 복원한다.

PITR의 기반: Base Backup에 WAL을 순서대로 재생하면 백업 이후 임의의 시점으로 데이터베이스를 복구할 수 있다.


3. 전체 아키텍처 — Base Backup + WAL 아카이빙

프로덕션 물리 백업 전략은 두 구성 요소의 조합으로 이루어진다.

Base Backup은 특정 시점의 데이터 디렉토리 전체 스냅샷이다. 이것만으로는 백업 시점 이후의 변경을 복구할 수 없다.

WAL 아카이빙은 Base Backup 이후 발생한 모든 변경 사항을 보존한다. 두 요소가 결합될 때 비로소 PITR이 가능해진다.


4. WAL 아카이빙 설정

4.1 postgresql.conf 필수 파라미터

WAL 아카이빙은 기본적으로 비활성화 상태다. 활성화하려면 서버 재시작이 필요하므로, 서비스 오픈 전에 반드시 설정해야 한다.

# postgresql.conf

# WAL 레벨: replica 이상이어야 아카이빙 및 PITR 지원
# minimal 설정 시 archive_mode 활성화 불가
wal_level = replica

# 아카이빙 활성화 (서버 재시작 필요)
archive_mode = on

# WAL 파일을 아카이브 디렉토리로 복사하는 명령
# %p: 아카이빙할 WAL 파일의 전체 경로
# %f: WAL 파일명
archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'

# WAL 세그먼트가 가득 차지 않아도 강제 아카이빙하는 타임아웃 (초)
# 낮출수록 최신 WAL이 더 빨리 아카이빙되어 RPO가 단축됨
archive_timeout = 300

# WAL 크기 설정
max_wal_size = 4GB
min_wal_size = 1GB
wal_compression = on

archive_mode는 서버 재시작이 필요하지만, archive_commandpg_reload_conf() 또는 SIGHUP으로 재시작 없이 변경할 수 있다. 아카이빙을 임시로 중단해야 할 때는 archive_mode를 끄는 것보다 archive_command를 수정하는 방식이 운영상 더 유연하다.

4.2 archive_mode: on vs always

모드동작
on프라이머리에서만 아카이빙
always스탠바이 서버에서도 아카이빙. 스트리밍 복제로 받은 WAL도 아카이빙

스탠바이 서버가 아카이브 스토리지에 더 가깝거나, 프라이머리 장애 시에도 WAL 아카이빙을 이어가야 하는 환경에서는 always가 유용하다.

4.3 archive_command의 exit code 규칙

archive_commandexit code 0을 반환해야만 PostgreSQL이 해당 WAL 파일을 아카이빙 성공으로 간주하고 삭제·재활용을 허용한다. non-zero 반환 시 PostgreSQL은 계속 재시도한다.

이 동작은 실무 리스크와 직결된다.

  • 실패를 무조건 무시(항상 exit 0)하면 아카이빙되지 않은 WAL 파일이 삭제될 수 있다.
  • 아카이브 목적지 접근 불가 상태에서 재시도가 누적되면 pg_wal/ 디렉토리가 채워져 디스크 풀 장애로 이어질 수 있다.

스크립트 오류 처리를 신중하게 설계해야 한다.

4.4 아카이브 디렉토리 준비

# 아카이브 디렉토리 생성 및 권한 설정
mkdir -p /var/lib/postgresql/archive
chown postgres:postgres /var/lib/postgresql/archive
chmod 700 /var/lib/postgresql/archive

# postgresql.conf 변경 후 재시작
systemctl restart postgresql

# 아카이빙 상태 확인
psql -U postgres -c "SELECT * FROM pg_stat_archiver;"

4.5 원격 아카이빙

로컬 디스크만 사용하면 서버 전체 장애 시 아카이브도 함께 손실된다. 원격 스토리지 아카이빙이 필수다.

# rsync를 이용한 원격 서버 아카이빙
archive_command = 'rsync -a %p backup_user@archive-server:/pg_archive/%f'

# AWS S3
archive_command = 'aws s3 cp %p s3://my-pg-archive/wal/%f'

# 이중 저장 스크립트 호출
archive_command = '/usr/local/bin/archive_wal.sh %p %f'

로컬과 원격을 모두 저장하는 스크립트 예시다.

#!/bin/bash
# /usr/local/bin/archive_wal.sh
# 안전한 WAL 아카이빙: 로컬 + S3 이중 저장

WAL_PATH=$1
WAL_FILE=$2
LOCAL_ARCHIVE="/var/lib/postgresql/archive"
S3_BUCKET="s3://my-pg-archive/wal"

# 로컬 복사 실패 시 즉시 중단 (exit 1 → PostgreSQL이 재시도)
cp "${WAL_PATH}" "${LOCAL_ARCHIVE}/${WAL_FILE}" || exit 1

# S3 복사 실패는 로컬 성공 이후이므로 재시도보다 경보를 우선
# exit 0으로 아카이빙 진행은 허용하되, 알림으로 후속 조치 유도
aws s3 cp "${WAL_PATH}" "${S3_BUCKET}/${WAL_FILE}" \
  --only-show-errors || \
  echo "[WARN] S3 upload failed for ${WAL_FILE}" >> /var/log/pg_archive.log

exit 0

S3 복사 실패 시 exit 0을 반환하는 것은 "아카이빙 중단보다 경보 후 처리"를 선택한 설계다. 팀 운영 정책에 따라 S3 실패도 exit 1로 처리해 재시도하도록 바꿀 수 있다. 어느 쪽을 선택하든 의도를 코드 주석이나 운영 문서에 명시해야 한다.


5. pg_basebackup — 물리적 베이스 백업

5.1 사전 요건

# postgresql.conf
wal_level = replica       # 필수
archive_mode = on         # PITR 목적이라면 필수
max_wal_senders = 3       # pg_basebackup은 복제 프로토콜 사용
-- pg_hba.conf에 복제 접속 허용 추가
-- host replication backup_user 192.168.1.0/24 scram-sha-256

-- 백업 전용 사용자 (superuser 불필요, REPLICATION 권한만)
CREATE USER backup_user WITH REPLICATION PASSWORD 'securepass';

5.2 주요 옵션

-X (WAL 포함 방식)

옵션설명권장
-X noneWAL 미포함비권장 (복구 불가 가능성)
-X fetch백업 완료 후 WAL 수집조건부 (WAL 보존 보장 안됨)
-X stream백업 중 WAL 실시간 스트리밍권장

-X stream이 권장되는 이유: 백업 진행 중 생성된 WAL이 백업 완료 전에 재활용(삭제)될 위험을 차단한다.

-F (출력 형식)

옵션설명
-Fp (plain)파일 그대로 복사. 복구 시 바로 사용 가능
-Ft (tar)테이블스페이스별 tar 아카이브. 전송·저장에 유리

--checkpoint

# 빠른 체크포인트 강제 (백업 시작 시간 단축, I/O 부하 순간 증가)
pg_basebackup ... --checkpoint=fast

# 분산 체크포인트 (기본값, I/O 부하 분산)
pg_basebackup ... --checkpoint=spread

5.3 실전 백업 레시피

# 권장: plain 포맷 + WAL 스트리밍 + 빠른 체크포인트
pg_basebackup \
  -h localhost \
  -U backup_user \
  -D /backups/basebackup/$(date +%Y%m%d_%H%M%S) \
  -Fp \
  -Xs \
  -P \
  --checkpoint=fast

# tar 포맷 + 압축 (전송·스토리지 비용 절감)
pg_basebackup \
  -h localhost \
  -U backup_user \
  -D /backups/basebackup/$(date +%Y%m%d) \
  -Ft \
  -z \
  -Xs \
  -P \
  --checkpoint=fast

# 백업 완료 후 무결성 검증 (PostgreSQL 13+)
pg_verifybackup /backups/basebackup/20260414

5.4 pg_verifybackup — 백업 무결성 검증

PostgreSQL 13에서 도입된 pg_verifybackupbackup_manifest 파일을 기반으로 백업의 무결성을 검증한다.

# 기본 검증
pg_verifybackup /backups/basebackup/20260414

# WAL 검증 건너뛰기 (데이터 파일만 확인, 빠른 검증)
pg_verifybackup --no-parse-wal /backups/basebackup/20260414

backup_manifest가 없는 PostgreSQL 12 이하 버전 백업에는 사용할 수 없다.


6. 증분 백업 — PostgreSQL 17+

6.1 개요

PostgreSQL 16까지는 pg_basebackup이 전체 백업(Full Backup)만 지원했다. 증분 백업은 pgBackRest, Barman 같은 서드파티 도구에 의존해야 했다.

PostgreSQL 17부터 pg_basebackup에 네이티브 증분 백업(--incremental)이 도입됐다. 이전 백업 이후 변경된 블록만 저장해 백업 시간과 스토리지를 줄일 수 있다.

6.2 활성화 요건

증분 백업을 사용하려면 summarize_wal 파라미터를 활성화해야 한다. PostgreSQL 17에서 새로 도입된 파라미터로, WAL 요약기(WAL Summarizer) 프로세스를 가동한다.

# postgresql.conf (PostgreSQL 17+)
summarize_wal = on   # 기본값: off, 서버 재시작 필요

6.3 증분 백업 워크플로

# 1단계: 전체 백업 (기준점)
pg_basebackup \
  -h localhost \
  -U backup_user \
  -D /backups/full_20260414 \
  -Ft -Xs -P --checkpoint=fast

# backup_manifest 파일이 /backups/full_20260414/backup_manifest 에 생성됨

# 2단계: 첫 번째 증분 백업 (전체 백업 기준)
pg_basebackup \
  --incremental=/backups/full_20260414/backup_manifest \
  -h localhost \
  -U backup_user \
  -D /backups/incr_20260415 \
  -Ft -Xs -P --checkpoint=fast

# 3단계: 두 번째 증분 백업 (직전 증분 백업 기준)
pg_basebackup \
  --incremental=/backups/incr_20260415/backup_manifest \
  -h localhost \
  -U backup_user \
  -D /backups/incr_20260416 \
  -Ft -Xs -P --checkpoint=fast

6.4 복원: pg_combinebackup

증분 백업은 단독으로 복원할 수 없다. 전체 백업과 연결된 증분 체인을 합성하는 pg_combinebackup이 필요하다.

# 전체 + 증분1 + 증분2를 합성하여 복원 가능한 디렉토리 생성
pg_combinebackup \
  /backups/full_20260414 \
  /backups/incr_20260415 \
  /backups/incr_20260416 \
  -o /restore/combined_datadir

# 합성된 디렉토리로 PostgreSQL 시작
pg_ctl -D /restore/combined_datadir start

pg_combinebackup에 백업을 나열할 때는 반드시 시간 순서대로 전달해야 한다. 순서가 틀리면 오류가 발생한다. 증분 체인의 첫 번째는 반드시 Full Backup이어야 한다.

현재 PostgreSQL 17+의 네이티브 증분 백업은 pgBackRest나 Barman에 비해 기능이 제한적일 수 있다. 기능 요건을 공식 문서와 비교한 후 도입 여부를 결정한다.


7. 관련 파라미터 튜닝 가이드

# WAL 아카이빙 & 물리 백업 관련 핵심 파라미터

# WAL 레벨 (replica 이상 필수)
wal_level = replica

# 아카이빙
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout = 300          # 5분마다 미완성 WAL 강제 아카이빙

# WAL 크기 제어
max_wal_size = 4GB             # 체크포인트 간 최대 WAL 크기
min_wal_size = 1GB             # 재활용을 위한 최소 WAL 크기 확보
wal_compression = on           # WAL 압축 (CPU 오버헤드 vs 스토리지 절감)
wal_buffers = -1               # 자동 설정 (shared_buffers의 1/32, 최대 16MB)

# 체크포인트
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9

# 복제 연결 (pg_basebackup용)
max_wal_senders = 5
wal_keep_size = 1GB            # 스탠바이가 WAL을 따라잡을 여유 확보

# 증분 백업 (PostgreSQL 17+)
summarize_wal = on

8. WAL 아카이빙 모니터링

WAL 아카이빙 실패는 조용히 진행된다. pg_wal/ 디렉토리가 쌓이다가 디스크 풀 장애로 이어질 수 있다. 반드시 모니터링을 설정해야 한다.

-- 아카이빙 상태 전체 확인
SELECT
  archived_count,
  last_archived_wal,
  last_archived_time,
  failed_count,
  last_failed_wal,
  last_failed_time,
  now() - last_archived_time AS lag
FROM pg_stat_archiver;
컬럼의미
failed_count누적 아카이빙 실패 수. 0이 아니면 즉시 조사 필요
last_failed_time마지막 실패 시각
lag마지막 성공 아카이빙으로부터 경과 시간
# pg_wal 디렉토리 크기 모니터링 (비정상적으로 커지면 경보 필요)
du -sh $(psql -U postgres -tAc "SHOW data_directory")/pg_wal

아카이빙 실패 알림 스크립트 예시다.

#!/bin/bash
# WAL 아카이빙 상태 점검 크론 (5분마다 실행 권장)

FAILED=$(psql -U postgres -tAc \
  "SELECT failed_count FROM pg_stat_archiver")

if [ "$FAILED" -gt 0 ]; then
  LAST_FAIL=$(psql -U postgres -tAc \
    "SELECT last_failed_wal || ' at ' || last_failed_time FROM pg_stat_archiver")
  echo "WAL 아카이빙 실패 감지: ${LAST_FAIL}" | \
    mail -s "[ALERT] PostgreSQL WAL 아카이빙 실패" ops@example.com
fi

9. 물리적 백업 자동화 스크립트

#!/bin/bash
# /usr/local/bin/pg_physical_backup.sh

set -euo pipefail

DB_HOST="localhost"
DB_USER="backup_user"
BACKUP_ROOT="/backups/basebackup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_ROOT}/${TIMESTAMP}"
RETENTION_DAYS=7
LOG_FILE="/var/log/pg_physical_backup.log"

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

log "물리적 백업 시작"

mkdir -p "${BACKUP_DIR}"
pg_basebackup \
  -h "${DB_HOST}" \
  -U "${DB_USER}" \
  -D "${BACKUP_DIR}" \
  -Ft \
  -z \
  -Xs \
  --checkpoint=fast \
  -P \
  2>>"${LOG_FILE}"

# 무결성 검증 (PostgreSQL 13+)
if pg_verifybackup "${BACKUP_DIR}" 2>>"${LOG_FILE}"; then
  BACKUP_SIZE=$(du -sh "${BACKUP_DIR}" | cut -f1)
  log "백업 완료 및 검증 성공: ${BACKUP_DIR} (${BACKUP_SIZE})"
else
  log "ERROR: 백업 무결성 검증 실패! ${BACKUP_DIR}"
  # 검증 실패한 백업 디렉토리 삭제 전 경로 확인
  # BACKUP_DIR이 BACKUP_ROOT 하위인지 검증 후 삭제
  if [[ "${BACKUP_DIR}" == "${BACKUP_ROOT}/"* ]]; then
    rm -rf "${BACKUP_DIR}"
  fi
  exit 1
fi

# 보관 기간 초과 백업 정리
# find로 대상 목록을 확인하고 삭제 전 로그 기록
find "${BACKUP_ROOT}" -maxdepth 1 -mindepth 1 -type d \
  -mtime +${RETENTION_DAYS} -print | while read -r OLD_DIR; do
  log "만료 백업 삭제: ${OLD_DIR}"
  rm -rf "${OLD_DIR}"
done
# crontab -e
# 매일 새벽 1시 물리적 백업 실행
0 1 * * * /usr/local/bin/pg_physical_backup.sh

10. 논리 vs 물리 — 언제 무엇을 쓸까

기준논리적 백업 (pg_dump)물리적 백업 (pg_basebackup)
백업 속도느림 (대형 DB)빠름
복구 속도느림빠름
PITR 지원미지원지원 (WAL 아카이빙 병행 시)
선택적 복구지원 (테이블·스키마 단위)미지원 (클러스터 전체)
버전 간 이식성지원 (낮은 → 높은 버전)미지원 (동일 메이저 버전)
스탠바이 초기화미지원지원
증분 백업미지원지원 (PostgreSQL 17+)
권장 활용마이그레이션, 선택 복구, 개발 환경재해 복구, PITR, HA 구성

실전에서 권장되는 조합이다.

# 매일: 물리적 백업 + 연속 WAL 아카이빙 → PITR 기반
pg_basebackup + archive_command

# 매주: 논리적 백업 → 이식성·선택 복구 기반
pg_dump

11. 마치며 — 백업 체인을 완성하는 WAL

pg_basebackup은 특정 시점의 완전한 스냅샷을 만든다. 하지만 그것만으로는 어제 찍힌 사진에 불과하다. WAL 아카이빙이 더해질 때, 그 스냅샷과 현재 사이의 모든 순간이 복구 가능해진다.

"물리 백업은 기반을 세우고, WAL 아카이빙은 그 위에 시간을 쌓는다."

PostgreSQL 17에서 도입된 네이티브 증분 백업(--incremental)은 대형 DB 환경에서 백업 비용을 낮추는 중요한 진전이다. 아직 서드파티 도구에 비해 기능이 제한적일 수 있으므로, 공식 문서와 팀 요건을 비교해 도입 여부를 결정한다.

다음 파트에서는 물리 백업과 WAL 아카이빙을 활용해 PITR을 실제로 구현하는 방법을 단계별로 다룬다.


참고 자료

이 글 공유하기

시리즈 내비게이션

PostgreSQL 백업/복구 완전 정복

3 / 6 · 4

같은 주제 더 보기·대표 시리즈로 시작

English

최신 글을 RSS로 받아보세요

뉴스레터 오픈 전에는 RSS로 먼저 업데이트를 받아보실 수 있습니다.

RSS 구독 안내 보기