CloudNativePG 복제 클러스터 구축 가이드: Barman Cloud와 AWS S3 백업·WAL 아카이빙
작성자: Swapnil Suryawanshi
게시일: 2026년 4월 15일
이 가이드에서는 CloudNativePG 복제 클러스터를 구축하고 백업 및 WAL 아카이빙을 구성하는 방법을 단계별로 설명합니다. Barman Cloud 플러그인과 AWS S3를 활용해 EDB Postgres® AI for CloudNativePG 환경에서 프라이머리 클러스터를 백업하고, 해당 백업을 기반으로 복제 클러스터를 구성한 뒤 실제 데이터 복제 상태까지 검증합니다.
환경 세부 정보
- 오퍼레이터(Operator): EDB Postgres® AI for CloudNativePG 1.28.1
- 데이터베이스(Database): EDB Postgres Advanced Server 18 (EPAS)
- 백업 설정(Backup Setup): Barman Cloud Plugin v0.11.0
- 스토리지(Storage): AWS S3
1. 사전 요구 사항: 네임스페이스 및 자격 증명 생성
먼저 프라이머리(Primary) 및 레플리카(Replica) 클러스터를 위한 별도의 네임스페이스를 생성하고, Barman 플러그인이 오브젝트 스토어에 액세스하는 데 필요한 S3 자격 증명을 저장합니다.
Bash
user% kubectl create ns primary
namespace/primary created
user% kubectl create ns replica
namespace/replica created
프라이머리 네임스페이스에 동일한 AWS 자격 증명을 생성합니다.
Bash
user% kubectl create secret generic aws-creds \
--from-literal=ACCESS_KEY_ID=xxxxxxxxN3GE5FSxxxxxx \
--from-literal=ACCESS_SECRET_KEY=xxxxxxxxGrS+xlfTlCZTaTxxxxxx -n primary
secret/aws-creds created
레플리카 네임스페이스에 동일한 AWS 자격 증명을 생성합니다.
Bash
user% kubectl create secret generic aws-creds \
--from-literal=ACCESS_KEY_ID=xxxxxxxxN3GE5FSxxxxxx \
--from-literal=ACCESS_SECRET_KEY=xxxxxxxxGrS+xlfTlCZTaTxxxxxx -n replica
secret/aws-creds created
2. Barman Cloud 플러그인 설정
Barman 플러그인은 안전한 통신을 위해 cert-manager가 필요합니다. cmctl 설치를 확인하고 cert-manager를 설치한 후 Barman Cloud 플러그인을 배포합니다.
Bash
user% brew install cmctl
==> Auto-updating Homebrew...
Adjust how often this is run with `$HOMEBREW_AUTO_UPDATE_SECS` or disable with
`$HOMEBREW_NO_AUTO_UPDATE=1`. Hide these hints with `$HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
==> New Formulae
:
:notchi: Notch companion for Claude Code
nvidia-sync: Utility for launching applications and containers on remote Linux systems
You have 30 outdated formulae and 1 outdated cask installed.
installed
Bash
user% kubectl create namespace cert-manager
namespace/cert-manager created
user% kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
Warning: resource namespaces/cert-manager is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
namespace/cert-manager configured
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io unchanged
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io unchanged
serviceaccount/cert-manager-cainjector created
serviceaccount/cert-manager created
serviceaccount/cert-manager-webhook created
clusterrole.rbac.authorization.k8s.io/cert-manager-cainjector created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrole.rbac.authorization.k8s.io/cert-manager-cluster-view created
clusterrole.rbac.authorization.k8s.io/cert-manager-view created
clusterrole.rbac.authorization.k8s.io/cert-manager-edit created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-approve:cert-manager-io created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-certificatesigningrequests created
clusterrole.rbac.authorization.k8s.io/cert-manager-webhook:subjectaccessreviews created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-cainjector created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-approve:cert-manager-io created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-certificatesigningrequests created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-webhook:subjectaccessreviews created
role.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
role.rbac.authorization.k8s.io/cert-manager:leaderelection created
role.rbac.authorization.k8s.io/cert-manager-tokenrequest created
role.rbac.authorization.k8s.io/cert-manager-webhook:dynamic-serving created
rolebinding.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager-tokenrequest created
rolebinding.rbac.authorization.k8s.io/cert-manager-webhook:dynamic-serving created
service/cert-manager-cainjector created
service/cert-manager created
service/cert-manager-webhook created
deployment.apps/cert-manager-cainjector created
deployment.apps/cert-manager created
deployment.apps/cert-manager-webhook created
mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
Bash
user% kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-6bfcb455c7-q75dr 1/1 Running 1 (37m ago) 107m
cert-manager-cainjector-84d45cd8f4-j4zvg 1/1 Running 0 107m
cert-manager-webhook-5bb447875c-lsn7s 1/1 Running 0 107m
user% cmctl check api The cer
t-manager API is ready
오퍼레이터의 네임스페이스에 플러그인 매니페스트를 적용합니다.
Bash
user% kubectl apply -f https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v0.11.0/manifest.yaml
customresourcedefinition.apiextensions.k8s.io/objectstores.barmancloud.cnpg.io created
serviceaccount/plugin-barman-cloud created
role.rbac.authorization.k8s.io/barman-plugin-leader-election-role created
clusterrole.rbac.authorization.k8s.io/barman-plugin-metrics-auth-role created
clusterrole.rbac.authorization.k8s.io/barman-plugin-metrics-reader created
clusterrole.rbac.authorization.k8s.io/barman-plugin-objectstore-editor-role created
clusterrole.rbac.authorization.k8s.io/barman-plugin-objectstore-viewer-role created
clusterrole.rbac.authorization.k8s.io/plugin-barman-cloud created
rolebinding.rbac.authorization.k8s.io/barman-plugin-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/barman-plugin-metrics-auth-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/plugin-barman-cloud-binding created
secret/plugin-barman-cloud-52ggkcd52d created
service/barman-cloud created
deployment.apps/barman-cloud created
certificate.cert-manager.io/barman-cloud-client created
certificate.cert-manager.io/barman-cloud-server created
issuer.cert-manager.io/selfsigned-issuer created
user% kubectl rollout status deployment -n postgresql-operator-system barman-cloud
deployment "barman-cloud" successfully rolled out
3. ObjectStore 리소스 생성
두 네임스페이스 모두에 ObjectStore 리소스를 정의합니다. 이는 플러그인에게 백업 및 WAL 파일을 저장하고 검색할 위치를 알려줍니다.
프라이머리 네임스페이스에 프라이머리 클러스터용 ObjectStore를 생성합니다.
YAML
user% vi ObjectStore-primary.yaml
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: s3-store
namespace: primary # Also apply a copy to the 'replica' namespace
spec:
configuration:
destinationPath: s3://swapnil-backup/cnp/
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: ACCESS_SECRET_KEY
wal:
compression: gzip
Bash
user% kubectl apply -f ObjectStore-primary.yaml
objectstore.barmancloud.cnpg.io/s3-store created
레플리카 네임스페이스에 레플리카 클러스터용 ObjectStore를 생성합니다.
YAML
user% vi ObjectStore-replica.yaml
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: s3-store
namespace: replica
spec:
configuration:
destinationPath: s3://swapnil-backup/cnp/
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: ACCESS_SECRET_KEY
wal:
compression: gzip
Bash
user% kubectl apply -f ObjectStore-replica.yaml
objectstore.barmancloud.cnpg.io/s3-store created
4. 프라이머리 클러스터 배포 및 WAL 아카이빙 검증
프라이머리 네임스페이스에 프라이머리 클러스터를 배포하고 Barman 플러그인을 통해 WAL 아카이빙 상태를 확인합니다.
YAML
user% vi cluster-primary.yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Cluster
metadata:
name: cluster-primary
namespace: primary
spec:
instances: 3
imageName: docker.enterprisedb.com/k8s/edb-postgres-advanced:18-standard-ubi9
primaryUpdateStrategy: unsupervised
storage:
size: 1G
replica:
primary: cluster-primary
source: cluster-primary
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: s3-store
externalClusters:
- name: cluster-primary
plugin:
name: barman-cloud.cloudnative-pg.io
parameters:
barmanObjectName: s3-store
serverName: cluster-primary
- name: cluster-replica
plugin:
name: barman-cloud.cloudnative-pg.io
parameters:
barmanObjectName: s3-store
serverName: cluster-replica
Bash
user% kubectl apply -f cluster-primary.yaml
cluster.postgresql.k8s.enterprisedb.io/cluster-primary created
user% kubectl get pods -L role -n primary
NAME READY STATUS RESTARTS AGE ROLE
cluster-primary-1 2/2 Running 0 3m11s primary
cluster-primary-2 2/2 Running 0 2m10s replica
cluster-primary-3 2/2 Running 0 102s replica
CNP 클러스터 상태를 확인하고 WAL 아카이빙이 “OK” 상태인지 검증합니다.
Bash
user% kubectl cnp status cluster-primary -n primary
Cluster Summary
Name primary/cluster-primary
System ID: 7621517035557347356
PostgreSQL Image: docker.enterprisedb.com/k8s/edb-postgres-advanced:18-standard-ubi9
Primary instance: cluster-primary-1
Primary promotion time: 2026-03-26 10:59:23 +0000 UTC (3m8s)
Status: Cluster in healthy state
Instances: 3
Ready instances: 3
Size: 169M
Current Write LSN: 0/8000060 (Timeline: 1 - WAL File: 000000010000000000000008)
Continuous Backup status (Barman Cloud Plugin)
No recovery window information found in ObjectStore 's3-store' for server 'cluster-primary'
Working WAL archiving: OK
WALs waiting to be archived: 0
Last Archived WAL: 000000010000000000000007.00000028.backup @ 2026-03-26T11:00:11.577402Z
Last Failed WAL: -
Streaming Replication status
Replication Slots Enabled
Name Sent LSN Write LSN Flush LSN Replay LSN Write Lag Flush Lag Replay Lag State Sync State Sync Priority Replication Slot
---- -------- --------- --------- ---------- --------- --------- ---------- ----- ---------- ------------- ----------------
cluster-primary-2 0/8000060 0/8000060 0/8000060 0/8000060 00:00:00 00:00:00 00:00:00 streaming async 0 active
cluster-primary-3 0/8000060 0/8000060 0/8000060 0/8000060 00:00:00 00:00:00 00:00:00 streaming async 0 active
Instances status
Name Current LSN Replication role Status QoS Manager Version Node
---- ----------- ---------------- ------ --- --------------- ----
cluster-primary-1 0/8000060 Primary OK BestEffort 1.28.1 replicacluster-control-plane
cluster-primary-2 0/8000060 Standby (async) OK BestEffort 1.28.1 replicacluster-control-plane
cluster-primary-3 0/8000060 Standby (async) OK BestEffort 1.28.1 replicacluster-control-plane
Plugins status
Name Version Status Reported Operator Capabilities
---- ------- ------ ------------------------------
barman-cloud.cloudnative-pg.io 0.11.0 N/A Reconciler Hooks, Lifecycle Service
5. Barman Cloud를 이용한 수동 백업 실행
복제 클러스터 부트스트랩을 위해 S3 오브젝트 스토어에 데이터가 채워지도록 Barman 플러그인을 통해 수동 백업을 수행합니다.
플러그인 방식을 사용하여 백업을 트리거합니다.
Bash
user% kubectl cnp backup cluster-primary --method=plugin --plugin-name=barman-cloud.cloudnative-pg.io -n primary
backup/cluster-primary-20260326163523 created
백업 완료 여부를 확인합니다.
Bash
user% kubectl get backup -n primary
NAME AGE CLUSTER METHOD PHASE ERROR
cluster-primary-20260326163523 33s cluster-primary plugin completed
복구 가능한 첫 번째 지점(First Point of Recoverability)과 마지막 성공 백업을 검증합니다.
Bash
user% kubectl cnp status cluster-primary -n primary
Cluster Summary
Name primary/cluster-primary
System ID: 7621517035557347356
PostgreSQL Image: docker.enterprisedb.com/k8s/edb-postgres-advanced:18-standard-ubi9
Primary instance: cluster-primary-1
Primary promotion time: 2026-03-26 10:59:23 +0000 UTC (7m27s)
Status: Cluster in healthy state
:
:
Continuous Backup status (Barman Cloud Plugin)
ObjectStore / Server name: s3-store/cluster-primary
First Point of Recoverability: 2026-03-26 16:35:25 IST
Last Successful Backup: 2026-03-26 16:35:25 IST
Last Failed Backup: -
Working WAL archiving: OK
WALs waiting to be archived: 0
Last Archived WAL: 000000010000000000000008 @ 2026-03-26T11:05:08.855493Z
Last Failed WAL: -
6. CloudNativePG 복제 클러스터 배포 및 검증
복제 클러스터는 프라이머리의 오브젝트 스토어에서 복구(Recovery)를 통해 부트스트랩되도록 구성된 후 동기화를 유지합니다. 복제 클러스터는 복구를 위해 cluster-primary 소스를 가리킵니다.
YAML
user% vi cluster-replica.yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Cluster
metadata:
name: cluster-replica
namespace: replica
spec:
instances: 3
imageName: docker.enterprisedb.com/k8s/edb-postgres-advanced:18-standard-ubi9
storage:
size: 1G
bootstrap:
recovery:
source: cluster-primary
replica:
primary: cluster-primary
source: cluster-primary
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: s3-store
externalClusters:
- name: cluster-primary
plugin:
name: barman-cloud.cloudnative-pg.io
parameters:
barmanObjectName: s3-store
serverName: cluster-primary
- name: cluster-replica
plugin:
name: barman-cloud.cloudnative-pg.io
parameters:
barmanObjectName: s3-store
serverName: cluster-replica
Bash
user% kubectl apply -f cluster-replica.yaml
cluster.postgresql.k8s.enterprisedb.io/cluster-replica created
user% kubectl get pods -L role -n replica
NAME READY STATUS RESTARTS AGE ROLE
cluster-replica-1 2/2 Running 0 116s primary
cluster-replica-2 2/2 Running 0 88s replica
cluster-replica-3 2/2 Running 0 60s replica
복제 상태와 소스 클러스터 연결을 확인합니다.
Bash
user% kubectl cnp status cluster-replica -n replica
Replica Cluster Summary
Name replica/cluster-replica
System ID: 7621517035557347356
PostgreSQL Image: docker.enterprisedb.com/k8s/edb-postgres-advanced:18-standard-ubi9
Designated primary: cluster-replica-1
Source cluster: cluster-primary
Primary promotion time: 2026-03-26 11:08:43 +0000 UTC (2m8s)
Status: Cluster in healthy state
Instances: 3
Ready instances: 3
Size: 88M
Continuous Backup status (Barman Cloud Plugin)
No recovery window information found in ObjectStore 's3-store' for server 'cluster-replica'
Working WAL archiving: OK
WALs waiting to be archived: 0
Last Archived WAL: 000000010000000000000008 @ 2026-03-26T11:08:49.722535Z
Last Failed WAL: -
Instances status
Name Current LSN Replication role Status QoS Manager Version Node
---- ----------- ---------------- ------ --- --------------- ----
cluster-replica-1 0/9000000 Designated primary OK BestEffort 1.28.1 replicacluster-control-plane
cluster-replica-2 0/9000000 Standby (in Replica Cluster) OK BestEffort 1.28.1 replicacluster-control-plane
cluster-replica-3 0/9000000 Standby (in Replica Cluster) OK BestEffort 1.28.1 replicacluster-control-plane
Plugins status
Name Version Status Reported Operator Capabilities
---- ------- ------ ------------------------------
barman-cloud.cloudnative-pg.io 0.11.0 N/A Reconciler Hooks, Lifecycle Service
7. PostgreSQL 데이터 복제 검증
프라이머리 클러스터에 데이터를 생성하고 복제 클러스터로 올바르게 복제되는지 확인합니다.
프라이머리 읽기/쓰기 테스트
Bash
user% kubectl cnp psql cluster-primary -n primary
psql (18.3.0)
Type "help" for help.
postgres=# create table test(id int);
CREATE TABLE
postgres=#
postgres=# insert into test values (1);
INSERT 0 1
postgres=# select * from test;
id
----
1
(1 row)
postgres=# checkpoint;
CHECKPOINT
postgres=# select * from pg_switch_wal();
pg_switch_wal
---------------
0/9020208
(1 row)
postgres=# select * from pg_switch_wal();
pg_switch_wal
---------------
0/A000078
(1 row)
레플리카 읽기 테스트
Bash
user% kubectl cnp psql cluster-replica -n replica
psql (18.3.0)
Type "help" for help.
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | test | table | postgres
(1 row)
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | test | table | postgres
(1 row)
postgres=# select * from test;
id
----
1
(1 row)
결론
primary 네임스페이스의 cluster-primary에서 부트스트랩되어 replica 네임스페이스에 cluster-replica를 성공적으로 배포한 것은 CloudNativePG 환경 내에서 Barman Cloud 플러그인의 뛰어난 견고성을 입증합니다.
전체 바이너리 백업과 지속적인 WAL 아카이빙을 위해 S3 호환 스토리지를 활용함으로써, 초기 단계 동안 프라이머리의 활성 포드에 직접적인 네트워크 종속성 없이 레플리카 클러스터를 초기화하고 동기화할 수 있는 분리된 아키텍처(Decoupled Architecture)를 달성했습니다.
고급 운영: 라이프사이클 관리
이제 분산 토폴로지가 설정되고 Barman Cloud 플러그인을 통해 데이터가 동기화되므로 클러스터 간의 트래픽을 수동 또는 자동으로 전환할 수 있습니다.
다음 단계: 복제 클러스터를 프라이머리 상태로 승격시키고 데이터 손실 없이 기존 프라이머리를 강등시키는 방법은 런북 [분산 토폴로지(K8s)에서 CloudNativePG 복제 클러스터의 스위치오버 및 스위치백 – 2부]를 참조하십시오.
CloudNativePG 복제 클러스터 FAQ
CloudNativePG 복제 클러스터는 어떻게 구성하나요?
이 구성에서는 프라이머리와 레플리카에 각각 별도의 네임스페이스를 생성하고 Barman Cloud 플러그인과 ObjectStore를 설정합니다. 이후 프라이머리 클러스터의 S3 백업을 기반으로 cluster-replica를 복구 방식으로 부트스트랩해 복제 클러스터를 구성합니다.
Barman Cloud 플러그인은 CloudNativePG에서 어떤 역할을 하나요?
이 가이드에서는 Barman Cloud 플러그인을 프라이머리 및 복제 클러스터의 백업과 WAL 아카이빙에 사용합니다. ObjectStore 리소스를 통해 백업과 WAL 파일을 저장하고 검색할 S3 위치를 지정합니다.
CloudNativePG 백업을 AWS S3에 저장할 수 있나요?
예. 이 구성에서는 ObjectStore의 destinationPath에 AWS S3 경로를 지정하고 S3 자격 증명을 연결합니다. 프라이머리 클러스터의 수동 백업과 WAL 파일은 설정된 오브젝트 스토어를 통해 관리됩니다.
PostgreSQL WAL 아카이빙 상태는 어떻게 확인하나요?
kubectl cnp status 명령으로 클러스터 상태를 조회하면 Barman Cloud Plugin의 Continuous Backup 상태를 확인할 수 있습니다. 이 가이드에서는 Working WAL archiving: OK와 대기 중인 WAL 수, 마지막으로 아카이빙된 WAL을 확인해 정상 동작 여부를 검증합니다.
Barman Cloud 백업으로 복제 클러스터를 어떻게 부트스트랩하나요?
먼저 프라이머리 클러스터의 백업을 완료한 뒤 복제 클러스터 설정의 bootstrap.recovery.source에 cluster-primary를 지정합니다. 이후 복제 클러스터를 배포하고 kubectl cnp status 및 실제 읽기 테스트를 통해 소스 클러스터와의 복제 상태를 확인합니다.