백업이 아니라 복구가 시험이다

2편 마지막에 이렇게 적었다 — “백업의 가치는 백업이 아니라 복구에서 결정된다.” 이 글은 그 한 줄을 직접 돌려보는 워크북이다. Barman의 PITR target 옵션 4가지를 한 lab에서 한 번씩 시도해 본다.

처음 보는 독자도 따라올 수 있게 환경을 짧게 정리한다.

호스트역할Barman 라벨
demo-pg01PostgreSQL 17 primary
demo-barman01Barman 3.18 서버pg01

pg01은 Barman 서버 라벨, demo-pg01은 실제 호스트네임 — 두 이름은 별개다. 환경 셋업이 처음이라면 2편을 먼저 보고 오는 게 빠르다.

참고로 barman recoverbarman restore는 같은 명령이다. Barman 3.x에서 restore가 새 권장 이름이지만 recover도 그대로 동작한다. 이 글은 2편과 톤을 맞춰 recover로 통일했다.


시나리오 준비 — 지웠다 살리는 데이터

PITR을 그럴듯하게 돌려 보려면 의도적으로 손상된 시점이 필요하다. 다음 SQL을 demo-pg01에서 미리 실행해 둔다.

-- T0: 기준 데이터
sudo -u postgres psql <<'SQL'
CREATE TABLE notes (
  id   int PRIMARY KEY,
  body text,
  ts   timestamptz default now()
);
INSERT INTO notes(id, body)
  SELECT g, 'note-' || g FROM generate_series(1, 1000) g;
SQL
-- T1 (예: 14:30:00): 안전 시점 — 이후로 되감을 라벨 박기 + XID 기록
sudo -u postgres psql <<'SQL'
BEGIN;
SELECT pg_create_restore_point('safe-state');     -- 시나리오 C 용
SELECT pg_current_xact_id();                       -- 시나리오 B 용 — 예: 12345
COMMIT;
SQL
-- T2 (예: 14:32:00 이후): 사고 — 테이블 삭제
sudo -u postgres psql -c "DROP TABLE notes;"

이제 notes 테이블이 사라졌다. base backup이 T0 이전에 떠 있고 WAL이 계속 수집되고 있다고 가정한다 (2편의 lab 그대로). 이 base backup + WAL을 가지고 T1 직후 / XID 12345 직후 / 명시 라벨 / base backup 직후 4가지 시점으로 되감아 본다.

복원 대상은 빈 디렉토리 — 매 시나리오 사이에 비워서 시작한다.

sudo -u barman rm -rf /var/lib/barman/restore && \
  sudo -u barman mkdir -p /var/lib/barman/restore

시나리오 A — --target-time: 시계로 되감기

가장 직관적이다. 사고 직전 시각으로 돌린다.

sudo -u barman barman recover pg01 latest /var/lib/barman/restore \
  --target-time "2026-05-06 14:32:00"

기대 동작 — 14:32:00 시점까지 WAL을 replay한 뒤 멈춘다. notes 테이블은 살아 있고, DROP TABLE은 reach되지 않는다.

타임존은 Barman 서버의 시스템 시간대가 기본이다. 다른 TZ로 명시하려면 2026-05-06 14:32:00+09 형태로 붙인다 — 운영에서는 항상 명시하는 편이 안전하다.

Recovery targets must be a value after the end of the backup.” — base backup 시작 시점 이전은 reach 불가능. 시점이 base backup 시작보다 이르면 즉시 실패한다.


시나리오 B — --target-xid: 트랜잭션 ID로 되감기

시계는 누적된 운영 환경에서 의외로 부정확하다. Barman 호스트와 PostgreSQL 호스트의 시계가 살짝 어긋나 있거나, 동시에 여러 트랜잭션이 들어왔거나. 트랜잭션 ID(XID)가 가장 정확한 좌표다.

sudo -u barman barman recover pg01 latest /var/lib/barman/restore \
  --target-xid 12345

기대 동작 — XID 12345가 commit된 직후까지 replay하고 멈춘다. T1 시점에 기록해 둔 XID가 사고 직전 마지막 안전 트랜잭션이라 이걸로 멈추면 notes는 살아 있다.

항목내용
강점시점이 논리적으로 정확 — 시계 오차·동시 트랜잭션 영향 없음
약점사고 직후가 되어서야 그 XID를 안다 → 사고 직전에 미리 기록해 두는 게 이상적
보조pg_waldump으로 WAL을 훑어 commit 레코드의 XID 시퀀스 추적 가능

--exclusive 플래그를 같이 주면 그 XID 직전까지만 replay (그 트랜잭션 자체는 제외). 기본값은 그 XID 포함.


시나리오 C — --target-name: 명시 라벨로 되감기

운영 중 위험한 작업 직전에 라벨을 박아 두는 패턴.

-- 사고 직전(T1)에 미리 박아 둔 라벨
SELECT pg_create_restore_point('safe-state');
sudo -u barman barman recover pg01 latest /var/lib/barman/restore \
  --target-name 'safe-state'

기대 동작 — safe-state restore point까지 replay하고 멈춘다.

이 옵션의 가치는 언어가 자연스럽다는 점이다. 시각·XID는 사고 후 재구성해야 하지만 라벨은 팀이 기억할 수 있는 이름이다 — before-migration-2026q2, pre-DROP-INDEX-experiment 같은. 운영 가이드를 “위험한 ALTER 직전엔 restore point부터 박는다“로 정착시키면 PITR이 한층 평이해진다.


시나리오 D — --target-immediate: base backup 직후

가장 단순한 옵션 — 마지막 base backup이 일관성을 확보하는 그 시점까지만 replay하고 멈춘다. 즉 base backup 종료 직후의 클러스터 상태로 일어선다.

sudo -u barman barman recover pg01 latest /var/lib/barman/restore \
  --target-immediate

언제 쓰는가 — “가장 최근 base backup의 그 순간으로 일단 돌려놓고, 이후 사고 영향을 분리해 분석하고 싶다” 같은 forensic 시나리오. WAL replay를 최소화해 빠르게 일관성 상태에 도달한다.


그 외 옵션 빠른 참조

옵션의미비고
--target-lsnLSN(3/64000000 형식)으로 되감기XID보다 더 세밀, 특정 WAL 위치를 정확히 알 때
--target-tli특정 timeline으로 복구latest / current / 숫자 ID
--target-actionreach 후 동작 — shutdown / pause / promote미지정 시 PostgreSQL이 paused 후 운영자 결정 대기
--exclusivetarget 직전까지(target 자체 제외) replay기본은 target 포함
--standby-modereplica로 일어나도록 standby.signal 생성target과 무관한 옵션

--target-action이 특히 중요하다. 지정하지 않으면 paused — 운영자가 SELECT pg_wal_replay_resume()을 직접 호출할 때까지 새 트랜잭션이 돌지 않는다. 의도하지 않으면 “왜 안 살아나지” 하고 한참 헤맨다.

backup_id=auto(가장 최근 backup 자동 선택)일 때는 제약이 있다 — --target-time, --target-lsn, --target-tli만 허용된다. --target-xid--target-name을 쓰려면 명시적인 backup-id를 지정해야 한다 (barman list-backup pg01로 확인).


시점을 정하는 법 — barman show-backup + pg_waldump

PITR의 절반은 어디로 되감을지 정하는 일이다. 두 명령어가 그 좌표를 준다.

sudo -u barman barman show-backup pg01 latest

출력에서 다음 항목을 본다.

항목의미
Begin time / End timebase backup 경계 — 그 이전 시점은 reach 불가
Begin LSN / End LSNbase backup의 LSN 경계 — --target-lsn 사용 시 기준점
Begin Offset / End OffsetWAL 파일 내 위치

WAL을 더 세밀하게 보려면 pg_waldump로 commit 레코드를 훑는다.

sudo -u postgres /usr/pgsql-17/bin/pg_waldump \
  /var/lib/barman/pg01/streaming/000000010000000000000004 \
  | grep COMMIT | head

각 줄에 LSN·XID·timestamp가 함께 나오므로, 사고 직전의 어떤 좌표를 사용할지 골라잡기 쉬워진다.


자주 만나는 함정 5가지

증상원인해결
Recovery targets must be a value after the end of the backuptarget이 base backup 시작 이전barman show-backupBegin time 이후로 잡거나 더 오래된 backup 사용
복구 후 PostgreSQL이 안 살아남--target-action 미지정 → paused 상태SELECT pg_wal_replay_resume(); 또는 처음부터 --target-action promote
timeline mismatch이전 복구 후 새 timeline으로 진입--target-tli latest 또는 명시적 timeline ID
target-name 못 찾음restore point가 현재 timeline의 WAL에 없음pg_create_restore_point해당 timeline에 박혔는지 확인
barman recover가 아무 진행 안 함--remote-ssh-command로 cross-host 복원인데 SSH 키 미설치barman 사용자에서 postgres@<host>로 키 기반 접속 미리 잡기

정리

항목내용
4가지 target--target-time (시계) / --target-xid (XID) / --target-name (라벨) / --target-immediate (base 직후)
가장 정확XID — 시계 오차·동시 트랜잭션 무관
가장 운영 친화--target-name — 위험 작업 직전 pg_create_restore_point 박는 패턴
시점 결정barman show-backup + pg_waldump 조합
필수 동반--target-action(기본 paused), --exclusive(stop-before vs include)
backup_id=auto 제약--target-time, --target-lsn, --target-tli만 허용 — 그 외는 명시 backup-id 필요

PITR은 두 단계다 — 시점을 정하는 일이 반이고, 복구하는 일이 반이다. 보통 시점을 정하는 일이 더 어렵다.


참고 자료