복제
복제 (Replication)
물리적 복제는 PostgreSQL의 강점 중 하나이며, 세계에서 가장 큰 조직들 중 일부가 비즈니스 연속성 맥락에서 데이터 관리를 위해 PostgreSQL을 선택한 이유 중 하나예요. 주로 고가용성을 달성하는 데 사용되지만, 물리적 복제는 읽기 전용 워크로드의 스케일아웃과 프라이머리에서 일부 작업을 오프로드하는 것도 가능하게 해줘요.
:::info[Important]
이 섹션은 같은 쿠버네티스 클러스터에서 관리되는 같은 Cluster 리소스 내의 복제에 관한 내용이에요. 다른 Postgres Cluster 리소스와(심지어 서로 다른 쿠버네티스 클러스터를 가로질러) 복제하는 방법에 대한 정보는 "Replica clusters" 섹션을 참조하세요.
:::
출처: 문서
본문
애플리케이션 수준 복제 (Application-level replication)
수년간 PostgreSQL의 복제 기능에 기여해온 우리는 CloudNativePG의 고가용성을 네이티브 물리적 복제 기술 위에 구축하고, 이를 쿠버네티스 API에 직접 통합하기로 결정했어요.
쿠버네티스 용어로 이것을 storage-level replication과 대비되는 애플리케이션 수준 복제(application-level replication) 라고 불러요.
매우 성숙한 기술
PostgreSQL은 프라이머리 인스턴스에서 하나 이상의 레플리카로 데이터를 복제하기 위한 매우 견고하고 성숙한 네이티브 프레임워크를 가지며, WAL(Write Ahead Log)에 지속적으로 저장되는 트랜잭션 변경의 개념을 중심으로 구축됐어요.
크래시 복구와 시점 복구 기술의 진화로 시작된 물리적 복제는 PostgreSQL 8.2(2006)에서 연속 복구 상태의 웜 스탠바이로의 WAL shipping을 통해 처음 도입됐어요.
PostgreSQL 9.0(2010)은 hot standby를 통한 WAL 스트리밍과 읽기 전용 레플리카를 도입했어요. 2011년 PostgreSQL 9.1은 트랜잭션 수준의 동기식 복제를 가져와 RPO=0 클러스터를 지원했어요. 캐스케이딩 복제는 PostgreSQL 9.2(2012)에 추가됐어요. 논리 복제의 기초는 PostgreSQL 9.4(2014)에서 세워졌고, 버전 10(2017)은 오리진에서 목적지로 데이터를 복제하기 위한 publisher/subscriber 패턴에 대한 네이티브 지원을 도입했어요. 아래 표는 이러한 이정표를 요약해요.
| Version | Year | Feature |
|---|---|---|
| 8.2 | 2006 | Warm Standby with WAL shipping |
| 9.0 | 2010 | Hot Standby and physical streaming replication |
| 9.1 | 2011 | Synchronous replication (priority-based) |
| 9.2 | 2012 | Cascading replication |
| 9.4 | 2014 | Foundations of logical replication |
| 10 | 2017 | Logical publisher/subscriber and quorum-based synchronous replication |
| 버전 | 연도 | 기능 |
|---|---|---|
| 8.2 | 2006 | WAL shipping을 통한 웜 스탠바이 |
| 9.0 | 2010 | 핫 스탠바이와 물리적 스트리밍 복제 |
| 9.1 | 2011 | 동기식 복제 (우선순위 기반) |
| 9.2 | 2012 | 캐스케이딩 복제 |
| 9.4 | 2014 | 논리 복제의 기초 |
| 10 | 2017 | 논리 publisher/subscriber와 쿼럼 기반 동기식 복제 |
이 표는 핵심 PostgreSQL 복제 기능과 각자의 버전을 강조해요.
스트리밍 복제 지원
현재 CloudNativePG는 spec에서 제공된 instances 수에 기반해 클러스터 내에서 물리적 스트리밍 레플리카를 선언적으로 네이티브하고 투명하게 관리해요:
replicas = instances - 1 (where instances > 0)
클러스터 초기화 직후 오퍼레이터는 다음과 같이 streaming_replica라는 사용자를 만들어요:
CREATE USER streaming_replica WITH REPLICATION;
-- NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOBYPASSRLS
기본적으로 오퍼레이터는 암호화된 채널을 통해 클러스터 내 스트리밍 복제를 자동으로 설정하고, streaming_replica 사용자에 대해 TLS 클라이언트 인증서 인증을 강제해요. pg_hba.conf에서 가져온 다음 발췌문이 이를 보여줘요:
# Require client certificate authentication for the streaming_replica user
hostssl postgres streaming_replica all cert map=cnpg_streaming_replica
hostssl replication streaming_replica all cert map=cnpg_streaming_replica
:::note[Certificates] CloudNativePG가 인증서를 관리하는 방법에 대한 자세한 내용은 문서의 "Certificates" 섹션을 참조하세요. :::
구성된 경우 오퍼레이터는 HA 클러스터의 모든 레플리카에 대한 복제 슬롯을 관리해서, 각 스탠바이가 필요로 하는 WAL 파일이 장애 조치(failover) 또는 스위치오버(switchover) 후에도 프라이머리의 스토리지에 보존되도록 보장해요.
:::note[Replication slots for High Availability] CloudNativePG가 고가용성 레플리카용 복제 슬롯을 자동으로 관리하는 방법에 대한 자세한 내용은 아래 "Replication slots for High Availability" 섹션을 참조하세요. :::
연속 백업 통합 (Continuous backup integration)
클러스터에 연속 백업이 구성된 경우, CloudNativePG는 연속 복구 상태에서 레플리카가 restore_command를 활용하도록 투명하게 구성해요. 결과적으로 PostgreSQL은 스트리밍 복제를 통해 WAL을 가져오는 데 실패할 때마다 WAL 아카이브를 폴백 옵션으로 사용할 수 있어요.
동기식 복제 (Synchronous Replication)
CloudNativePG는 PostgreSQL의 쿼럼 기반 및 우선순위 기반 동기식 복제를 모두 지원해요.
:::warning 기본적으로 동기식 복제는 트랜잭션 커밋 중 WAL 복제를 위해 필요한 수의 스탠바이 노드가 없으면 쓰기 작업을 일시 중지해요. 이 동작은 데이터 내구성을 우선시하며 PostgreSQL DBA 모범 사례와 일치해요. 일반적으로 최소 3개 인스턴스가 있는 클러스터에서 동기식 복제를 계획해서 그중 하나의 손실을 견디도록 하세요. 하지만 설정에서 자가 치유(self-healing)가 엄격한 데이터 내구성보다 더 높은 우선순위라면 이 설정을 조정할 수 있어요. 이 동작 관리에 대한 자세한 내용은 Data Durability and Synchronous Replication 섹션을 참조하세요. :::
:::info[Important] failover quorum 기능은 동기식 복제와 함께 사용해서 장애 조치 이벤트 중 데이터 내구성과 안전을 향상시킬 수 있어요. :::
synchronous_standby_names 옵션의 직접 구성은 허용되지 않아요. 하지만 CloudNativePG는 이 옵션을 로컬 Pod의 이름으로 자동 채우면서, Cluster 리소스 너머로 동기식 복제를 확장하도록 커스터마이즈하는 것도 허용해요. 이는 .spec.postgresql.synchronous stanza로 달성할 수 있어요.
동기식 복제는 기본적으로 비활성화돼요 (synchronous stanza가 정의되지 않음). 정의되면 두 옵션은 필수예요:
method:any(쿼럼) 또는first(우선순위) 중 하나number: 트랜잭션이 응답을 기다려야 하는 동기식 스탠바이 서버의 수
쿼럼 기반 동기식 복제
PostgreSQL에서 쿼럼 기반 동기식 복제는 트랜잭션 커밋이 지정된 수의 스탠바이에 WAL 레코드가 복제될 때까지 기다리도록 보장해요. 이를 활성화하려면 method를 any로 설정하세요.
이 복제 방법은 CloudNativePG 클러스터에서 가장 흔한 설정이에요.
예시
세 개의 인스턴스를 가진 전형적인 cluster-example 구성을 기반으로 하는 아래 예시는 최소 하나의 인스턴스로 쿼럼 기반 동기식 복제를 설정해요:
postgresql:
synchronous:
method: any
number: 1
이 구성으로 CloudNativePG는 synchronous_standby_names의 내용을 다음과 같이 자동 설정해요:
ANY 1 (cluster-example-2, cluster-example-3, cluster-example-1)
사용 중단된 동기식 복제 구현에서 마이그레이션하기
이 섹션은 사용 중단된 쿼럼 기반 동기식 복제 형식에서 CloudNativePG의 더 새롭고 견고한 구현으로 마이그레이션하는 방법을 설명해요.
다음 매니페스트가 있다고 할 때:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: angus
spec:
instances: 3
minSyncReplicas: 1
maxSyncReplicas: 1
storage:
size: 1Gi
다음과 같이 새 형식으로 업데이트할 수 있어요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: angus
spec:
instances: 3
storage:
size: 1Gi
postgresql:
synchronous:
method: any
number: 1
dataDurability: required
엄격한 데이터 내구성보다 자가 치유를 우선시하려면 dataDurability를 preferred로 설정하세요.
우선순위 기반 동기식 복제
PostgreSQL의 우선순위 기반 동기식 복제는 트랜잭션 커밋이 우선순위에 따라 선택된 요청된 수의 동기식 스탠바이에 WAL 레코드가 복제될 때까지 기다리게 해요. synchronous_standby_names 옵션에서 앞에 나열된 스탠바이에게 더 높은 우선순위가 주어지고 동기식으로 간주돼요. 현재 동기식 스탠바이가 연결을 끊으면 즉시 다음으로 높은 우선순위의 스탠바이로 대체돼요. 이 방법을 사용하려면 method를 first로 설정하세요.
:::info[Important]
현재 이 방법은 아래 설명된 maxStandbyNamesFromCluster, standbyNamesPre, standbyNamesPost 옵션을 사용해 동기식 복제를 현재 클러스터 너머로 확장할 때 가장 유용해요.
:::
synchronous_standby_names 내용 제어하기
기본적으로 CloudNativePG는 Cluster 리소스의 로컬 Pod 이름으로 synchronous_standby_names를 채워 PostgreSQL 클러스터 내 동기식 복제를 보장해요. 요구사항과 복제 방법(쿼럼 또는 우선순위)에 따라 .spec.postgresql.synchronous stanza의 다음 선택적 파라미터로 synchronous_standby_names 내용을 커스터마이즈할 수 있어요:
maxStandbyNamesFromCluster: 로컬Cluster객체에서 PostgreSQL의synchronous_standby_names옵션에 자동으로 포함될 수 있는 Pod 이름의 최대 수.standbyNamesPre: 오퍼레이터가 자동으로 나열하는 로컬 Pod 이름 목록 앞에 붙일 스탠바이 이름(구체적으로application_name) 목록.standbyNamesPost: 오퍼레이터가 자동으로 나열하는 로컬 Pod 이름 목록 뒤에 추가할 스탠바이 이름(구체적으로application_name) 목록.
:::warning
standbyNamesPre와 standbyNamesPost의 올바른 이름을 보장하는 것은 여러분의 책임이에요. CloudNativePG는 여기에 나열된 application_name을 가진 모든 스탠바이를 관리하고 그들의 고가용성을 보장하길 기대해요. 잘못된 항목은 PostgreSQL 데이터베이스의 가동 시간을 위험에 빠뜨릴 수 있어요.
:::
예시
다음은 모두 세 개의 인스턴스를 가진 cluster-example을 기반으로 한 몇 가지 예시예요:
다음처럼 설정하면:
postgresql:
synchronous:
method: any
number: 1
maxStandbyNamesFromCluster: 1
standbyNamesPre:
- angus
synchronous_standby_names의 내용은 다음과 같아요:
ANY 1 (angus, cluster-example-2)
다음처럼 설정하면:
postgresql:
synchronous:
method: any
number: 1
maxStandbyNamesFromCluster: 0
standbyNamesPre:
- angus
- malcolm
synchronous_standby_names의 내용은 다음과 같아요:
ANY 1 (angus, malcolm)
다음처럼 설정하면:
postgresql:
synchronous:
method: first
number: 2
maxStandbyNamesFromCluster: 1
standbyNamesPre:
- angus
standbyNamesPost:
- malcolm
synchronous_standby_names 옵션은 다음과 같아요:
FIRST 2 (angus, cluster-example-2, malcolm)
데이터 내구성과 동기식 복제
.spec.postgresql.synchronous stanza의 dataDurability 옵션은 동기식 복제에 대한 데이터 안전과 가용성 사이의 트레이드오프를 제어해요. required 또는 preferred로 설정할 수 있으며, 지정하지 않으면 기본값은 required예요.
:::info[Important]
preferred는 standbyNamesPre와 standbyNamesPost가 설정되지 않았을 때만 사용할 수 있어요.
:::
필수 데이터 내구성 (Required Data Durability)
dataDurability가 required로 설정되면 PostgreSQL은 WAL(Write-Ahead Log) 레코드가 지정된 수의 동기식 스탠바이에 복제된 경우에만 트랜잭션을 커밋된 것으로 간주해요. 이 설정은 가용성보다 데이터 안전을 우선시하며, 필요한 수의 동기식 스탠바이를 사용할 수 없으면 쓰기 작업이 일시 중지된다는 뜻이에요. 이는 데이터 손실 없음(RPO=0)을 보장하지만 네트워크 중단이나 스탠바이 실패 중 데이터베이스 가용성을 줄일 수 있어요.
동기식 스탠바이는 다음 우선순위 순서로 선택돼요:
- 정상 인스턴스
- 비정상 인스턴스
- 프라이머리
그런 다음 maxStandbyNamesFromCluster가 설정된 경우 이 값에 따라 목록이 잘리며, 정상 인스턴스를 우선시하고 synchronous_standby_names가 채워지도록 보장해요.
예시
다음 예시를 살펴보세요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: foo
spec:
instances: 3
postgresql:
synchronous:
method: any
number: 1
dataDurability: required
-
초기 상태.
synchronous_standby_names의 내용은:ANY 1 ("foo-2","foo-3","foo-1") -
foo-2가 사용 불가능해짐. 우선순위가 뒤로 밀림:ANY 1 ("foo-3","foo-2","foo-1") -
foo-3도 사용 불가능해짐. 목록에 정상 스탠바이가 없음:ANY 1 ("foo-2","foo-3","foo-1")이 시점에 스탠바이 중 하나가 다시 사용 가능해질 때까지 쓰기 작업은 허용되지 않아요.
-
스탠바이가 다시 사용 가능해지면
synchronous_standby_names는 초기 상태로 돌아가요.
선호 데이터 내구성 (Preferred Data Durability)
dataDurability가 preferred로 설정되면 필요한 동기식 인스턴스 수가 사용 가능한 스탠바이 수에 따라 조정돼요. PostgreSQL은 지정된 수의 동기식 스탠바이에 WAL 레코드를 복제하려고 시도하지만, 요청된 수보다 적은 스탠바이를 사용할 수 있더라도 쓰기 작업은 계속돼요.
:::info[Important] 레플리카에 대해 ready/available이 무엇을 의미하는지 명확히 이해하고 그에 따라 기대치를 설정하세요. 기본적으로 레플리카는 소스에 최소한 한 번 성공적으로 연결되었을 때 준비된 것으로 간주돼요. 하지만 CloudNativePG를 사용하면 최대 지연(maximum lag)에 기반해 레플리카의 startup 및 readiness 프로브를 구성할 수 있어요. 자세한 내용은 "Postgres instance manager" 섹션을 참조하세요. :::
이 설정은 데이터 안전과 가용성의 균형을 맞추며, 일시적인 스탠바이 사용 불가능 동안 애플리케이션이 계속 쓰기를 할 수 있게 해줘요 — 그래서 *자가 치유 모드(self-healing mode)*라고도 알려져 있어요.
:::warning 이 모드는 모든 스탠바이가 사용 불가능해지면 데이터 손실이 발생할 수 있어요. :::
preferred 데이터 내구성에서는 정상 레플리카만 synchronous_standby_names에 포함돼요.
예시
다음 예시를 고려해 보세요. 데모를 위해 5개 인스턴스와 2개의 동기식 스탠바이를 가진 bar라는 클러스터를 사용할게요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: bar
spec:
instances: 5
postgresql:
synchronous:
method: any
number: 2
dataDurability: preferred
-
초기 상태.
synchronous_standby_names의 내용은:ANY 2 ("bar-2","bar-3", "bar-4", "bar-5") -
bar-2와bar-3이 사용 불가능해짐. 목록에서 제거됨:ANY 2 ("bar-4", "bar-5") -
bar-4도 사용 불가능해짐. 목록에서 제거됨. 사용 가능한 스탠바이 수가 요청된 수보다 적으므로 요청된 수가 줄어듦:ANY 1 ("bar-5") -
bar-5도 사용 불가능해짐.synchronous_standby_names가 비워져 동기식 복제가 완전히 비활성화됨. 쓰기 작업은 계속되지만 프라이머리 실패 시 잠재적 데이터 손실의 위험이 있음. -
레플리카가 돌아오면
synchronous_standby_names는 초기 상태로 돌아감.
실패 도메인 인지 동기식 복제 (Failure Domain-Aware Synchronous Replication)
CloudNativePG는 동기식 스탠바이 선택을 프라이머리와 다른 실패 도메인(예: 다른 가용 영역)에 위치한 인스턴스로 제한할 수 있어요. 실패 도메인을 정의하는 레이블은 .spec.postgresql.synchronous stanza의 다음 상호 배타 옵션 중 하나를 통해 선언돼요:
podFailureDomainKeys: 각 인스턴스 Pod의 레이블에서 해석되는 Pod 레이블 키 목록 — 노드를 전혀 조회하지 않음 (권장)nodeFailureDomainKeys: 각 인스턴스를 호스팅하는 노드의 레이블에서 해석되는 노드 레이블 키 목록 (예:topology.kubernetes.io/zone)
나열된 모든 레이블이 같은 값을 가질 때 두 인스턴스는 같은 실패 도메인에 속해요.
::::note[Pod의 토폴로지 레이블]
쿠버네티스 1.35부터 topology.kubernetes.io/zone과 topology.kubernetes.io/region 레이블은 스케줄링 시 노드에서 각 Pod로 자동 복사돼요. 결과적으로 topology.kubernetes.io/zone(가장 흔한 실패 도메인 식별자)은 추가 설정 없이 Pod에서 사용할 수 있어요. 쿠버네티스 1.34 이하를 사용하거나 PodTopologyLabelsAdmission 기능을 사용할 수 없거나 활성화되지 않았다면(KEP-4742 참조), nodeFailureDomainKeys를 대신 사용하세요.
::::
CloudNativePG는 실패 도메인 제약을 완전히 충족할 수 있을 만큼 충분한 크로스 도메인 스탠바이가 있을 때만 적용해요: required 데이터 내구성으로는 전체 구성된 number, preferred로는 하나만. 그럴 때 프라이머리의 실패 도메인 밖의 스탠바이만 synchronous_standby_names를 채워요.
preferred 데이터 내구성에서는 이 배치 선호가 요구되는 확인(acknowledgment) 수를 낮출 수 있다는 뜻이에요: "Preferred Data Durability"에서 다루듯, 그 수는 목록의 스탠바이 수로 상한이 정해지므로, 정상 레플리카 두 개 중 단일 크로스 도메인 스탠바이는 실패 도메인 키가 없는 클러스터가 요구하는 두 개 대신 하나의 요구 확인으로 이어져요. 쓰기는 절대 차단되지 않지만 각 커밋의 내구성은 실패 도메인 분포를 따르게 돼요.
그렇지 않은 경우(크로스 도메인 스탠바이가 너무 적거나, Pod를 호스팅하는 노드가 삭제됐거나 podFailureDomainKeys에 나열된 레이블이 Pod에 없는 등의 이유로 토폴로지를 추출할 수 없는 경우), 제약은 완전히 버려지고 키가 전혀 구성되지 않은 것처럼 스탠바이가 선택돼요. 이 올-오어-낫싱 동작은 쓰기 트랜잭션이 도착할 수 없는 확인을 절대 기다리지 않도록 보장해요.
클러스터 상태의 SyncReplicationTopologySatisfied 조건은 배치 선호가 현재 존중되고 있는지 보고해요. 조건의 reason은 kubectl get clusters -o wide의 SYNCTOPOLOGY 열에도 표시돼요. kubectl cnpg status 명령은 인스턴스가 실패 도메인에 어떻게 분산되어 있는지 보여줘요.
각 인스턴스의 실패 도메인과 토폴로지가 성공적으로 추출되었는지 여부를 포함한 기본 데이터는 클러스터의 .status.topology 키 아래에서 사용할 수 있어요.
:::important
이 조건은 클러스터 인스턴스만 평가해요: standbyNamesPre와 standbyNamesPost를 통해 제공된 스탠바이 이름은 오퍼레이터의 지식 밖이며 검사에 포함되지 않아요.
:::
예를 들어 다음 클러스터는 가능할 때마다 프라이머리와 다른 가용 영역에서 실행되는 인스턴스에서 선택된 두 개의 동기식 스탠바이를 유지해요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example-syncreplicas
spec:
instances: 5
postgresql:
synchronous:
podFailureDomainKeys:
- topology.kubernetes.io/zone
method: first
number: 2
storage:
size: 1Gi
동기식 복제 (사용 중단됨)
:::warning
CloudNativePG 1.24 이전에는 쿼럼 기반 동기식 복제 구현만 지원됐어요. 이 방법은 이제 사용 중단되었지만 당분간 제거되지는 않을 거예요.
새 방법은 자가 치유보다 데이터 내구성을 우선시하며 우선순위 기반 동기식 복제와 synchronous_standby_names 옵션에 대한 완전한 제어를 포함한 더 견고한 기능을 제공해요.
이전 단락에서 설명한 대로 동기식 복제에 대한 새 구성 방법으로 점진적으로 마이그레이션하는 것을 권장해요.
:::
:::info[Important] 사용 중단된 방법과 새 방법은 상호 배타적이에요. :::
CloudNativePG는 minSyncReplicas와 maxSyncReplicas라는 두 구성 옵션을 통해 쿼럼 기반 동기식 스트리밍 복제 구성을 지원해요. 이 둘은 언제든지 사용 가능해야 하는 동기식 스탠바이 레플리카의 최소 및 최대 수예요.
자가 치유 목적으로 오퍼레이터는 항상 이 두 값을 사용 가능한 레플리카 수와 비교해 쿼럼을 결정해요.
:::info[Important]
기본적으로 동기식 복제는 사용 가능한 모든 레플리카 중에서 무차별적으로 선택해요. 다음 섹션에서 설명하는 syncReplicaElectionConstraint 옵션으로 노드 레이블을 작업하여 동기식 레플리카가 스케줄링될 수 있는 노드를 제한할 수 있어요.
:::
동기식 복제는 기본적으로 비활성화돼요 (minSyncReplicas와 maxSyncReplicas가 정의되지 않음).
minSyncReplicas와 maxSyncReplicas가 둘 다 설정된 경우 CloudNativePG는 PostgreSQL의 synchronous_standby_names 옵션을 다음 값으로 자동 업데이트해요:
ANY q (pod1, pod2, ...)
여기서:
q는 오퍼레이터가 자동 계산한 정수로:1 ≤ minSyncReplicas ≤ q ≤ maxSyncReplicas ≤ readyReplicaspod1, pod2, ...는 클러스터의 모든 PostgreSQL Pod 목록
:::warning
자가 치유 기능을 제공하기 위해 오퍼레이터는 minSyncReplicas가 현재 사용 가능한 레플리카 수보다 높으면 이를 무시할 수 있어요. readyReplicas가 0이면 동기식 복제는 자동으로 비활성화돼요.
:::
PostgreSQL 문서에 명시된 대로, method ANY는 쿼럼 기반 동기식 복제를 지정하며 목록의 요청된 수 이상의 동기식 스탠바이에 WAL 레코드가 복제될 때까지 트랜잭션 커밋이 기다리게 한다.
:::info[Important]
오퍼레이터는 동기식 복제 설정의 강제보다 자가 치유를 선택하지만, 동기식 복제는 3+ 인스턴스가 있는 클러스터에서만, 또는 더 일반적으로 maxSyncReplicas < (instances - 1)일 때 계획할 것을 권장해요.
:::
동기식 복제용 노드 선택하기
:::info[새 실패 도메인 API 사용]
사용 중단된 쿼럼 기반 동기식 복제 API가 특별히 필요하지 않다면 현재 API를 선호하세요. 동등하고 더 유연한 podFailureDomainKeys와 nodeFailureDomainKeys 옵션은 "Failure Domain-Aware Synchronous Replication"을 참조하세요.
:::
CloudNativePG는 PGDATA를 보유한 PVC와 Postgres Pod가 있는 노드의 레이블에 기반한 anti-affinity 규칙을 통해 쿼럼 기반 동기식 복제 세트에 참여할 자격이 있는 PostgreSQL 인스턴스를 선택할 수 있게 해줘요.
:::note[Scheduling] 일반적인 pod affinity와 anti-affinity 규칙에 대한 더 많은 정보는 "Scheduling" 섹션을 확인하세요. :::
:::warning
.spec.postgresql.syncReplicaElectionConstraint 옵션은 동기식 복제의 레거시 구현에만 적용돼요 ("Synchronous Replication (Deprecated)" 참조).
:::
이 기능의 예시 사용 사례로: 단일 sync 레플리카가 있는 클러스터에서, 일반적으로 노드의 topology.kubernetes.io/zone 레이블로 식별되는 프라이머리 인스턴스와 다른 가용 영역에 sync 레플리카가 있도록 보장할 수 있어요. 이것은 특히 복구 지점 목표(RPO) 측면에서 단일 가용 영역의 중단 발생 시 클러스터의 견고성을 높여요.
anti-affinity의 아이디어는 쿼럼에 참여하는 sync 레플리카가 프라이머리가 현재 실행 중인 노드와 선택된 레이블(이 경우 가용 영역 레이블)에 대해 서로 다른 값을 가진 노드에서 실행되는 Pod에서 선택되도록 보장하는 거예요. 그런 기준을 충족하는 노드가 없으면 레플리카는 동기식 복제에 적격해요.
:::info[Important] 동기식 레플리카 선거에 추가 제약을 정의하는 동안 자가 치유 강제는 여전히 적용돼요 ("Synchronous replication" 참조). :::
아래 예시는 .spec.postgresql 내의 syncReplicaElectionConstraint 섹션을 통해 어떻게 이 작업을 수행할 수 있는지 보여줘요. nodeLabelsAntiAffinity는 프라이머리가 있는 노드와 다른 가용 영역 레이블 값을 가진 노드에 위치한 레플리카와 현재 프라이머리 사이에 오퍼레이터가 동기식 복제를 동적으로 구성하도록 평가해야 하는 노드 레이블을 지정할 수 있게 해줘요:
spec:
instances: 3
postgresql:
syncReplicaElectionConstraint:
enabled: true
nodeLabelsAntiAffinity:
- topology.kubernetes.io/zone
상상할 수 있듯이, 가용 영역은 그저 예시일 뿐이며 스토리지, CPU, 메모리 같은 노드를 설명하는 다른 레이블에 기반해 이 동작을 커스터마이즈할 수 있어요.
복제 슬롯 (Replication slots)
복제 슬롯은 9.4에서 도입된 네이티브 PostgreSQL 기능으로, 연결된 모든 스트리밍 복제 클라이언트가 받을 때까지 프라이머리가 WAL 세그먼트를 제거하지 않도록 하고, 스탠바이가 (일시적으로) 연결이 끊겨도 복구 충돌을 일으킬 수 있는 행을 제거하지 않도록 보장하는 자동화된 방법을 제공해요.
복제 슬롯은 그것을 만든 인스턴스에만 존재하며, PostgreSQL은 그것을 스탠바이 서버에 복제하지 않아요. 결과적으로 장애 조치 또는 스위치오버 후에 새 프라이머리는 이전 프라이머리의 복제 슬롯을 포함하지 않아요. 이는 이전 프라이머리에 연결되어 슬롯을 잃어버린 스트리밍 복제 클라이언트에게 문제를 만들 수 있어요.
CloudNativePG는 물리적 복제 슬롯의 내용을 프라이머리에서 각 스탠바이로 동기화하는 턴키 솔루션을 제공하며, 두 가지 사용 사례를 다뤄요:
- Postgres 클러스터의 고가용성을 위해 자동으로 생성된 복제 슬롯 (자세한 내용은 아래 "Replication slots for High Availability" 참조)
- 프라이머리에 생성된 사용자 정의 복제 슬롯
고가용성을 위한 복제 슬롯
CloudNativePG는 고가용성 클러스터에서 시작하여 클러스터 관리 복제 슬롯의 개념을 도입함으로써 이 간극을 메워요. 이 기능은 고가용성 클러스터의 각 핫 스탠바이 레플리카에 대한 물리적 복제 슬롯을 프라이머리와 스탠바이 둘 다에서 자동 관리해요.
CloudNativePG에서 우리는 용어를 사용해요:
- Primary HA slot: 수명 주기를 전적으로 클러스터의 현재 프라이머리가 관리하고 스트리밍 복제에서 특정 스탠바이에 매핑하는 것이 목적인 물리적 복제 슬롯. 이런 슬롯은 프라이머리에만 존재해요.
- Standby HA slot: 프라이머리의
pg_replication_slots뷰 내용에 기반해 수명 주기를 전적으로 클러스터의 다른 스탠바이가 관리하고,pg_replication_slot_advance()로 정기 간격으로 업데이트되는 스탠바이용 물리적 복제 슬롯.
이 기능은 기본적으로 활성화되어 있으며 구성으로 비활성화할 수 있어요. 자세한 내용은 API reference의 "replicationSlots" 섹션을 참조하세요. 주요 옵션에 대한 간단한 설명은 다음과 같아요:
.spec.replicationSlots.highAvailability.enabled
: true이면 기능이 활성화돼요 (true가 기본값)
.spec.replicationSlots.highAvailability.slotPrefix
: 이 기능을 위해 오퍼레이터가 관리하는 복제 슬롯을 식별하는 접두사 (기본값: _cnpg_)
.spec.replicationSlots.updateInterval
: 스탠바이가 복제 슬롯의 로컬 사본 위치를 현재 프라이머리의 위치와 동기화하는 빈도 (초 단위, 기본값: 30)
권장되지는 않지만 다른 동작을 원한다면 위 옵션을 커스터마이즈할 수 있어요.
예를 들어 다음 매니페스트는 복제 슬롯이 비활성화된 클러스터를 만들어요.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
# Disable replication slots for HA in the cluster
replicationSlots:
highAvailability:
enabled: false
storage:
size: 1Gi
사용자 정의 복제 슬롯
CloudNativePG는 물리적 복제 슬롯을 선언적으로 정의하는 방법을 지원하지 않지만, SQL로 자신만의 슬롯을 만들 수는 있어요.
:::note[Information] 현재 복제 슬롯을 선언적으로 관리할 계획은 없지만, 사용자 피드백에 따라 바뀔 수 있어요. 그 이유는 복제 슬롯이 특정 목적으로 존재하며 각각이 프라이머리에서 슬롯의 전체 수명 주기를 감독하는 특정 애플리케이션이 관리해야 하기 때문이에요. :::
CloudNativePG는 프라이머리와 스탠바이 사이에서 사용자 관리 물리적 복제 슬롯의 동기화를 위에서 설명한 HA 복제 슬롯과 유사하게 관리할 수 있어요 (유일한 차이는 복제 슬롯을 직접 만들어야 한다는 점이에요).
이 기능은 기본적으로 활성화되어 있으며(즉, 모든 복제 슬롯이 동기화됨), synchronizeReplicas stanza를 통해 비활성화하거나 동작을 더 커스터마이즈할 수 있어요(예: 정규식으로 일부 슬롯 제외). 예를 들어:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
replicationSlots:
synchronizeReplicas:
enabled: true
excludePatterns:
- "^foo"
자세한 내용은 API reference의 "replicationSlots" 섹션을 참조하세요. 주요 옵션에 대한 간단한 설명은 다음과 같아요:
.spec.replicationSlots.synchronizeReplicas.enabled
: true이거나 지정되지 않으면 프라이머리의 모든 사용자 정의 복제 슬롯이 각 스탠바이에서 동기화돼요. false로 변경하면 오퍼레이터는 이전에 자체적으로 각 스탠바이에 만든 모든 복제 슬롯을 제거해요.
.spec.replicationSlots.synchronizeReplicas.excludePatterns
: 동기화에서 제외할 사용자 정의 복제 슬롯 이름과 일치시키는 정규식 패턴 목록. 명명 규칙에 따라 특정 슬롯을 제외하는 데 유용할 수 있어요.
:::warning 이 기능을 사용하는 사용자는 사용자 정의 복제 슬롯을 주의 깊게 모니터링해서 운영 요구사항과 일치하고 장애 조치 프로세스를 방해하지 않도록 해야 해요. :::
동기화 빈도
스탠바이가 프라이머리의 pg_replication_slots 뷰를 쿼리하고 복제 슬롯의 로컬 사본을 업데이트하는 빈도를 다음 예시처럼 제어할 수도 있어요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
# Reduce the frequency of standby HA slots updates to once every 5 minutes
replicationSlots:
updateInterval: 300
storage:
size: 1Gi
논리 디코딩 슬롯 동기화
CloudNativePG는 고가용성 클러스터의 모든 노드에서 논리 디코딩(복제) 슬롯을 동기화할 수 있어, 장애 조치나 스위치오버 후 논리 복제의 원활한 지속을 보장해요. 이 기능은 기본적으로 비활성화되어 있으며, 활성화하려면 두 단계가 필요해요.
첫 단계는 논리 디코딩 슬롯 동기화를 활성화하는 거예요:
# ...
replicationSlots:
highAvailability:
synchronizeLogicalDecoding: true
두 번째 단계는 PostgreSQL 파라미터를 구성하는 거예요: 필요한 구성은 아래 설명된 대로 PostgreSQL 버전에 따라 달라져요.
활성화되면 오퍼레이터는 장애 조치와 스위치오버 중 논리 디코딩 슬롯 상태를 자동 관리해서 슬롯 무효화를 방지하고 논리 복제 클라이언트의 데이터 손실을 피해요.
PostgreSQL 17 이상에서의 동작
PostgreSQL 17 이상의 경우 CloudNativePG는 synchronized_standby_slots 파라미터를 투명하게 관리해요.
PostgreSQL 구성에서 sync_replication_slots와 hot_standby_feedback를 모두 활성화해야 해요:
# ...
postgresql:
parameters:
# ...
hot_standby_feedback: 'on'
sync_replication_slots: 'on'
또한 failover 옵션이 활성화된 논리 복제 Subscription을 만들어야 해요. 예를 들어:
apiVersion: postgresql.cnpg.io/v1
kind: Subscription
# ...
spec:
# ...
parameters:
failover: 'true'
# ...
구성되면 논리 WAL 송신자 프로세스는 지정된 복제 슬롯이 관련 WAL을 수신하고 플러시했음을 확인한 후에만 디코딩된 변경을 플러그인에게 보내요. 이는 다음을 보장해요:
- 논리 복제 슬롯이 publisher의 레플리카에 안전하게 수신되기 전에는 변경을 소비하지 않고,
- 논리 복제 클라이언트가 장애 조치 후 데이터를 놓치지 않고 승격된 스탠바이에 원활하게 재연결할 수 있음.
논리 복제 슬롯 동기화에 대한 더 자세한 내용은 PostgreSQL 문서의 Logical Replication Failover를 참조하세요.
PostgreSQL 16 이하에서의 동작
PostgreSQL 16 및 이전 버전의 경우 CloudNativePG는 pg_failover_slots 확장을 사용해 장애 조치를 가로질러 논리 복제 슬롯의 동기화를 유지해요.
복제 슬롯용으로 보존되는 WAL 크기 상한
복제 슬롯이 활성화되면 PostgreSQL이 복제 슬롯이 요청한 WAL 파일을 보존하려 하기 때문에 디스크 공간이 부족해질 수 있어요. 이것은 (일시적으로?) 다운되거나 지연된 스탠바이 또는 그저 고아가 된 복제 슬롯 때문에 발생할 수 있어요.
PostgreSQL 13부터 체크포인트 시점에 복제 슬롯이 pg_wal 디렉토리에 보존하도록 허용된 WAL 파일의 최대 크기를 제어하는 max_slot_wal_keep_size 구성 옵션을 활용할 수 있어요. 기본적으로 PostgreSQL에서 max_slot_wal_keep_size는 -1로 설정되어, 복제 슬롯이 무제한의 WAL 파일을 보존할 수 있다는 뜻이에요. 결과적으로 복제 슬롯 지원이 활성화되면 max_slot_wal_keep_size를 명시적으로 설정할 것을 권장해요. 예를 들어:
# ...
postgresql:
parameters:
max_slot_wal_keep_size: "10GB"
# ...
복제 슬롯 모니터링
복제 슬롯은 인프라에서 주의 깊게 모니터링해야 해요. 기본적으로 Prometheus exporter에서 슬롯 이름, 유형, 활성 여부, 프라이머리로부터의 지연 같은 핵심 정보와 함께 pg_replication_slots 메트릭을 제공해요.
:::note[Monitoring] CloudNativePG 배포를 모니터링하는 방법에 대한 자세한 내용은 "Monitoring" 섹션을 참조하세요. :::