반응형

PostgreSQL Replication 서버 세팅 가이드

PostgreSQL의 Streaming Replication을 이용해 Primary-Standby 구성을 세팅하는 방법을 정리했다. 읽기 부하 분산, 장애 대비(failover), 백업 용도로 활용할 수 있다.

 

Replication 방식 비교

방식 단위 용도
Streaming Replication 전체 DB 클러스터 (물리적) HA, failover, 읽기 분산
Logical Replication 테이블 단위 (논리적) 선택적 복제, 버전 간 마이그레이션

이 글에서는 가장 일반적인 Streaming Replication을 다룬다.

 

환경 구성

역할 호스트 IP
Primary (쓰기) pg-primary 10.0.0.1
Standby (읽기) pg-standby 10.0.0.2

PostgreSQL 16 기준이며, 15 이상이면 동일하게 적용된다.

 

1. Primary 서버 설정

1-1. Replication 전용 계정 생성

-- Primary에서 실행
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'repl_password';

 

1-2. postgresql.conf 수정

# 외부 접속 허용
listen_addresses = '*'

# WAL 레벨 (replica 이상 필요)
wal_level = replica

# 동시 replication 연결 수
max_wal_senders = 5

# Standby가 따라잡기 위해 보관할 WAL 세그먼트
wal_keep_size = 1GB

# Replication 슬롯 수 (선택, 권장)
max_replication_slots = 5

 

1-3. pg_hba.conf에 Standby 접속 허용

# TYPE  DATABASE        USER          ADDRESS           METHOD
host    replication     replicator    10.0.0.2/32       scram-sha-256

 

1-4. Replication Slot 생성 (권장)

Slot을 사용하면 Standby가 꺼져 있는 동안에도 필요한 WAL이 삭제되지 않는다.

SELECT pg_create_physical_replication_slot('standby_slot');

주의: Standby가 장기간 꺼져 있으면 WAL이 무한히 쌓여 디스크가 가득 찬다. 운영 환경에서는 모니터링 필수.

 

1-5. Primary 재시작

sudo systemctl restart postgresql

 

2. Standby 서버 설정

2-1. 기존 데이터 디렉토리 비우기

# Standby에서 실행
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/16/main/*

 

2-2. pg_basebackup으로 Primary 복제

Primary의 전체 데이터를 Standby로 복사한다.

sudo -u postgres pg_basebackup \
    -h 10.0.0.1 \
    -U replicator \
    -D /var/lib/postgresql/16/main \
    -Fp -Xs -P -R \
    -S standby_slot

옵션 설명:

옵션 설명
-Fp Plain 포맷 (바로 사용 가능)
-Xs WAL을 스트리밍으로 전송
-P 진행률 표시
-R standby.signal 파일과 연결 정보 자동 생성
-S 사용할 Replication Slot 이름

 

-R 옵션이 자동으로 생성하는 파일들:

# standby.signal (빈 파일, 존재 자체가 Standby 모드를 의미)

# postgresql.auto.conf에 추가됨
primary_conninfo = 'host=10.0.0.1 user=replicator password=repl_password'
primary_slot_name = 'standby_slot'

 

2-3. Standby 전용 설정 (postgresql.conf)

# Standby에서 읽기 쿼리 허용
hot_standby = on

# 읽기 쿼리와 복제 충돌 시 대기 시간 (기본 30s)
max_standby_streaming_delay = 30s

 

2-4. Standby 시작

sudo systemctl start postgresql

 

3. Replication 상태 확인

Primary에서 확인:

-- 연결된 Standby 목록
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;

 

Standby에서 확인:

-- Standby 수신 상태
SELECT status, received_lsn, latest_end_lsn,
       latest_end_time, sender_host
FROM pg_stat_wal_receiver;

-- 복제 지연 시간 (PostgreSQL 10+)
SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag;

 

Slot 상태 확인:

SELECT slot_name, active, restart_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes
FROM pg_replication_slots;

 

4. Synchronous Replication (선택)

기본 Streaming Replication은 비동기(async)다. Primary가 커밋 후 Standby에 전파하므로, Primary 장애 시 최근 트랜잭션이 유실될 수 있다.

데이터 유실이 허용되지 않는 경우 동기 모드를 사용한다.

Primary의 postgresql.conf:

# Standby의 application_name과 매칭
synchronous_standby_names = 'FIRST 1 (standby1)'

# 동기 커밋 레벨
synchronous_commit = on

 

Standby의 primary_conninfo에 application_name 추가:

primary_conninfo = 'host=10.0.0.1 user=replicator password=repl_password application_name=standby1'

 

동기 모드 레벨:

synchronous_commit 보장 범위 성능
on Standby 디스크 flush 완료 가장 느림
remote_write Standby OS에 전달 완료 중간
remote_apply Standby에서 읽기 가능 가장 느림 + 읽기 일관성

주의: 동기 모드에서 Standby가 죽으면 Primary의 모든 쓰기가 멈춘다. Standby를 2대 이상 두거나 FIRST 1 (standby1, standby2)처럼 설정해야 안전하다.

 

5. Failover (수동 승격)

Primary에 장애가 발생하면 Standby를 Primary로 승격한다.

# Standby에서 실행
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main

# 또는 SQL로
SELECT pg_promote();

승격 후 standby.signal 파일이 자동 삭제되고, 쓰기가 가능해진다.

 

승격 후 해야 할 일:

# 1. 애플리케이션의 DB 연결을 새 Primary(10.0.0.2)로 변경

# 2. 기존 Primary 복구 후 Standby로 전환
#    (pg_basebackup으로 새 Primary에서 다시 복제)

# 3. 기존 Replication Slot 정리 (새 Primary에서)
SELECT pg_drop_replication_slot('standby_slot');

 

6. 자동 Failover (Patroni)

수동 failover는 운영에서 한계가 있다. 자동 failover가 필요하면 Patroni를 사용한다.

# 아키텍처
┌─────────┐     ┌─────────┐     ┌─────────┐
│ Patroni │     │ Patroni │     │  etcd    │
│ + PG    │────▶│ + PG    │────▶│ cluster  │
│ Primary │     │ Standby │     │ (DCS)    │
└─────────┘     └─────────┘     └─────────┘
      │               │
      └───────┬───────┘
              ▼
        ┌───────────┐
        │ HAProxy / │
        │ PgBouncer │
        └───────────┘

 

Patroni 최소 설정 (patroni.yml):

scope: pg-cluster
name: pg-node1

etcd3:
  hosts: 10.0.0.10:2379

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576  # 1MB
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        max_wal_senders: 5
        max_replication_slots: 5

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 10.0.0.1:5432
  data_dir: /var/lib/postgresql/16/main
  authentication:
    superuser:
      username: postgres
      password: postgres_pass
    replication:
      username: replicator
      password: repl_password

Patroni가 리더 선출, 자동 failover, pg_rewind를 통한 기존 Primary 재합류를 모두 처리한다.

 

7. Logical Replication (테이블 단위)

특정 테이블만 복제하거나, 다른 버전의 PostgreSQL 간 복제가 필요할 때 사용한다.

Primary (Publisher):

# postgresql.conf
wal_level = logical

-- Publication 생성
CREATE PUBLICATION my_pub FOR TABLE users, orders;

-- 또는 전체 테이블
CREATE PUBLICATION my_pub FOR ALL TABLES;

 

Standby (Subscriber):

-- 테이블 스키마는 미리 생성되어 있어야 함

-- Subscription 생성
CREATE SUBSCRIPTION my_sub
    CONNECTION 'host=10.0.0.1 dbname=myapp user=replicator password=repl_password'
    PUBLICATION my_pub;

 

Streaming vs Logical 비교:

항목 Streaming Logical
복제 단위 전체 클러스터 테이블 선택 가능
Standby 쓰기 불가 (읽기 전용) 가능
PG 버전 호환 동일 메이저 버전 다른 버전 가능
DDL 복제 자동 수동 (스키마 변경 별도)
Failover promote 가능 promote 불가
성능 오버헤드 낮음 상대적으로 높음

 

8. 운영 모니터링 쿼리 모음

-- 1. 복제 지연 (Primary에서)
SELECT client_addr,
       state,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag_pretty
FROM pg_stat_replication;

-- 2. 복제 지연 시간 (Standby에서)
SELECT CASE
    WHEN pg_last_wal_replay_lsn() = pg_last_wal_receive_lsn() THEN 0
    ELSE EXTRACT(EPOCH FROM now() - pg_last_xact_replay_timestamp())
END AS lag_seconds;

-- 3. Slot의 WAL 보관량 (디스크 사용량 감시)
SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots;

-- 4. 현재 서버가 Primary인지 Standby인지 확인
SELECT pg_is_in_recovery();  -- true = Standby, false = Primary

 

정리: 단계별 체크리스트

단계 서버 작업
1 Primary replication 계정 생성
2 Primary postgresql.conf 수정 (wal_level, max_wal_senders)
3 Primary pg_hba.conf에 Standby 허용
4 Primary Replication slot 생성, 재시작
5 Standby pg_basebackup -R 실행
6 Standby hot_standby = on, 시작
7 양쪽 pg_stat_replication / pg_stat_wal_receiver 확인

 

HA가 필요하면 Streaming Replication + Patroni 조합이 가장 검증된 구성이다. 테이블 단위 선택적 복제나 버전 간 마이그레이션이 필요한 경우에만 Logical Replication을 사용한다.

반응형

+ Recent posts