/엔지니어/데이터베이스/PostgreSQL 복제 · WAL · PITR —
데이터베이스고급UbuntuDebianCentOSRHELPostgreSQLStreaming ReplicationWAL

PostgreSQL 복제 · WAL · PITR — 프로덕션 HA 구성 완전 가이드

Streaming Replication으로 고가용성 구성, WAL 아카이빙으로 특정 시점 복구(PITR)를 구현하는 PostgreSQL 운영 심화 가이드.

PostgreSQL 고가용성 아키텍처

프로덕션 PostgreSQL은 Primary + Standby Streaming Replication 구성이 기본입니다. 여기에 WAL 아카이빙을 추가하면 분 단위 PITR(Point-in-Time Recovery)이 가능해집니다.

CODE
[Primary]  ──── WAL Stream ────►  [Standby (Hot Standby)]
    │
    └── WAL Archive ──► [S3 / NFS]  ◄── PITR 복구 시 사용

1. Primary 서버 설정

postgresql.conf 핵심 파라미터

INI
# /etc/postgresql/16/main/postgresql.conf

# 복제 설정
wal_level = replica              # replica 또는 logical
max_wal_senders = 5              # 최대 Standby 연결 수
wal_keep_size = 1GB              # WAL 보관 크기
hot_standby = on

# WAL 아카이빙 (PITR용)
archive_mode = on
archive_command = 'aws s3 cp %p s3://my-wal-bucket/archive/%f'
archive_timeout = 60             # 최대 60초마다 강제 아카이빙

# 성능 튜닝
wal_compression = zstd
checkpoint_completion_target = 0.9
max_wal_size = 4GB
min_wal_size = 1GB

# 모니터링
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all

pg_hba.conf — 복제 연결 허용

Config
# /etc/postgresql/16/main/pg_hba.conf
# TYPE  DATABASE    USER        ADDRESS         METHOD
host    replication replicator  10.0.1.0/24     scram-sha-256
Bash
# 복제 전용 사용자 생성
sudo -u postgres psql -c "
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong-password';
"

sudo systemctl reload postgresql

2. Standby 서버 초기 구성

Bash
# Standby에서: Primary의 베이스 백업으로 데이터 디렉터리 초기화
sudo -u postgres pg_basebackup \
  -h 10.0.1.10 \            # Primary IP
  -U replicator \
  -D /var/lib/postgresql/16/main \
  -Fp -Xs -P -R              # -R: recovery 설정 자동 생성

# -Fp: plain format
# -Xs: WAL streaming 포함
# -P: 진행률 표시
# -R: standby.signal + postgresql.auto.conf 자동 생성
INI
# postgresql.auto.conf (pg_basebackup -R 이 자동 생성)
primary_conninfo = 'host=10.0.1.10 port=5432 user=replicator password=strong-password application_name=standby1'
Bash
# Standby 시작
sudo systemctl start postgresql

# 복제 상태 확인 (Primary에서)
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"

3. 복제 지연 모니터링

SQL
-- Primary에서: 각 Standby의 WAL 지연 확인
SELECT
  application_name,
  state,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn,
  pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS replication_lag,
  write_lag,
  flush_lag,
  replay_lag
FROM pg_stat_replication;

-- Standby에서: 수신 중인 WAL 상태
SELECT
  status,
  receive_start_lsn,
  received_tli,
  pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())) AS replay_lag
FROM pg_stat_wal_receiver;

알림 기준: replay_lag이 30초를 초과하면 알림을 발생시키세요. 지속적인 지연은 Standby의 I/O 병목 또는 네트워크 문제를 의미합니다.


4. Failover — Standby를 Primary로 승격

Bash
# 방법 1: pg_ctl promote (즉시 승격)
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main

# 방법 2: 트리거 파일 (권장 — 스크립트 자동화 용이)
sudo -u postgres touch /tmp/failover.trigger
# postgresql.conf에 설정: promote_trigger_file = '/tmp/failover.trigger'

# 승격 확인
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
# 결과: f (false) → Primary로 전환됨

구 Primary를 새 Standby로 재편입 (pg_rewind)

Bash
# 구 Primary(장애 복구 후)에서
sudo systemctl stop postgresql

sudo -u postgres pg_rewind \
  --target-pgdata=/var/lib/postgresql/16/main \
  --source-server="host=10.0.1.20 user=replicator dbname=postgres"

# standby.signal 생성
sudo -u postgres touch /var/lib/postgresql/16/main/standby.signal

# primary_conninfo를 새 Primary(10.0.1.20)로 업데이트
sudo -u postgres psql -c "
ALTER SYSTEM SET primary_conninfo = 'host=10.0.1.20 port=5432 user=replicator password=strong-password';
"

sudo systemctl start postgresql

5. PITR — 특정 시점 복구

WAL 아카이빙이 활성화된 경우, 아카이브된 WAL을 재생해 임의의 시점으로 복구할 수 있습니다.

Bash
# 복구 서버에서: 최신 베이스 백업 다운로드
aws s3 cp s3://my-wal-bucket/basebackup/latest.tar.gz /tmp/
sudo -u postgres tar -xzf /tmp/latest.tar.gz -C /var/lib/postgresql/16/main

# recovery.conf 역할: postgresql.conf에 작성 (PG12 이후)
INI
# postgresql.conf (복구 서버)

restore_command = 'aws s3 cp s3://my-wal-bucket/archive/%f %p'

# 목표 시점 지정 (실수로 DROP TABLE한 직전 시각)
recovery_target_time = '2026-05-19 14:30:00 KST'
recovery_target_action = 'promote'   # 복구 완료 후 자동 승격
Bash
# standby.signal 생성 (복구 모드로 시작)
sudo -u postgres touch /var/lib/postgresql/16/main/standby.signal

sudo systemctl start postgresql

# 복구 진행 로그 실시간 확인
sudo journalctl -u postgresql -f | grep -E "recovery|redo|consistent"

# 복구 완료 확인
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"

6. 자동 베이스 백업 스크립트

Bash
#!/bin/bash
# /usr/local/bin/pg-basebackup.sh

S3_BUCKET="s3://my-wal-bucket/basebackup"
DATE=$(date +%Y%m%d-%H%M)
BACKUP_DIR="/tmp/pg-backup-$DATE"

# 베이스 백업 생성
sudo -u postgres pg_basebackup \
  -D "$BACKUP_DIR" \
  -Ft -z \        # tar + gzip
  -Xs \           # WAL streaming 포함
  -P

# S3 업로드
aws s3 cp "$BACKUP_DIR/base.tar.gz" "$S3_BUCKET/$DATE/base.tar.gz"
aws s3 cp "$BACKUP_DIR/pg_wal.tar.gz" "$S3_BUCKET/$DATE/pg_wal.tar.gz"

# 로컬 정리
rm -rf "$BACKUP_DIR"

# 30일 이상 된 S3 백업 삭제
aws s3 ls "$S3_BUCKET/" | awk '{print $4}' | while read dir; do
  dir_date=$(echo "$dir" | cut -d'-' -f1)
  if [ "$(date -d "$dir_date" +%s 2>/dev/null)" -lt "$(date -d '30 days ago' +%s)" ]; then
    aws s3 rm "$S3_BUCKET/$dir" --recursive
  fi
done

echo "Backup completed: $DATE"
Bash
# 매일 새벽 1시 실행
echo "0 1 * * * root /usr/local/bin/pg-basebackup.sh >> /var/log/pg-backup.log 2>&1" > /etc/cron.d/pg-backup
#PostgreSQL#Streaming Replication#WAL#PITR#HA#고가용성#DBA
편집 안내 · Editorial Note

이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.

관련 공식 문서PostgreSQL 공식 문서

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.