Kubernetes 환경에서 PostgreSQL 백업과 복구하기

1–2분

🖼️ Section 1. 들어가며

Kubernetes 환경에서 PostgreSQL을 운영하다 보면 etcd 백업으로 클러스터는 복구했는데,

막상 PostgreSQL PVC 안의 데이터는 복구가 안 돼서 애를 먹는 경우가 많다.

이런 상황에서 PostgreSQL의 백업과 복구도 함께 자동화해놔야 etcd 복원 후에도 DB 상태를 동기화할 수 있다.

이 포스트는 그런 상황을 대비해서 만든, 실제 운영 환경에서 사용 중인 PostgreSQL 백업/복구 자동화 스크립트를 소개하고자 작성되었다.

Bitnami PostgreSQL HA 차트를 기준으로 했지만, StatefulSet 기반이라면 대부분 호환된다.


🖼️ Section 2. 백업 방식 선택: 왜 pg_basebackup인가?

PostgreSQL의 대표적인 백업 방식은 다음과 같다

백업 방식특징적합 상황
pg_dumpSQL 텍스트 백업스키마 백업, 작은 DB
파일 복사데이터 디렉토리 직접 복제테스트 환경
pg_basebackup전체 Physical 백업 + WAL 포함실제 운영 환경

이 중 pg_basebackup은 PostgreSQL 내부 동작을 깨뜨리지 않으면서 전체 데이터를 복사 + WAL 포함으로 백업할 수 있어, 실무에서 가장 안정적인 선택지다.

특히 Bitnami PostgreSQL HA 차트에선 이 방법이 거의 공식처럼 사용된다.


🖼️ Section 3. 백업 스크립트 구성과 흐름 설명

🧩 전체 흐름

1. Primary Pod 탐색
2. pg_basebackup 수행
3. 압축 파일 생성
4. 로컬로 다운로드
5. 임시 파일 정리


📜 실제 백업 스크립트

#!/bin/bash
NAMESPACE=runway
APP_LABEL="app.kubernetes.io/component=postgresql"
BACKUP_DIR="./"
BASEBACKUP_FILE="pg_basebackup.tar.gz"
WAL_ARCHIVE_DIR="/bitnami/postgresql/wal-archive"
WAL_ARCHIVE_TAR="wal_archive_$(date +%Y%m%d_%H%M%S).tar.gz"
PG_USER="mrx"
PG_PORT="5432"
PG_HOST="localhost"
PG_BASEBACKUP_DIR="/tmp/pg_basebackup"
REMOTE_BASEBACKUP_TAR="/tmp/${BASEBACKUP_FILE}"
REMOTE_WAL_TAR="/tmp/${WAL_ARCHIVE_TAR}"
# 1. primary pod 찾기
PODS=$(kubectl get pods -n $NAMESPACE -l $APP_LABEL -o jsonpath='{.items[*].metadata.name}')
for POD in $PODS; do
ROLE=$(
kubectl exec -n $NAMESPACE $POD — \\
/opt/bitnami/scripts/postgresql-repmgr/entrypoint.sh repmgr \\
-f /opt/bitnami/repmgr/conf/repmgr.conf cluster show 2>/dev/null \\
| grep -w primary
)
if [[ -n "$ROLE" ]]; then
PRIMARY_POD=$POD
echo "[*] Primary pod found: $PRIMARY_POD"
break
fi
done
if [[ -z "$PRIMARY_POD" ]]; then
echo "[!] Primary pod를 찾을 수 없습니다."
exit 1
fi
# 2. pg_basebackup 수행
echo "[*] pg_basebackup 수행중…"
kubectl exec -n $NAMESPACE $PRIMARY_POD — rm -rf $PG_BASEBACKUP_DIR $REMOTE_BASEBACKUP_TAR
kubectl exec -n $NAMESPACE $PRIMARY_POD — mkdir -p $PG_BASEBACKUP_DIR
kubectl exec -n $NAMESPACE $PRIMARY_POD — \\
pg_basebackup -U $PG_USER -h $PG_HOST -p $PG_PORT -D $PG_BASEBACKUP_DIR -Fp -Xs -P
kubectl exec -n $NAMESPACE $PRIMARY_POD — \\
tar czf $REMOTE_BASEBACKUP_TAR -C $PG_BASEBACKUP_DIR .
# 4. 로컬로 백업 파일 다운로드
LOCAL_BASEBACKUP_PATH="${BACKUP_DIR}${BASEBACKUP_FILE}"
LOCAL_WAL_PATH="${BACKUP_DIR}${WAL_ARCHIVE_TAR}"
echo "[*] pg_basebackup 파일 다운로드 중… ($LOCAL_BASEBACKUP_PATH)"
kubectl cp $NAMESPACE/$PRIMARY_POD:$REMOTE_BASEBACKUP_TAR $LOCAL_BASEBACKUP_PATH
# 5. Pod 내 임시 파일 정리
kubectl exec -n $NAMESPACE $PRIMARY_POD — rm -rf $PG_BASEBACKUP_DIR $REMOTE_BASEBACKUP_TAR $REMOTE_WAL_TAR
echo "[*] 모든 백업 작업 완료!"
echo " – pg_basebackup: $LOCAL_BASEBACKUP_PATH"
view raw gistfile1.txt hosted with ❤ by GitHub

🖼️ Section 4. 복구 스크립트 구성과 절차 설명

복구는 StatefulSet을 일시적으로 중단한 다음, PVC에 접근해 압축을 풀고 데이터를 복원하는 방식이다.

복구 완료 후 StatefulSet을 다시 올리면 PostgreSQL이 자동으로 재시작한다.

📜 실제 복구 스크립트

#!/bin/bash
NAMESPACE="runway"
STS_NAME="postgresql-ha-postgresql"
PVC_NAME="data-postgresql-ha-postgresql-0"
PG_DATA_DIR="/bitnami/postgresql/data"
TMP_POD="pg-data-restore"
BACKUP_FILE="./pg_basebackup.tar.gz"
set -e
echo "[*] 1. StatefulSet scale down(0)으로 중지"
kubectl scale sts ${STS_NAME} -n ${NAMESPACE} –replicas=0
echo "[*] 2. 임시 busybox Pod 생성"
cat <<EOF | kubectl apply -n ${NAMESPACE} -f –
apiVersion: v1
kind: Pod
metadata:
name: ${TMP_POD}
spec:
restartPolicy: Never
containers:
– name: busybox
image: cr.makina.rocks/external-hub/bitnami/bitnami-shell:11-debian-11-r90
command: ["sleep", "3600"]
volumeMounts:
– name: datadir
mountPath: /bitnami/postgresql
volumes:
– name: datadir
persistentVolumeClaim:
claimName: ${PVC_NAME}
EOF
echo "[*] 임시 Pod Running 대기…"
kubectl wait –for=condition=Ready pod/${TMP_POD} -n ${NAMESPACE} –timeout=60s
echo "[*] 3. 기존 데이터 디렉토리 완전 삭제"
kubectl exec -n ${NAMESPACE} ${TMP_POD} — sh -c 'rm -rf ${PG_DATA_DIR}/* ${PG_DATA_DIR}/.* 2>/dev/null || true'
kubectl exec -n ${NAMESPACE} ${TMP_POD} — mkdir -p ${PG_DATA_DIR}
echo "[*] 4. basebackup 파일 복사"
kubectl cp ${BACKUP_FILE} ${NAMESPACE}/${TMP_POD}:/tmp/pg_basebackup.tar.gz
echo "[*] 5. 압축 해제"
kubectl exec -n ${NAMESPACE} ${TMP_POD} — tar xvfz /tmp/pg_basebackup.tar.gz -C ${PG_DATA_DIR}
kubectl exec -n ${NAMESPACE} ${TMP_POD} — rm /tmp/pg_basebackup.tar.gz
echo "[*] 6. (필요시 권한 수정)"
kubectl exec -n ${NAMESPACE} ${TMP_POD} — chown -R 1001:root ${PG_DATA_DIR} || true
echo "[*] 7. 임시 Pod 삭제"
kubectl delete pod -n ${NAMESPACE} ${TMP_POD}
echo "[*] 8. StatefulSet 다시 1로 스케일 업"
kubectl scale sts ${STS_NAME} -n ${NAMESPACE} –replicas=1
kubectl rollout status sts/${STS_NAME} -n ${NAMESPACE}
echo "[*] 복구 후 상태/로그 확인:"
echo " kubectl get pod -n ${NAMESPACE}"
echo " kubectl logs -n ${NAMESPACE} ${STS_NAME}-0 -c postgresql –tail=100"
echo " kubectl exec -it -n ${NAMESPACE} ${STS_NAME}-0 -c postgresql — psql -U mrx -d mrx"
view raw gistfile1.txt hosted with ❤ by GitHub

🖼️ Section 5. 운영 환경에서의 실전 팁

  • Kubernetes의 특성상 PVC가 유지되더라도 내부 데이터는 깨질 수 있다. 이럴 때 etcd로 클러스터를 복구하고 나면, PostgreSQL 데이터도 수동 복구가 필수다.
  • 이 스크립트를 정기적으로 실행하면 DB 상태를 최신으로 보존할 수 있고, 장애 발생 시 즉시 복구가 가능하다.
  • CronJob으로 정기 백업 구성 + 복구 스크립트를 Wiki에 정리해두면 베스트.

🖼️ Section 6. 실무에서 바로 써먹는 요약

  • pg_basebackup은 PostgreSQL 운영 환경에 가장 안정적인 백업 방식
  • Kubernetes에서는 Pod가 아니라 PVC 단위로 백업/복구를 구성해야 한다.
  • StatefulSet은 복구 시 일시적으로 중지하고 복원 후 다시 올리는 방식이 안전
  • etcd로 클러스터 복원 후 DB 동기화를 위해 반드시 DB 백업도 별도로 구성하자