본문으로 건너뛰기
8.5 Connection Pooling

8.5 Connection Pooling

PostgreSQL은 연결 하나마다 별도 프로세스를 띄우므로, 연결 수가 수백·수천으로 늘면 메모리와 컨텍스트 스위칭 비용이 급격히 커진다. 짧게 붙었다 떨어지는 연결이 많은 애플리케이션이라면 앞단에 connection pooler를 두어, 실제 데이터베이스 연결 수를 일정하게 억제하는 편이 낫다. CloudNativePG는 Pooler라는 CRD로 PgBouncer를 선언적으로 배포해 이 계층을 관리한다. 이 절에서는 Pooler의 구조와 pool 모드, 인증 방식, 운영 시 주의점을 정리한다.

아키텍처: 앞단의 PgBouncer

Pooler를 만들면 Operator가 PgBouncer Pod를 띄우고, 그 앞에 별도 서비스를 만든다. 애플리케이션은 Cluster 서비스가 아니라 이 Pooler 서비스로 접속하고, PgBouncer가 소수의 실제 연결을 재사용하며 뒤쪽 PostgreSQL로 중계한다.

    flowchart TD
  APP["애플리케이션<br/>연결 다수"]
  PB["PgBouncer<br/>(Pooler)"]
  RW["-rw 서비스"]
  PG["PostgreSQL<br/>연결 소수"]

  APP -->|많은 연결| PB
  PB -->|풀링된 연결| RW --> PG

  classDef node fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
  class APP,PB,RW,PG node
  
PgBouncer는 1.19 이상이 필요하다. CloudNativePG가 인증에 쓰는 auth_dbname 기능이 그 버전부터 지원되기 때문이다.

기본 정의

가장 단순한 Pooler는 대상 Cluster, 인스턴스 수, 연결 타입, pool 모드만 지정하면 된다.

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: pooler-example-rw
spec:
  cluster:
    name: cluster-example
  instances: 3
  type: rw
  pgbouncer:
    poolMode: session
    parameters:
      max_client_conn: "1000"
      default_pool_size: "10"

핵심 필드는 다음과 같다.

필드의미
cluster.name대상 PostgreSQL 클러스터 (생성 후 변경 불가)
instancesPgBouncer Pod 수
typerw(primary) 또는 ro(replica)
pgbouncer.poolModesession·transaction·statement
pgbouncer.parametersPgBouncer 세부 설정 map
paused연결 일시 정지/재개 (기본 false)

위 예시에서 max_client_conn: 1000은 PgBouncer가 클라이언트로부터 받는 최대 연결 수, default_pool_size: 10은 뒤쪽 PostgreSQL로 실제 유지하는 연결 수다. 1000개의 클라이언트 연결을 10개의 데이터베이스 연결로 접는 셈이다.

pool 모드 고르기

pool 모드는 데이터베이스 연결을 언제 회수해 재사용할지를 정한다.

모드연결 반환 시점특징
session클라이언트가 연결을 끊을 때가장 안전, 풀링 효과는 작음
transaction트랜잭션이 끝날 때균형점. 가장 널리 쓰임
statementstatement 단위최대 다중화, 제약 많음
transaction·statement 모드에서는 세션 단위 상태가 유지되지 않는다. prepared statement, 세션 수준 SET, advisory lock, LISTEN/NOTIFY 등을 쓰는 애플리케이션은 예기치 못한 동작을 겪을 수 있다. 이 기능들을 쓴다면 session 모드를 쓰거나 애플리케이션 쪽에서 상태 의존을 없애야 한다.

인증: 자동 통합

기본값에서 CloudNativePG는 PgBouncer 인증을 자동으로 구성한다. PostgreSQL에 cnpg_pooler_pgbouncer 롤을 만들고, 비밀번호를 조회하는 SECURITY DEFINER 함수를 배포한 뒤, PgBouncer가 그 함수로 인증하도록 auth_user·auth_dbname을 설정한다. TLS 인증서도 Cluster 것을 재사용한다.

CREATE ROLE cnpg_pooler_pgbouncer WITH LOGIN;
GRANT CONNECT ON DATABASE postgres TO cnpg_pooler_pgbouncer;
CREATE OR REPLACE FUNCTION public.user_search(uname TEXT)
  RETURNS TABLE (usename name, passwd text)
  LANGUAGE sql SECURITY DEFINER
  SET search_path = pg_catalog, pg_temp AS
  'SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE usename=$1;';
REVOKE ALL ON FUNCTION public.user_search(text) FROM public;
GRANT EXECUTE ON FUNCTION public.user_search(text) TO cnpg_pooler_pgbouncer;

커스텀 인증서 Secret을 직접 지정하면 이 자동 통합이 꺼지고, 인증 구성 전체를 사용자가 책임진다.

고가용성

여러 PgBouncer Pod를 서로 다른 노드에 흩어 두려면 instances를 늘리고 pod anti-affinity를 건다.

spec:
  instances: 3
  type: rw
  template:
    metadata:
      labels:
        app: pooler
    spec:
      containers: []
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values: ["pooler"]
            topologyKey: kubernetes.io/hostname

모니터링

PgBouncer exporter는 9127 포트로 메트릭을 노출한다(cnpg_pgbouncer_pools_*, cnpg_pgbouncer_stats_* 등). Prometheus로 수집하려면 PodMonitor를 직접 만든다.

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: pooler-example-rw
spec:
  selector:
    matchLabels:
      cnpg.io/poolerName: pooler-example-rw
  podMetricsEndpoints:
  - port: metrics
spec.monitoring.enablePodMonitor 필드는 deprecated 되었다. 모니터링 구성을 온전히 통제하기 위해 PodMonitor를 직접 만드는 방식이 권장된다.

연결 일시 정지

계획된 switchover나 유지보수 때는 paused로 연결을 잠시 멈출 수 있다.

spec:
  paused: true   # 활성 연결을 닫고 새 연결을 보류

false로 되돌리면 RESUME이 실행되어 연결이 다시 열린다. Pooler의 상태는 status.phase(active·paused·inactive·failed)로 확인한다.

제약

  • 하나의 Pooler는 하나의 Cluster에만 붙는다. 여러 Cluster를 걸치는 Pooler는 만들 수 없다.
  • spec.cluster는 생성 후 변경 불가 — 다른 곳을 가리키려면 새 Pooler를 만든다.
  • Pooler 이름은 같은 namespace의 Cluster 이름과 달라야 한다.
  • Cluster를 지워도 Pooler는 자동 삭제되지 않고, Pooler를 지워도 Cluster는 영향받지 않는다.

정리

  • Pooler CRD가 PgBouncer를 선언적으로 배포해 실제 DB 연결 수를 억제한다
  • type으로 rw/ro를 고르고, poolMode로 연결 회수 시점을 정한다
  • transaction/statement 모드는 세션 상태를 유지하지 않으므로 주의
  • 인증·TLS는 기본 자동 통합(cnpg_pooler_pgbouncer + user_search)
  • HA는 instances + anti-affinity, 모니터링은 PodMonitor 직접 생성
  • 하나의 Pooler는 하나의 Cluster 전용, spec.cluster는 변경 불가

이것으로 Part VIII 보안과 접속을 마친다. 데이터베이스를 감싸는 방어선부터 애플리케이션의 접속 경로까지, 계층별로 무엇을 지키고 어디로 연결하는지를 정리했다.