본문으로 건너뛰기
11.4 WAL 아카이브와 PITR

11.4 WAL 아카이브와 PITR

Point-In-Time Recovery(PITR)은 base 백업 + 그 이후의 WAL을 외부 저장소로 보내 두었다가, 사고 시 원하는 시점까지 재생해 복원하는 기법입니다. 분 단위 RPO·실수 SQL의 원복·테스트 환경 재현 등에 표준입니다. archive 설정·기본 복구·복구 시점 지정을 정리합니다.

큰 그림

    flowchart LR
  PG["PostgreSQL primary"]
  WAL["pg_wal/<br/>16MB 세그먼트"]
  ARCH["archive_command<br/>외부 저장소로 복사"]
  STORE["S3 / NFS / etc."]
  BASE["pg_basebackup<br/>(주기적 base)"]

  PG --> WAL --> ARCH --> STORE
  PG --> BASE --> STORE

  classDef pg fill:#ede9fe,stroke:#6d28d9,color:#3b0764,stroke-width:2px
  classDef step fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
  classDef store fill:#d1fae5,stroke:#047857,color:#064e3b
  class PG pg
  class WAL,ARCH,BASE step
  class STORE store
  

복구는 역순:

    flowchart LR
  STORE["저장소"]
  BASE_R["base 복구"]
  WAL_R["WAL 재생"]
  TARGET["recovery_target_time<br/>또는 LSN·xid"]
  NEW["복구된 클러스터"]

  STORE --> BASE_R --> WAL_R
  WAL_R -. 까지 .-> TARGET
  TARGET --> NEW

  classDef step fill:#fed7aa,stroke:#c2410c,color:#7c2d12
  classDef done fill:#d1fae5,stroke:#047857,color:#064e3b
  class BASE_R,WAL_R,TARGET step
  class NEW done
  

archive 설정

postgresql.conf:

wal_level = replica           # 또는 logical
archive_mode = on
archive_command = '/usr/bin/test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout = 60s         # 빈 클러스터도 1분마다 강제 archive
변수의미
%fWAL 세그먼트 파일명
%p절대 경로 (PGDATA/pg_wal/…)
%r마지막 재시작 포인트

archive_command종료 코드 0만 성공으로 인정. 실패하면 PostgreSQL이 같은 세그먼트를 반복 재시도 — 누락 없습니다. 단, 계속 실패하면 pg_wal/이 가득 찹니다.

실전 archive 명령

# 로컬 디렉토리
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'

# rsync to remote
archive_command = 'rsync -aq %p backup_user@backup_host:/archive/%f'

# pgBackRest 표준
archive_command = 'pgbackrest --stanza=main archive-push %p'

# AWS S3 (예: barman-cloud-wal-archive)
archive_command = 'barman-cloud-wal-archive -P aws s3://my-bucket/wal main %p'

restore_command

복구 시 PostgreSQL이 archive에서 WAL을 가져오는 명령합니다. postgresql.conf(PG 12+) 또는 recovery.conf(PG 11까지)에 정의합니다.

restore_command = 'cp /archive/%f %p'
# 또는
restore_command = 'pgbackrest --stanza=main archive-get %f %p'

복구 모드 트리거 (PG 12+):

# 빈 PGDATA에 base 풀어 둠
tar xf base.tar -C /var/lib/pgsql/17/data

# recovery 트리거 파일
touch /var/lib/pgsql/17/data/recovery.signal
# (standby로 띄우려면 standby.signal)

# postgresql.conf에 restore_command 설정 후
pg_ctl start -D /var/lib/pgsql/17/data

복구 시점 지정

postgresql.conf에 하나의 타깃을 설정:

옵션의미
recovery_target_time = '2026-05-23 10:00:00'시간
recovery_target_lsn = '0/3A1F8E0'LSN
recovery_target_xid = '12345'트랜잭션 ID
recovery_target_name = '<savepoint>'named restore point
recovery_target = 'immediate'base 백업 직후 일관성 회복 시점
recovery_target_time = '2026-05-23 09:55:00'
recovery_target_action = 'promote'   # 도달 후 자동 promote (read-write)
# 또는 'shutdown' / 'pause'

named restore point

운영 자체적으로 체크포인트를 박을 수 있습니다.

SELECT pg_create_restore_point('before-bad-deploy');

이후 복구 시:

recovery_target_name = 'before-bad-deploy'

대규모 배포·마이그레이션 전 표준 패턴입니다.

복구 절차 — 표준

  1. 새 호스트(또는 정지된 호스트)에 빈 PGDATA 준비
  2. 가장 최근의 base 백업을 복원 (tar 풀기 또는 pg_basebackup 결과 복사)
  3. postgresql.confrestore_command·recovery_target_* 설정
  4. recovery.signal 파일 생성
  5. 클러스터 기동
  6. WAL 재생 진행 (pg_stat_activitystartup process 추적)
  7. 타깃 도달 후 pause이면 운영자 검증 → SELECT pg_wal_replay_resume();
  8. promote 완료 후 새 timeline 생성합니다. read-write 클러스터로 가동

timeline

복구가 끝나 promote되면 새 timeline이 만들어집니다. WAL 파일명의 첫 8자리가 timeline ID.

000000010000000000000003A   # timeline 1
000000020000000000000003A   # timeline 2 (PITR 후)

같은 timeline 안의 WAL을 이미 다른 복구가 사용했더라도 새 timeline은 자기 흐름을 새로 만듭니다. PostgreSQL이 자동 관리하므로 운영자가 직접 timeline 다룰 일은 거의 없습니다.

pg_waldump — 디버깅

pg_waldump /archive/000000010000000000000003A | head

WAL 안의 record를 사람이 읽을 수 있게 출력합니다. 언제 어떤 변경이 들어왔는지 추적할 때.

모니터링

SELECT * FROM pg_stat_archiver;
-- archived_count, last_archived_wal, last_archived_time,
-- failed_count, last_failed_wal, last_failed_time

failed_count 또는 last_failed_time 새 값 = archive 실패. 즉시 대응합니다.

-- 복구 진행 중
SELECT pg_is_in_recovery(), pg_last_wal_replay_lsn(),
       pg_last_xact_replay_timestamp();

복구 중에는 pg_is_in_recovery() = true.

흔한 사고

사고원인
archive 실패 누적 → pg_wal 꽉 참 → 클러스터 정지archive 모니터링 부족
복구 시 WAL 누락archive 보존 정책이 너무 짧음
restore_command가 실패 종료 안 함“WAL 없음"을 PostgreSQL이 못 알아 끝없이 기다림 — 종료 코드 1로 명확히
timeline 혼동base와 WAL의 timeline 안 맞음
recovery_target 잘못 지정복구 후 새 timeline이라 다시 복구 어렵다
PITR 한 번 시작 = 새 timeline 생성. 원본 데이터로 돌리려면 다시 base + WAL부터 복구합니다. 그래서 PITR은 운영 클러스터 본체에서가 아니라 별도 staging에서 먼저 시도하는 게 표준입니다.

운영 자동화

수동 archive_command 관리는 운영 부담입니다. pgBackRest/Barman이 archive_command·restore_command·archive 보존을 통합 관리 — 다음 절(11.5, 11.6) 참고합니다.

정리

  • WAL archive = archive_command로 완료된 WAL 세그먼트를 외부 저장소로
  • restore_command + recovery.signal + recovery_target_* 로 PITR
  • 시점 지정은 time/LSN/xid/named restore point
  • PITR 끝나면 새 timeline 생성 — 원복 시 같은 출발에서 다시
  • archive 모니터링은 pg_stat_archiver — 실패는 즉시 대응
  • 운영 자동화는 pgBackRest/Barman으로 감싸기

다음 절(11.5)에서는 사실상 표준이 된 백업 도구 — pgBackRest를 봅니다.