본문으로 건너뛰기
2.3 컨테이너·Kubernetes

2.3 컨테이너·Kubernetes

PostgreSQL을 컨테이너로 띄우는 패턴은 크게 두 갈래다 — 단일 Docker 컨테이너(개발·테스트·CI), Kubernetes operator(운영 클러스터). 공식 Docker 이미지 사용법과 두 주요 operator(CloudNativePG, Zalando) 비교를 정리합니다.

공식 Docker 이미지 — postgres

Docker Hub의 postgres는 PostgreSQL Global Development Group이 직접 관리하는 공식 이미지입니다.

docker run -d \
  --name pg17 \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_USER=app_user \
  -e POSTGRES_DB=app_main \
  -p 5432:5432 \
  -v pg17_data:/var/lib/postgresql/data \
  postgres:17
환경변수의미
POSTGRES_PASSWORD슈퍼유저(기본 postgres) 비밀번호. 필수
POSTGRES_USER슈퍼유저 이름 (기본 postgres)
POSTGRES_DB첫 데이터베이스 이름 (기본 $POSTGRES_USER)
POSTGRES_INITDB_ARGSinitdb 추가 인자 (예: --data-checksums)
POSTGRES_HOST_AUTH_METHOD외부 접속 인증 방식 (기본 scram-sha-256)
PGDATA데이터 디렉토리 위치 (기본 /var/lib/postgresql/data)

초기화 스크립트

/docker-entrypoint-initdb.d/.sql·.sh·.sql.gz를 마운트하면 첫 기동 시 한 번 실행됩니다. 이미 PGDATA에 데이터가 있으면 건너뜁니다.

# docker-compose.yml
services:
  pg:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_INITDB_ARGS: "--data-checksums"
    volumes:
      - ./initdb:/docker-entrypoint-initdb.d:ro
      - pg_data:/var/lib/postgresql/data
    ports: ["5432:5432"]
volumes:
  pg_data:

운영용으로 부족한 부분

공식 이미지는 단일 인스턴스 전용입니다. 운영 클러스터에 필요한 다음을 직접 처리해야 합니다.

  • 자동 페일오버
  • 베이스 백업·WAL 아카이브
  • 복제 슬롯 관리
  • TLS 인증서 회전
  • 모니터링 지표 수집

그래서 운영 환경에서는 operator를 씁니다.

Kubernetes operator 비교

PostgreSQL operator는 여러 개 있지만, 운영에서 자주 보이는 두 개를 비교합니다.

항목CloudNativePG (CNPG)Zalando postgres-operator
제작EDB (오픈소스)Zalando
라이선스Apache 2.0MIT
첫 릴리스20222017
클러스터 정의 CRDClusterpostgresql
HA 구현자체 (cluster manager)Patroni 기반
베이스 백업내장 (Barman 통합)별도 (WAL-E/G)
TLS 자동 발급cert-manager 통합수동·cert-manager
모니터링 노출Prometheus 메트릭 내장Prometheus 메트릭 내장
주요 사용처EDB Postgres for Kubernetes 기반, 신규 프로젝트 다수Zalando 내부, OpenSearch 시절부터 운영

CloudNativePG 최소 예시

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: app-pg
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:17.0
  storage:
    size: 50Gi
    storageClass: fast-ssd
  postgresql:
    parameters:
      shared_buffers: "2GB"
      effective_cache_size: "6GB"
      max_wal_size: "4GB"
  bootstrap:
    initdb:
      database: app_main
      owner: app_user
      dataChecksums: true
  backup:
    barmanObjectStore:
      destinationPath: s3://my-pg-backups
      s3Credentials:
        accessKeyId: { name: pg-s3-creds, key: ACCESS_KEY_ID }
        secretAccessKey: { name: pg-s3-creds, key: SECRET_ACCESS_KEY }

CNPG는 Cluster 한 CR로 instances(primary + standby), 백업, TLS, 모니터링을 모두 잡습니다.

Zalando 최소 예시

apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: app-pg
spec:
  teamId: dba
  postgresql:
    version: "17"
  numberOfInstances: 3
  volume:
    size: 50Gi
    storageClass: fast-ssd
  databases:
    app_main: app_user
  users:
    app_user: [superuser, createdb]

Zalando는 내부적으로 Patroni를 컨테이너 안에 둬 etcd/k8s configmap을 leader election에 씁니다. 자체 페일오버 검증된 도구라 운영 안정성이 강합니다.

컨테이너 환경에서 주의할 점

    flowchart TD
  POD["Pod: postgres container"]
  STORE["PersistentVolume<br/>(network attached)"]
  WAL["WAL archive<br/>(S3·NCP Object Storage)"]
  SVC["Service / LoadBalancer"]
  CLIENT["client app"]

  CLIENT --> SVC --> POD
  POD --> STORE
  POD --> WAL

  classDef pod fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
  classDef vol fill:#fed7aa,stroke:#c2410c,color:#7c2d12
  classDef wal fill:#d1fae5,stroke:#047857,color:#064e3b
  classDef svc fill:#ede9fe,stroke:#6d28d9,color:#3b0764
  class POD pod
  class STORE vol
  class WAL wal
  class SVC,CLIENT svc
  

핵심 관점:

항목메모
스토리지네트워크 스토리지(EBS/CephFS 등)의 fsync 성능이 베어메탈보다 느리다. WAL flush 지연 주의
메모리 제한container memory limit을 너무 빡빡하게 잡으면 OOM Killer가 postgres를 죽인다. work_mem × connections 여유 확보
종료 신호k8s가 SIGTERM → 30초 grace → SIGKILL. PostgreSQL은 fast shutdown 후 안 끝나면 데이터 손상 위험 → terminationGracePeriodSeconds를 60초 이상
포트 노출LoadBalancer로 외부 노출 시 pg_hba.conf와 TLS 필수
로그stdout 출력 (operator 자동). 외부 로그 수집기(Loki, Datadog)로 모음
ephemeral volume에 PGDATA 두지 말 것. Pod 재시작 = 데이터 손실입니다. 항상 PersistentVolume + ReclaimPolicy=Retain.

단순 컨테이너 vs operator 선택

시나리오권장
개발·테스트·CI공식 postgres:17 이미지 단일 컨테이너
통합 테스트 환경docker-compose, testcontainers
운영 (단일 인스턴스)k8s에 운영하기보다 일반 VM/베어메탈 + PGDG 추천
운영 (HA·복제 필요, k8s 인프라 있음)CloudNativePG 또는 Zalando operator
매니지드 서비스를 못 쓰는 컴플라이언스 환경operator
매니지드 가능하면RDS/Aurora/NCP Cloud DB (Part XVII)
“k8s에서 PostgreSQL 운영” 자체가 옳은가는 별개 논쟁입니다. operator가 잘 도와주지만 결국 stateful 워크로드라 운영 부담이 작지 않습니다. 매니지드를 쓸 수 있으면 매니지드가 보통 답입니다.

정리

  • 공식 Docker 이미지는 개발·테스트용. POSTGRES_PASSWORD·볼륨·initdb 스크립트로 시작
  • 운영 k8s에서는 CloudNativePG 또는 Zalando operator
    • CNPG: 신규·EDB 계열, Apache 2.0, Barman 통합
    • Zalando: 더 오래된 프로덕션 검증, Patroni 기반
  • ephemeral volume 절대 금지합니다. 네트워크 스토리지 fsync 성능과 종료 시간(terminationGracePeriodSeconds)에 주의
  • k8s 자체 운영보다 매니지드 클라우드가 더 간단한 경우가 많음

다음 절(2.4)에서는 패키지/소스/컨테이너 어떤 경로로 깔았든 공통으로 거치는 initdb 단계를 깊게 봅니다.