본문으로 건너뛰기

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)
관리자 자격 증명 Secretpgbouncer-db-secrets (Vault VSS)불필요 (오퍼레이터 내장)
네임스페이스lumie-backendlumie-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.local5432lumie-db Primary
개발 (RW)lumie-dev-pooler.lumie-dev.svc.cluster.local5432lumie-dev-db Primary
프로덕션 (RO, 직접)lumie-db-ro.lumie-db.svc5432lumie-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_appNOSUPERUSER NOBYPASSRLSHikariCP 런타임 풀 (모든 DML)Vault infrastructure/lumie-dbLUMIE_APP_USER/PASSWORD
postgresSUPERUSERFlyway 마이그레이션 전용Vault infrastructure/lumie-dbPOSTGRES_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.yamlspec.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"