CloudNativePG
CloudNativePG(CNPG)는 Kubernetes Native PostgreSQL 오퍼레이터입니다. PostgreSQL 인스턴스의 프로비저닝, 고가용성 복제, 자동 백업, 페일오버를 Kubernetes CRD로 선언적으로 관리합니다.
개요
주요 기능
| 기능 | 설명 |
|---|---|
| PostgreSQL 버전 | 18.1 (wal2json 플러그인 포함) |
| 고가용성 | Primary-Replica 스트리밍 복제 |
| 자동 페일오버 | Primary 장애 시 Replica 자동 승격 |
| 백업 | WAL 아카이브 + 베이스백업 → MinIO |
| 모니터링 | Prometheus 메트릭 및 PodMonitor |
아키텍처
설치 및 구성
Helm 차트 설정
# CloudNativePG Operator
image:
repository: zot.lumie-infra.com/cloudnative-pg/cloudnative-pg
tag: "1.28.1"
pullPolicy: IfNotPresent
replicaCount: 1
resources:
requests:
cpu: 15m
memory: 100Mi
limits:
memory: 100Mi
monitoring:
podMonitorEnabled: true
grafanaDashboard:
create: false # 수동 임포트 (Grafana.com ID: 20417)
webhook:
port: 9443
mutating:
create: true
validating:
create: true
priorityClassName: high-priority
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: cloudnative-pg
topologyKey: kubernetes.io/hostname
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10001
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
스토리지 클래스
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path-retain
provisioner: rancher.io/local-path
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
특징:
- Retain 정책: PVC 삭제 시에도 데이터 보존
- WaitForFirstConsumer: Pod 스케줄링 후 볼륨 바인딩
- Local Path: 각 노 드의 로컬 디스크 사용
PostgreSQL 클러스터
infra-db 클러스터
공유 인프라 서비스용 PostgreSQL 클러스터입니다.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: infra-db
spec:
instances: 3
imageName: zot.lumie-infra.com/storage/postgresql-wal2json:18.1
bootstrap:
initdb:
database: teleport
owner: postgres
secret:
name: infra-db-bootstrap-secret
postInitSQL:
- "CREATE USER grafana WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE grafana OWNER grafana"
- "CREATE USER umami WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE umami OWNER umami"
- "CREATE USER keycloak WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE keycloak OWNER keycloak"
- "CREATE USER openclaw WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE openclaw OWNER openclaw"
- "CREATE USER coder WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE coder OWNER coder"
- "CREATE USER gitea WITH PASSWORD 'VAULT_PASSWORD'"
- "CREATE DATABASE gitea OWNER gitea"
storage:
storageClass: local-path-retain
size: 2Gi
resources:
requests:
memory: "384Mi"
cpu: "50m"
limits:
memory: "384Mi"
enableSuperuserAccess: true
postgresql:
parameters:
wal_level: "logical"
max_replication_slots: "8"
affinity:
enablePodAntiAffinity: true
topologyKey: kubernetes.io/hostname
podAntiAffinityType: required
monitoring:
enablePodMonitor: true
주요 설정 설명
WAL 설정
wal_level = logical
max_replication_slots = 8
- logical WAL: 논리적 복제 및 CDC(Change Data Capture) 지원
- replication_slots: 최대 8개의 복제 슬롯 허용
안티 어피니티
affinity:
enablePodAntiAffinity: true
topologyKey: kubernetes.io/hostname
podAntiAffinityType: required
- required: 각 PostgreSQL 인스턴스를 서로 다른 노드에 배치 (강제)
- topologyKey: 노드 단위로 분산 배치
데이터베이스 관리
연결 정보
| 서비스 | 엔드포인트 | 포트 | 용도 |
|---|---|---|---|
| Read/Write | infra-db-rw.infra-db.svc.cluster.local | 5432 | Primary 연결 |
| Read Only | infra-db-ro.infra-db.svc.cluster.local | 5432 | Replica 연결 |
| Read/Write (Any) | infra-db-r.infra-db.svc.cluster.local | 5432 | 모든 인스턴스 |
사용자 및 데이터베이스
기본 사용자
-- 슈퍼유저 (CNPG 오퍼레이터 관리, Teleport 기본 DB 소유자)
postgres (owner of teleport database)
-- 서비스별 사용자
grafana (owner of grafana database)
umami (owner of umami database)
keycloak (owner of keycloak database)
openclaw (owner of openclaw database)
coder (owner of coder database)
gitea (owner of gitea database)
권한 관리
-- 새 서비스 사용자 추가 예시
CREATE USER newservice WITH PASSWORD 'secure_password';
CREATE DATABASE newservice OWNER newservice;
GRANT CONNECT ON DATABASE newservice TO newservice;
백업 및 복구
WAL 아카이브
CloudNativePG는 자동으로 WAL(Write-Ahead Log) 파일을 Cloudflare R2에 아카이브합니다. infra-db 클러스터는 R2 Barman Object Store를 사용하며, 보존 기간은 365일입니다.
backup:
barmanObjectStore:
destinationPath: "s3://lumie-dr/cnpg/infra-db"
endpointURL: "https://a0884dfaf71a61fe7cb952fb9d69eb68.r2.cloudflarestorage.com"
s3Credentials:
accessKeyId:
name: r2-barman-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: r2-barman-creds
key: ACCESS_SECRET_KEY
wal:
compression: gzip
maxParallel: 4
data:
compression: gzip
retentionPolicy: "365d"
앱 클러스터(lumie-db, lumie-dev-db)도 동일한 R2 Barman 구성으로 백업됩니다. 자세한 내용은 infra-db 문서를 참조하세요.
복구 절차
1. Point-in-Time Recovery (PITR)
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: infra-db-restored
spec:
instances: 3
bootstrap:
recovery:
source: infra-db-backup
recoveryTarget:
targetTime: "2024-01-15 10:30:00"
externalClusters:
- name: infra-db-backup
barmanObjectStore:
destinationPath: s3://cnpg-backups/infra-db
endpointURL: http://minio.minio.svc.cluster.local:9000
2. 베이스백업에서 복구
# 사용 가능한 백업 확인
kubectl get backup -n infra-db
# 특정 백업에서 복구
kubectl apply -f - <<EOF
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: infra-db-restored
namespace: infra-db
spec:
instances: 3
bootstrap:
recovery:
backup:
name: backup-20240115-020000
EOF
모니터링
Prometheus 메트릭
CloudNativePG는 다음 메트릭을 제공합니다:
| 메트릭 | 설명 |
|---|---|
cnpg_pg_up | PostgreSQL 인스턴스 상태 |
cnpg_pg_replication_lag | 복제 지연 시간 |
cnpg_pg_database_size_bytes | 데이터베이스 크기 |
cnpg_pg_wal_files | WAL 파일 수 |
cnpg_backup_duration_seconds | 백업 소요 시간 |
PodMonitor 설정
monitoring:
enablePodMonitor: true
이 설정으로 Prometheus가 자동으로 PostgreSQL 메트릭을 수집합니다.
Grafana 대시보드
대시보드 ID: 20417 (Grafana.com) URL: https://grafana.com/grafana/dashboards/20417-cloudnativepg/
수동으로 임포트하여 사용합니다 (ConfigMap 크기 제한으로 자동 생성 비활성화).
운영 가이드
클러스터 상태 확인
# 클러스터 상태 조회
kubectl get cluster -n infra-db
# 상세 정보 확인
kubectl describe cluster infra-db -n infra-db
# Pod 상태 확인
kubectl get pods -n infra-db -l postgresql=infra-db
페일오버 테스트
# Primary Pod 삭제 (자동 페일오버 테스트)
kubectl delete pod infra-db-1 -n infra-db
# 페일오버 상태 확인
kubectl get cluster infra-db -n infra-db -o yaml | grep -A 10 status
스케일링
# 인스턴스 수 변경 (4 → 5)
kubectl patch cluster infra-db -n infra-db --type='merge' -p='{"spec":{"instances":5}}'
# 스케일링 진행 상황 확인
kubectl get pods -n infra-db -w
로그 확인
# Primary 로그 확인
kubectl logs infra-db-1 -n infra-db -c postgres
# Operator 로그 확인
kubectl logs -n cnpg -l app.kubernetes.io/name=cloudnative-pg
보안
DB 역할 분리 (RLS 필수)
앱 클러스터(lumie-db, lumie-dev-db)는 두 역할을 명확히 분리합니다.
| 역할 | 속성 | 용도 |
|---|---|---|
lumie_app | NOSUPERUSER NOBYPASSRLS | HikariCP 런타임 연결 풀 — RLS 정책 완전 적용 |
postgres | SUPERUSER | Flyway 마이그레이션 전용 |
NOSUPERUSER NOBYPASSRLS가 설정된 lumie_app 역할을 사용해야 테넌트 격리 RLS 정책이 올바르게 동작합니다. 런타임 연결에서 postgres SUPERUSER를 사용하면 모든 RLS 정책이 우회됩니다.
lumie_app의 비밀번호는 Vault infrastructure/lumie-db 경로에서 관리됩니다 (CNPG 오퍼레이터가 생성하지 않음 — CNPG #3788).
CNPG 오퍼레이터가 생성하는 *-superuser Secret은 Vault에 미러링하지 않습니다 (오퍼레이터가 소유권을 가짐).
인증 및 권한
- 슈퍼유저 접근:
enableSuperuserAccess: true로 활성화 - 자격 증명: Vault Static Secret을 통해 관리
- 네트워크: 클러스터 내부에서만 접근 가능
TLS 암호화
# TLS 설정 (향후 구성)
certificates:
serverTLSSecret: "infra-db-server-tls"
serverCASecret: "infra-db-ca-tls"
clientCASecret: "infra-db-client-ca-tls"