백업·복구(pg_dump, PITR)
백업 없는 데이터베이스는 시한폭탄이다
장애는 언제든 발생한다. 잘못된 DELETE, DROP TABLE, 하드웨어 고장, 랜섬웨어 감염 등 원인은 다양하다. 백업이 없거나 복구 절차를 한 번도 검증하지 않은 상태에서 장애를 맞으면 데이터를 되살릴 방법이 없다. PostgreSQL은 두 가지 백업 방식을 제공한다. 논리 백업은 SQL 덤프를 생성하고, 물리 백업은 파일 시스템 수준의 스냅샷을 저장한다. 두 방식의 차이를 이해하고 상황에 맞게 선택해야 한다.
논리 백업: pg_dump / pg_dumpall
pg_dump는 지정한 데이터베이스를 SQL 스크립트 또는 아카이브 파일로 내보낸다. 운영 중인 서버에서 실행 가능하며, 별도의 서버 설정 없이 바로 사용할 수 있다.
출력 포맷
| 플래그 | 포맷 | 특징 |
|---|---|---|
| 없음 (기본) | plain SQL | 사람이 읽을 수 있음, psql로 복원 |
-Fc | custom | 압축, 선택적 복원, pg_restore 사용 |
-Fd | directory | 멀티 파일, 병렬 덤프/복원 지원 |
-Ft | tar | tar 아카이브 |
운영 환경에서는 custom 포맷(-Fc) 또는 directory 포맷(-Fd) 을 사용한다. 압축이 내장되어 있고, pg_restore -j로 병렬 복원이 가능해 대용량 데이터베이스에서 복원 시간을 크게 줄일 수 있다.
자주 쓰는 pg_dump 명령
# custom 포맷으로 단일 DB 백업
pg_dump -U postgres -Fc -f mydb.dump mydb
# 특정 테이블만 백업
pg_dump -U postgres -Fc -t orders -t order_items -f orders.dump mydb
# 데이터 없이 스키마만 백업
pg_dump -U postgres -Fc --schema-only -f schema.dump mydb
# 병렬 4 job으로 복원 (directory 포맷)
pg_restore -U postgres -d mydb -j 4 mydb_dir/pg_dumpall
pg_dumpall은 클러스터 전체(역할, 테이블스페이스, 모든 데이터베이스)를 SQL 스크립트로 내보낸다. 역할(Role) 이나 전역 설정까지 포함해야 할 때 사용한다.
pg_dumpall -U postgres -f full_cluster.sql
psql -U postgres -f full_cluster.sql # 복원논리 백업의 한계
논리 백업은 특정 시점의 스냅샷만 저장한다. 덤프 직후 변경된 데이터는 포함되지 않는다. 대용량 데이터베이스는 덤프·복원 모두 시간이 오래 걸리고, Point-in-Time Recovery(PITR) 를 지원하지 않는다.
물리 백업: pg_basebackup
pg_basebackup은 운영 중인 PostgreSQL 서버에서 파일 시스템 수준의 전체 복사본(베이스 백업) 을 만든다. WAL 아카이빙 및 PITR의 시작점이 된다.
# 단순 베이스 백업 (tar, gzip 압축)
pg_basebackup -h localhost -U replicator \
-D /backup/base \
-Ft -z -P
# 복제 설정 자동 포함 (-R)
pg_basebackup -h localhost -U replicator \
-D /var/lib/postgresql/17/standby \
-Fp -Xs -P -R| 옵션 | 의미 |
|---|---|
-Fp | plain (파일 그대로) |
-Ft | tar 아카이브 |
-Xs | WAL을 스트리밍으로 포함 |
-z | gzip 압축 |
-R | standby.signal + primary_conninfo 자동 생성 |
-P | 진행률 표시 |
WAL 아카이빙 설정
PITR을 사용하려면 두 가지가 필요하다: 베이스 백업과 WAL 아카이브. WAL 아카이빙은 WAL 세그먼트가 완성될 때마다 지정한 위치로 복사하는 작업이다.
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/%f' # %p = 소스 경로, %f = 파일명archive_command는 성공 시 exit code 0을 반환해야 한다. 실패하면 PostgreSQL이 해당 세그먼트를 계속 재시도하며, 아카이브가 따라잡지 못하면 pg_wal/이 증가한다.
운영 환경에서는 단순 cp 대신 S3, NFS, pgBackRest 같은 신뢰성 있는 대상을 사용한다:
# AWS S3 예시 (aws cli 필요)
archive_command = 'aws s3 cp %p s3://my-wal-bucket/wal/%f'PITR 복구 절차
PITR은 베이스 백업에 WAL을 재생(replay)하여 특정 시점으로 복구하는 기법이다.
복구 절차 단계별
1단계: 베이스 백업을 새 디렉터리에 복원
# 기존 데이터 디렉터리 백업 이동
mv /var/lib/postgresql/17/main /var/lib/postgresql/17/main.bak
# 베이스 백업 복원
tar -xzf /backup/base.tar.gz -C /var/lib/postgresql/17/main2단계: 복구 파라미터 설정
PostgreSQL 12 이후에는 recovery.conf가 없다. postgresql.conf에 직접 복구 파라미터를 추가한다.
# postgresql.conf
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2024-01-03 14:00:00'
recovery_target_action = 'promote' # 완료 후 읽기/쓰기 모드로 전환3단계: recovery signal 파일 생성
touch /var/lib/postgresql/17/main/recovery.signalrecovery.signal 파일이 존재하면 PostgreSQL은 복구 모드로 시작한다.
4단계: PostgreSQL 시작 및 완료 확인
pg_ctlcluster 17 main start
# 로그에서 복구 완료 메시지 확인
tail -f /var/log/postgresql/postgresql-17-main.log
# LOG: database system is ready to accept connections복구 목표 옵션
recovery_target_time 외에도 다양한 방식으로 복구 목표를 지정할 수 있다.
| 파라미터 | 의미 | 예시 |
|---|---|---|
recovery_target_time | 특정 시각까지 재생 | '2024-01-03 14:00:00+09' |
recovery_target_lsn | 특정 LSN까지 재생 | '0/5000000' |
recovery_target_xid | 특정 트랜잭션 ID까지 재생 | '12345' |
recovery_target_name | pg_create_restore_point()로 만든 명명 복원점 | 'before_migration' |
recovery_target | 'immediate' = 베이스 백업 직후 일관된 상태 | 'immediate' |
-- 운영 중 복원 지점 생성 (마이그레이션 전 스냅샷 역할)
SELECT pg_create_restore_point('before_v2_migration');recovery_target_inclusive는 목표 지점 직전까지만 재생할지(false) 목표 지점 트랜잭션까지 포함할지(true, 기본값)를 결정한다.
pgBackRest: 프로덕션 백업 도구
단순 cp나 pg_basebackup 직접 사용은 소규모에서는 충분하지만, 대용량 운영 환경에서는 한계가 있다. pgBackRest는 PostgreSQL 전용 오픈소스 백업 도구로 다음 기능을 제공한다.
- 증분·차등 백업: 변경된 블록만 백업해 저장 공간 절약
- 병렬 압축: 멀티 코어를 활용해 pg_dump보다 10배 이상 빠른 백업
- WAL 아카이빙 통합:
archive_command로 바로 연결 - 원격 저장소: S3, Azure Blob, GCS 지원
- 보존 정책: 자동 오래된 백업 삭제
# pgBackRest PITR 복구 예시 (특정 시각으로 복원)
pgbackrest --stanza=mydb \
--type=time \
--target="2024-01-03 14:00:00+09" \
--target-action=promote \
restore백업 방식 비교
| 항목 | pg_dump | pg_basebackup + WAL | pgBackRest |
|---|---|---|---|
| 백업 범위 | 단일 DB (논리) | 클러스터 전체 (물리) | 클러스터 전체 (물리) |
| PITR 지원 | ✗ | ✓ | ✓ |
| 증분 백업 | ✗ | ✗ | ✓ |
| 복원 속도 | 느림 (대형 DB) | 빠름 | 매우 빠름 (병렬) |
| 설정 복잡도 | 낮음 | 중간 | 높음 |
| 적합한 규모 | 소규모 / 개발 | 중소규모 | 프로덕션 |
DBA 체크리스트
- 백업 직후 반드시 복원 테스트를 수행한다. 복원이 성공해야 백업이다.
archive_command실패는 로그에서 확인하고 즉시 해결한다. 아카이브가 끊기면 PITR 범위에 구멍이 생긴다.recovery_target_time에는 타임존을 명시한다('2024-01-03 14:00:00+09'). 서버 타임존과 혼동하면 엉뚱한 시점으로 복구된다.- 논리 백업(
pg_dump)은 매일 1회 이상, WAL 아카이빙은 연속적으로 수행한다. - pgBackRest를 사용할 경우 보존 정책(
repo1-retention-full,repo1-retention-archive)을 명시해 디스크가 가득 차지 않도록 한다. recovery.signal파일을 삭제하지 않으면 PostgreSQL이 항상 복구 모드로 시작한다. promote 완료 후 파일이 자동 삭제되는지 확인한다.
References
- https://www.postgresql.org/docs/current/backup-dump.html
- https://www.postgresql.org/docs/current/app-pgdump.html
- https://www.postgresql.org/docs/current/app-pgbasebackup.html
- https://www.postgresql.org/docs/current/continuous-archiving.html
- https://www.postgresql.org/docs/current/recovery-config.html
- https://pgbackrest.org/user-guide.html
- https://dzone.com/articles/point-in-time-recovery-pitr-in-postgresql
- https://stormatics.tech/blogs/postgresql-backup-best-practices
- https://dev.to/mohhddhassan/postgresql-backups-and-point-in-time-recovery-with-pgbackrest-13gp