본문으로 건너뛰기

2.3 컨테이너·Kubernetes

PostgreSQL을 컨테이너로 띄우는 패턴은 크게 두 갈래입니다. 개발·테스트·CI에서 쓰는 단일 Docker 컨테이너와 운영 클러스터를 맡기는 Kubernetes operator입니다. 뒤쪽 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에 씁니다. 자체 페일오버 검증된 도구라 운영 안정성이 강합니다.

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

핵심 관점:

항목메모
스토리지네트워크 스토리지(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 워크로드라 운영 부담이 작지 않습니다. 매니지드를 쓸 수 있으면 매니지드가 보통 답입니다.