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나 인증서를 직접 손댈 일은 없다.
flowchart TD
APP["애플리케이션"]
P["primary"]
R1["replica 1"]
R2["replica 2"]
WAL["WAL stream"]
APP -->|"쓰기"| P
P --> WAL
WAL -->|"streaming"| R1
WAL -->|"streaming"| R2
APP -.->|"읽기"| R1
기본 구성: 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: 1GiSynchronous 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 — 요구한 수의 synchronous standby가 살아 있지 않으면 쓰기를 멈춘다. 데이터 유실을 절대 허용하지 않는 모드다.
spec:
postgresql:
synchronous:
method: any
number: 1
dataDurability: requiredstandby를 고를 때는 건강한 인스턴스 → 비정상 인스턴스 → primary 순으로 우선순위를 둔다.
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"slot이 잡는 WAL 상한
slot이 WAL을 붙들다가 디스크를 가득 채우는 사고를 막으려면, PostgreSQL 파라미터로 상한을 둔다(PostgreSQL 13+).
spec:
postgresql:
parameters:
max_slot_wal_keep_size: "10GB"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 뒤에도 복제를 지켜 주되, WAL이 무한정 쌓이지 않게 상한을 둔다
다음 절(6.2)에서는 primary가 실제로 죽었을 때 Operator가 어떻게 감지하고 새 primary를 세우는지, 자동 failover의 흐름과 타이밍을 본다.