본문으로 건너뛰기

"PITR" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

Barman PITR 워크북

· 약 7분

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

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에는 도달하지 않습니다.

타임존은 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-action도달 후 동작: 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-backuppg_waldump로 좌표를 찾습니다.

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

sudo -u barman barman show-backup pg01 latest

출력에서 다음 항목을 봅니다.

항목의미
Begin time / End timebase backup 경계이며 그 이전 시점에는 도달 불가
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은 두 단계로 나뉘어요. 시점을 정하는 일이 반이고 복구하는 일이 반인데, 보통은 시점을 정하는 쪽이 더 어려워요.

참고 자료

Barman 빠른 시작

· 약 8분

어떻게 Barman인가

1편이 "왜 Barman인가"였다면, 이 글은 "어떻게 Barman인가"예요. 1편에서 Barman의 14년 궤적과 아키텍처를 정리했어요. 이번에는 같은 자리에서 한 발짝 더 들어가 두 대의 Rocky Linux 머신을 준비하고, streaming-only 모드로 PostgreSQL을 설정한 뒤, 5개 명령어로 첫 백업과 PITR 복구까지 끝내 봅니다.

이번에 다룰 시나리오를 한 화면에 펼치면 다음과 같습니다.

단계명령어의미
1barman check pg01PG 연결, streaming, 권한 확인
2barman backup pg01첫 base backup
3barman list-backup pg01카탈로그 조회
4barman recover pg01 latest <dir>최신 백업 복원
5barman recover ... --target-time "..."임의 시점 (PITR) 복원

읽으면서 따라 할 수 있도록 모든 명령어와 설정 파일 내용을 그대로 옮겨 둡니다.

사전 준비

두 호스트로 lab 환경을 구성합니다.

호스트역할OS
demo-pg01PostgreSQL 17 primaryRocky Linux 9
demo-barman01Barman 3.18 서버Rocky Linux 9

두 호스트가 hostname으로 서로 통신한다고 가정합니다(DNS 또는 /etc/hosts). 단일 머신에서 시험하려면 demo-pg01demo-barman01을 모두 localhost로 두고 진행해도 됩니다.

설치

PGDG 저장소에서 설치합니다.

demo-pg01demo-barman01 양쪽에 PGDG 저장소를 등록합니다.

sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql

demo-pg01에서 PostgreSQL 17 설치/초기화:

sudo dnf install -y postgresql17-server postgresql17-contrib
sudo /usr/pgsql-17/bin/postgresql-17-setup initdb
sudo systemctl enable --now postgresql-17

demo-barman01에 Barman 3.18 설치:

sudo dnf install -y barman barman-cli

설치 시 barman 시스템 사용자가 자동 생성되며, 백업 카탈로그 기본 경로는 /var/lib/barman입니다. /etc/cron.d/barman도 함께 깔리므로 별도 systemd timer 설정 없이 cron이 매 분 한 번 barman cron을 돌립니다.

설정 1: PostgreSQL 측 (demo-pg01)

streaming 백업은 PostgreSQL의 replication 프로토콜 위에서 동작하므로, replication user와 replication slot이 필요합니다.

/var/lib/pgsql/17/data/postgresql.conf 핵심 항목:

listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10

/var/lib/pgsql/17/data/pg_hba.conf에 Barman 측 접속을 열어 둡니다.

# TYPE DATABASE USER ADDRESS METHOD
host replication barman demo-barman01 scram-sha-256
host postgres barman demo-barman01 scram-sha-256

PostgreSQL을 재시작한 후 사용자와 슬롯을 생성합니다.

sudo systemctl restart postgresql-17
sudo -u postgres psql <<'SQL'
CREATE USER barman WITH REPLICATION ENCRYPTED PASSWORD 'changeme';
GRANT pg_read_all_settings, pg_read_all_stats TO barman;
SELECT pg_create_physical_replication_slot('barman');
SQL

실제 운영에서는 changeme을 비밀 관리자(Vault, AWS Secrets Manager 등)로 옮기고 .pgpass 또는 환경변수로 분리해야 합니다.

설정 2: Barman 측 (demo-barman01)

/etc/barman.conf는 default 값 그대로 두는 편이 무난합니다. 서버별 설정만 추가합니다(default 항목 자체는 별도 글에서 다룹니다).

/etc/barman.d/pg01.conf:

[pg01]
description = "Production PG primary"
conninfo = host=demo-pg01 user=barman dbname=postgres
streaming_conninfo = host=demo-pg01 user=barman
backup_method = postgres
streaming_archiver = on
slot_name = barman
create_slot = manual
retention_policy = RECOVERY WINDOW OF 4 WEEKS

[pg01]Barman 서버 라벨이자 서버 식별자입니다. CLI(barman backup pg01), 카탈로그 디렉토리(/var/lib/barman/pg01/...), conf 파일명(pg01.conf)에 같은 값을 씁니다. 실제 호스트네임 demo-pg01과는 별개이며, Barman이 부르는 이름과 네트워크가 부르는 이름을 분리한 것입니다.

conninfo = host=demo-pg01 ...에는 Barman이 PostgreSQL에 접속할 때 사용하는 실제 호스트네임을 지정하므로 라벨과 달라도 자연스럽습니다. backup_method = postgrespg_basebackup을 통한 streaming 백업을 뜻하고, streaming_archiver = on은 WAL을 pg_receivewal 방식으로 받도록 설정합니다. slot_name = barman은 앞에서 만든 replication slot을 사용합니다. create_slot = manual로 지정한 이유는 슬롯을 SQL로 이미 만들었으므로 Barman이 자동 생성하지 않게 하기 위해서입니다.

비밀번호는 barman 사용자의 ~/.pgpass로 분리합니다.

sudo -u barman tee ~barman/.pgpass > /dev/null <<'EOF'
demo-pg01:5432:*:barman:changeme
EOF
sudo chmod 600 ~barman/.pgpass
sudo chown barman:barman ~barman/.pgpass

5개 명령어 시나리오

이제부터는 모든 명령어를 barman 사용자로 실행합니다. sudo -i -u barman으로 barman 셸에 들어가거나, 명령어마다 sudo -u barman을 앞에 붙입니다.

1. barman check pg01

셋업을 검증합니다.

sudo -u barman barman check pg01

기대 출력 (요약):

Server pg01:
PostgreSQL: OK
wal_level: OK
replication slot: OK
directories: OK
retention policy settings: OK
pg_basebackup: OK
pg_basebackup compatible: OK
systemid coherence: OK
pg_receivexlog: OK
receive-wal running: OK
archiver errors: OK

한 줄이라도 FAILED가 나오면 그 항목에 셋업 단계의 문제가 있습니다. 가장 자주 보이는 receive-wal running: FAILED는 cron이 아직 한 번도 돌지 않았거나 slot 이름이 어긋났다는 뜻입니다. 한 번 강제로 돌려 두는 편이 빠릅니다.

sudo -u barman barman cron

2. barman backup pg01

첫 base backup을 실행합니다.

sudo -u barman barman backup pg01

기대 출력 (요약):

Starting backup using postgres method for server pg01 in /var/lib/barman/pg01/base/20260504T143012
Backup start at LSN: 0/3000028
Starting backup copy via pg_basebackup for 20260504T143012
Copy done (time: 12 seconds)
Backup size: 41.5 MiB
Backup end at LSN: 0/4000060
Marking backup as DONE
Backup completed (start time: 2026-05-04 14:30:12, elapsed time: 14 seconds)

/var/lib/barman/pg01/base/<timestamp>/ 아래에 base backup이, /var/lib/barman/pg01/streaming/에 WAL이 누적됩니다.

3. barman list-backup pg01

카탈로그를 조회합니다.

sudo -u barman barman list-backup pg01

기대 출력:

pg01 20260504T143012 - F - 2026-05-04 14:30:26 - Size: 41.5 MiB - WAL Size: 16 MiB

F는 full backup입니다. backup-id는 timestamp 기반(20260504T143012)이며, latest 키워드를 별칭으로 쓸 수 있습니다.

상세 보기:

sudo -u barman barman show-backup pg01 latest

Begin time / End time / Begin LSN / End LSN 등 PITR 타깃을 결정할 때 필요한 값이 모두 여기에 있습니다.

4. barman recover

최신 백업으로 복원합니다.

복원 대상은 빈 디렉토리여야 합니다. PostgreSQL이 새로 기동할 자리를 미리 비워 둡니다.

sudo mkdir -p /var/lib/barman/restore
sudo chown barman:barman /var/lib/barman/restore

sudo -u barman barman recover pg01 latest /var/lib/barman/restore

복원이 끝나면 그 디렉토리 안에 base backup이 풀리고, recovery.signalpostgresql.auto.confrestore_command 항목이 자동 생성됩니다. PostgreSQL을 그 데이터 디렉토리로 띄우면 곧장 기동합니다.

다른 호스트로 직접 복원하려면 --remote-ssh-command "ssh postgres@<host>"를 추가합니다. 이때 barman 사용자에서 그 호스트의 postgres로 SSH 키 기반 접속이 미리 설정되어 있어야 합니다.

5. barman recover --target-time

특정 시점으로 되감는 PITR을 실행합니다.

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

복원 시점은 base backup 시작 시점 이후, 가장 마지막에 받은 WAL 이전 사이여야 합니다. 정확한 경계가 헷갈리면 barman show-backup pg01 latestBegin time / End time을 기준으로 잡습니다.

다른 PITR 타깃 옵션도 같은 자리에 지정합니다.

옵션의미
--target-time시점 기준
--target-xid트랜잭션 ID 기준
--target-namepg_create_restore_point()로 만든 명시적 라벨
--target-immediatebase backup 직후 일관성 시점

일상 운영

cron 한 줄과 보존 정책을 설정합니다.

설치 시 동봉된 /etc/cron.d/barman이 매 분 barman cron을 실행합니다(WAL 수신과 아카이브 정리 담당). 정기 백업은 별도 cron 한 줄로 잡는 방식이 표준입니다.

# /etc/cron.d/barman-backup — 매일 02:00에 모든 등록 서버 백업
0 2 * * * barman /usr/bin/barman backup all

보존 정책은 서버별 conf 한 줄로 끝납니다.

retention_policy = RECOVERY WINDOW OF 4 WEEKS
# 또는 개수 기반:
# retention_policy = REDUNDANCY 5

RECOVERY WINDOW는 "이 시점부터 N 단위(WEEKS/DAYS) 전까지 PITR이 가능하도록 보장"한다는 의미입니다. 지난 4주 동안 임의 시점으로 되감을 수 있도록 base backup과 WAL을 함께 보존합니다. 정책에서 벗어난 backup은 barman cron이 자동으로 정리합니다.

자주 만나는 에러 빠른 가이드

증상원인한 줄 해결
receive-wal running: FAILEDstreaming WAL receiver 미실행barman cron 수동 실행, slot 이름/권한 재점검
replication slot: FAILEDPG 측 슬롯이 없음 / 이름 불일치pg_create_physical_replication_slot('barman') 다시 실행
pg_basebackup: FAILEDreplication 권한/pg_hba 미흡barman 사용자의 REPLICATION 속성 확인, pg_hba에 host replication
Connection refusedlisten_addresses, 방화벽postgresql.conflisten_addresses = '*', firewalld에서 5432 허용

barman check는 한 번에 끝내려 들기보다 "FAIL 한 줄씩 잡아 나가는 도구"로 보면 마음이 편합니다. 위 4개를 해결하면 첫 셋업의 90%가 끝납니다.

정리

항목내용
백업 모델streaming-only (SSH 없이 pg_basebackup + replication slot)
카탈로그 위치/var/lib/barman/pg01/{base,streaming,wals}
일상 명령어barman cron (자동) + barman backup all (cron 1회/일)
검증 명령어barman check pg01 — 셋업 직후/이상 발생 시 첫 진단
PITRbarman recover ... --target-time "..." 한 줄
보존 정책RECOVERY WINDOW OF 4 WEEKS 권장

백업의 가치는 백업이 아니라 복구에서 결정돼요. 셋업 직후 PITR을 한 번은 반드시 돌려봐요.

참고 자료