본문으로 건너뛰기

6.1 Replication

CloudNativePG는 PostgreSQL이 원래 제공하는 physical streaming replication을 그대로 씁니다. 새로운 복제 엔진을 만든 것이 아니라, replica를 몇 개 둘지 선언하면 Operator가 그에 맞춰 인스턴스를 배치하고 복제 채널을 붙여 줍니다. Cluster의 instances 값이 곧 인스턴스 총 개수이며, 그중 하나가 primary, 나머지가 replica가 됩니다. 즉 replica 수 = instances - 1입니다.

복제 자체는 primary가 남기는 WAL(Write-Ahead Log)을 replica가 네트워크로 받아 계속 재생하는 방식입니다. Operator는 이를 위해 streaming_replica라는 전용 사용자를 만들고, 복제 채널을 TLS client certificate 인증으로 암호화합니다. DBA가 pg_hba.conf나 인증서를 직접 관리할 일은 없습니다.

기본 구성: 3-노드 Cluster​

특별한 설정 없이 instances: 3만 선언해도 primary 1 + replica 2 구성이 완성됩니다. 복제는 기본이 asynchronous입니다.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
storage:
size: 1Gi
노트

asynchronous replication에서는 primary가 replica의 응답을 기다리지 않고 커밋을 끝냅니다. 응답 속도가 빠른 대신, primary가 갑자기 죽으면 아직 replica로 넘어가지 못한 최근 트랜잭션이 사라질 수 있습니다(RPO > 0).

Synchronous replication​

데이터 유실을 허용할 수 없다면 synchronous replication을 켭니다. 이때 primary는 지정한 수의 standby가 WAL을 받았다고 확인할 때까지 커밋을 멈춥니다. 안전(RPO=0)을 얻는 대신 커밋 지연이 늘어납니다.

CloudNativePG는 .spec.postgresql.synchronous 아래에서 두 값을 반드시 지정하게 합니다.

  • method: any(quorum 기반) 또는 first(priority 기반)
  • number: 응답을 기다릴 synchronous standby 개수

quorum 기반 (method: any)​

standby 목록 중 아무나 number개가 응답하면 커밋이 진행됩니다. 특정 standby에 얽매이지 않아 유연합니다.

spec:
instances: 3
postgresql:
synchronous:
method: any
number: 1

이 설정은 PostgreSQL의 synchronous_standby_names를 다음과 같이 자동 생성합니다.

ANY 1 (cluster-example-2, cluster-example-3, cluster-example-1)

priority 기반 (method: first)​

목록에서 앞쪽에 놓인 standby일수록 우선순위가 높습니다. 앞선 standby가 끊기면 즉시 다음 우선순위 인스턴스가 그 자리를 메웁니다.

synchronous_standby_names 내용 다듬기​

Operator가 자동으로 채우는 이름 목록을 조정하고 싶을 때 다음 옵션을 씁니다.

  • maxStandbyNamesFromCluster: Cluster 내부 pod 이름을 최대 몇 개까지 넣을지
  • standbyNamesPre: 로컬 pod 앞에 끼워 넣을 standby 이름
  • standbyNamesPost: 로컬 pod 뒤에 붙일 standby 이름
spec:
postgresql:
synchronous:
method: first
number: 2
maxStandbyNamesFromCluster: 1
standbyNamesPre:
- angus
standbyNamesPost:
- malcolm

결과는 다음과 같습니다. 외부 standby(angus, malcolm)와 Cluster 내부 pod 하나가 섞입니다.

FIRST 2 (angus, cluster-example-2, malcolm)

dataDurability: 안전과 가용성의 저울​

synchronous를 켜면 새로운 고민이 생깁니다. 응답할 standby가 부족하면 어떻게 해야 할까요? dataDurability가 이 선택을 정합니다.

required​

required — 요구한 수의 synchronous standby가 살아 있지 않으면 쓰기를 멈춥니다. 데이터 유실을 절대 허용하지 않는 모드입니다.

spec:
postgresql:
synchronous:
method: any
number: 1
dataDurability: required

standby를 고를 때는 건강한 인스턴스 → 비정상 인스턴스 → primary 순으로 우선순위를 둡니다.

preferred​

preferred — 요구한 수보다 standby가 적어도 쓰기를 계속합니다. 필요한 standby 수를 상황에 맞게 자동으로 낮추는 "self-healing" 모드입니다. 건강한 replica만 synchronous_standby_names에 올라갑니다.

spec:
postgresql:
synchronous:
method: any
number: 2
dataDurability: preferred
경고

preferred는 가용성을 우선하는 대신, 모든 standby가 사라진 상황에서는 데이터 유실이 발생할 수 있습니다. RPO=0을 반드시 지켜야 하는 시스템이라면 required를 씁니다.

동기 복제에서 standby 부족 시 쓰기 처리

Replication slot​

Replication slot은 replica가 아직 받아 가지 못한 WAL을 primary가 함부로 지우지 않도록 잡아 두는 장치입니다. slot이 없으면 replica가 잠깐 뒤처진 사이 필요한 WAL이 삭제되어 복제가 깨질 수 있습니다.

HA replication slot​

CloudNativePG는 고가용성을 위해 slot을 자동으로 관리합니다. primary에만 두는 slot과, standby로 동기화되는 slot을 함께 유지해 failover 뒤에도 복제가 끊기지 않게 합니다. 기본으로 켜져 있습니다.

spec:
replicationSlots:
highAvailability:
enabled: true # 기본값
slotPrefix: "_cnpg_" # 기본값
updateInterval: 30 # standby 동기화 주기(초)

사용자 정의 slot 동기화​

논리 복제 등으로 직접 만든 slot도 primary와 standby 사이에서 동기화합니다. 특정 slot은 정규식으로 제외합니다.

spec:
replicationSlots:
synchronizeReplicas:
enabled: true # 기본값
excludePatterns:
- "^foo"

HA replication slot의 failover 유지 경로

slot이 잡는 WAL 상한​

slot이 WAL을 붙들다가 디스크를 가득 채우는 사고를 막으려면, PostgreSQL 파라미터로 상한을 둡니다(PostgreSQL 13+).

spec:
postgresql:
parameters:
max_slot_wal_keep_size: "10GB"
경고

slot은 뒤처진 replica가 다시 따라올 여지를 주지만, 그 replica가 오래 끊겨 있으면 primary의 pg_wal이 계속 부풀어 결국 디스크가 찹니다. max_slot_wal_keep_size로 안전판을 두고, 죽은 replica를 방치하지 않습니다.

정리​

CloudNativePG는 PostgreSQL의 physical streaming replication을 그대로 쓰고, instances 값에 맞춰 primary·replica를 자동 배치합니다. 기본은 asynchronous이며, RPO=0이 필요하면 .spec.postgresql.synchronous에서 method(any/first)와 number를 지정합니다.

  • dataDurability가 안전(required)과 가용성(preferred)의 저울을 정합니다
  • HA replication slot이 failover 뒤에도 복제를 지켜 주되, max_slot_wal_keep_size로 WAL이 무한정 쌓이지 않게 막습니다