본문으로 건너뛰기

2.3 컨테이너·Kubernetes

PostgreSQL을 컨테이너로 띄우는 패턴은 크게 두 갈래입니다. 개발·테스트·CI에서 쓰는 단일 Docker 컨테이너와 운영 클러스터를 맡기는 Kubernetes operator입니다. 뒤쪽 operator는 CloudNativePG와 Zalando 두 가지를 비교합니다.

공식 Docker 이미지 — postgres​

Docker Hub의 postgres는 Docker Official Images 프로그램으로 관리되는 공식 이미지입니다.

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 Cloud Plugin (내장 Barman 통합은 1.26부터 deprecated)별도 (WAL-E/G)
TLS 자동 발급cert-manager 통합수동·cert-manager
모니터링 노출Prometheus 메트릭 내장Prometheus 메트릭 내장
주요 사용처EDB Postgres for Kubernetes 기반, 신규 프로젝트 다수Zalando 내부 대규모 운영(Spilo + Patroni)

CloudNativePG 최소 예시​

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: app-pg
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:17.11
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
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: app-pg-store
---
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: app-pg-store
spec:
configuration:
destinationPath: s3://my-pg-backups
s3Credentials:
accessKeyId: { name: pg-s3-creds, key: ACCESS_KEY_ID }
secretAccessKey: { name: pg-s3-creds, key: SECRET_ACCESS_KEY }

백업 저장소는 ObjectStore CR로 분리하고 Cluster는 .spec.plugins에서 이름으로 참조합니다. 기존의 .spec.backup.barmanObjectStore 필드는 CNPG 1.26부터 deprecated입니다. 이 예시는 Barman Cloud Plugin을 operator와 별도로, operator와 같은 namespace(보통 cnpg-system)에 설치해야 동작합니다. 플러그인 구조는 CNPG 2.5 CNPG-I 플러그인 구조, 자세한 구성은 CNPG 7.1 Backup을 참고합니다.

CNPG는 Cluster CR 하나로 instances(primary + standby), TLS, 모니터링을 정의하고, 백업 저장소만 ObjectStore로 따로 둡니다.

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. SIGKILL 시 다음 기동에 crash recovery 필요
포트 노출LoadBalancer로 외부 노출 시 pg_hba.conf와 TLS 필수
로그stdout 출력 (operator 자동). 외부 로그 수집기(Loki, Datadog)로 모음

fast shutdown이 grace 안에 끝나지 않으면 SIGKILL로 종료되어 재기동 시간이 길어지므로 terminationGracePeriodSeconds를 60초 이상으로 둡니다.

경고

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 워크로드라 운영 부담이 작지 않습니다. 매니지드를 쓸 수 있으면 매니지드가 보통 답입니다.