PgBouncer (CNPG Pooler)
Lumie의 PostgreSQL 커넥션 풀링은 CloudNativePG 네이티브 Pooler CRD로 제공됩니다. 이전에 사용하던 독 립 배포 icoretech/pgbouncer Helm 차트는 완전히 폐기(Phase D, 2026-05-25)되었으며, storage/kustomization.yaml에서도 제거되었습니다.
CNPG Pooler는 PgBouncer 엔진을 내장하여, 별도의 userlist Secret 없이 오퍼레이터가 cnpg_pooler_pgbouncer 사용자와 user_search() 함수를 클러스터에 자동으로 생성합니다.
개요
주요 변경 사항 (Phase D)
| 항목 | 이전 (폐기) | 현재 |
|---|---|---|
| 배포 방식 | icoretech/pgbouncer Helm 차트 | CNPG Pooler CRD |
| userlist 관리 | pgbouncer-userlist Secret (Vault VSS) | 오퍼레이터 자동 생성 (cnpg_pooler_pgbouncer) |
| 관리자 자격 증명 Secret | pgbouncer-db-secrets (Vault VSS) | 불필요 (오퍼레이터 내장) |
| 네임스페이스 | lumie-backend | lumie-db (prod), lumie-dev (dev) |
아키텍처
Pooler 매니페스트
프로덕션 (lumie-pooler, namespace: lumie-db)
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: lumie-pooler
namespace: lumie-db
spec:
cluster:
name: lumie-db
instances: 3
type: rw
pgbouncer:
poolMode: session
parameters:
max_client_conn: "200"
default_pool_size: "20"
template:
spec:
containers:
- name: pgbouncer
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
개발 환경 (lumie-dev-pooler, namespace: lumie-dev)
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: lumie-dev-pooler
namespace: lumie-dev
spec:
cluster:
name: lumie-dev-db
instances: 1
type: rw
pgbouncer:
poolMode: session
parameters:
max_client_conn: "100"
default_pool_size: "20"
풀링 모드
Session 모드 선택 이유
멀티테넌트 애플리케이션에서 각 테넌트는 RLS(Row-Level Security) 컨텍스트(SET LOCAL app.tenant_id)를 세션 내에서 유지해야 합니다.
Session 모드를 사용하는 이유:
SET LOCAL등 세션 상태 변경이 커넥션 재사용 시 올바르게 격리됨- Transaction 모드에서는 트랜잭션 종료 시 세션 상태가 초기화되어 RLS 컨텍스트 유실 위험
- 멀티테넌트 격리(
TenantContextHolder)를 위해 세션 상태 유지가 필수
풀링 모드 비교
| 모드 | 장점 | 단점 | 사용 사례 |
|---|---|---|---|
| Session | 세션 상태 유지, 모든 SQL 지원 | 커넥션 재사용률 낮음 | 멀티테넌트, RLS 의존 애플리케이션 |
| Transaction | 높은 커넥션 재사용률 | 세션 상태 초기화 | 단순한 CRUD 애플리케이션 |
| Statement | 최고 성능 | 제한된 SQL 지원 | 읽기 전용 워크로드 |
연결 엔드포인트
| 환경 | 엔드포인트 | 포트 | 대상 클러스터 |
|---|---|---|---|
| 프로덕션 (RW) | lumie-pooler.lumie-db.svc.cluster.local | 5432 | lumie-db Primary |
| 개발 (RW) | lumie-dev-pooler.lumie-dev.svc.cluster.local | 5432 | lumie-dev-db Primary |
| 프로덕션 (RO, 직접) | lumie-db-ro.lumie-db.svc | 5432 | lumie-db Replica (AI 모듈) |
프로덕션 백엔드의 DB_HOST 환경변수는 lumie-pooler.lumie-db.svc.cluster.local로 설정됩니다. 읽기 전용 경로(RO_DB_HOST)는 Pooler를 거치지 않고 lumie-db-ro.lumie-db.svc에 직접 연결합니다.
DB 역할 및 자격 증명
CNPG Pooler는 두 종류의 DB 역할을 중개합니다.
| 역할 | 속성 | 용도 | 자격 증명 출처 |
|---|---|---|---|
lumie_app | NOSUPERUSER NOBYPASSRLS | HikariCP 런타임 풀 (모든 DML) | Vault infrastructure/lumie-db → LUMIE_APP_USER/PASSWORD |
postgres | SUPERUSER | Flyway 마이그레이션 전용 | Vault infrastructure/lumie-db → POSTGRES_USER/PASSWORD |
lumie_app: RLS 정책이 완전히 적용됩니다.BYPASSRLS=false이므로 테넌트 데이터 격리가 보장됩니다.postgres: Flyway 마이그레이션에서만 사용합니다. 런타임 HikariCP 풀에서는 절대 사용하지 않습니다.bluemayne역할: 완전 폐기되었습니다. 참조하지 마십시오.cnpg_pooler_pgbouncer: CNPG 오퍼레이터가 자동으로 생성하는 내부 인증 사용자입니다. Vault에 미러링하지 않습니다.
lumie_app 초기화 (postInitSQL)
클러스터 부트스트랩 시 postInitApplicationSQLRefs를 통해 lumie_app 역할을 생성하고 권한을 부여합니다.
CREATE ROLE lumie_app WITH LOGIN NOSUPERUSER NOBYPASSRLS
NOCREATEDB NOCREATEROLE PASSWORD '...';
GRANT CONNECT ON DATABASE lumie TO lumie_app;
GRANT USAGE ON SCHEMA public TO lumie_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO lumie_app;
ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO lumie_app;
인증 방식
CNPG Pooler는 user_search() 함수를 사용하여 PostgreSQL pg_shadow 카탈로그에서 직접 인증 정보를 조회합니다. 별도의 userlist.txt나 Vault에서 관리하는 userlist Secret이 필요 없습니다.
프로덕션에서 cnpg_pooler_pgbouncer 인증 사용자는 오퍼레이터가 수동으로 생성하며, Vault에는 미러링하지 않습니다. (CNPG issue #3788 참조)
모니터링
연결 상태 확인
# Pooler Pod 상태 확인 (prod)
kubectl get pods -n lumie-db -l cnpg.io/poolerName=lumie-pooler
# Pooler Pod 상태 확인 (dev)
kubectl get pods -n lumie-dev -l cnpg.io/poolerName=lumie-dev-pooler
# Pooler 상세 정보
kubectl describe pooler lumie-pooler -n lumie-db
PgBouncer 관리 콘솔
# Pooler Pod에서 관리 콘솔 접속
kubectl exec -n lumie-db <pooler-pod> -- psql -U cnpg_pooler_pgbouncer pgbouncer
-- 풀 상태 확인
SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;
SHOW SERVERS;
운영 가이드
Pooler 설정 변경
Pooler 설정 변경은 applications/lumie/manifests/pooler.yaml을 수정하고 ArgoCD에 동기화합니다.
# Pooler 현재 상태 확인
kubectl get pooler -n lumie-db
# ArgoCD 동기화 후 롤아웃 확인
kubectl rollout status deployment/lumie-pooler -n lumie-db
풀 크기 조정
pooler.yaml의 spec.pgbouncer.parameters를 수정합니다.
spec:
pgbouncer:
poolMode: session
parameters:
max_client_conn: "300" # 최대 클라이언트 연결 수
default_pool_size: "25" # 풀당 최대 서버 연결 수
문제 해결
Pooler Pod가 시작되지 않음
# 이벤트 확인
kubectl describe pooler lumie-pooler -n lumie-db
# Pod 로그 확인
kubectl logs -n lumie-db -l cnpg.io/poolerName=lumie-pooler
연결 거부
# lumie-db 클러스터 상태 확인 (Pooler는 클러스터 RW 서비스에 의존)
kubectl get cluster lumie-db -n lumie-db
# Pooler 서비스 확인
kubectl get svc -n lumie-db | grep pooler
인증 실패
CNPG Pooler는 user_search() 함수로 인증합니다. 함수가 누락된 경우 오퍼레이터가 클러스터를 재조정(reconcile)하면 자동으로 재생성됩니다.
# user_search 함수 존재 여부 확인
kubectl exec -n lumie-db lumie-db-1 -c postgres -- \
psql -c "\df user_search"