본문 바로가기
Database & Storage

Docker로 PostgreSQL 운영하기: 백업·복구·업그레이드 실전 가이드

by yamoojin83 2025. 10. 9.

Docker로 PostgreSQL 운영하기: 백업·복구·업그레이드 실전 가이드

Docker로 PostgreSQL를 올리면 설치와 실행은 놀랍도록 쉽지만,
막상 운영 단계에 들어가면 백업, 복구, 버전 업그레이드, 볼륨 관리 같은 문제들이 한꺼번에 몰려옵니다.

저 역시 처음에는 docker run 한 줄로 DB를 띄웠다가,
볼륨 마운트와 백업 전략 없이 운영을 시작해버려서
테스트 데이터가 통째로 날아간 적이 있습니다.

이 글은 Docker + PostgreSQL 조합을 실제 서비스에 사용하면서 겪었던 시행착오를 바탕으로,
컨테이너 구성 → 백업/복구 전략 → 업그레이드 시나리오까지 한 번에 정리한 운영 가이드입니다.

1. 기본 구성: Docker Compose로 PostgreSQL 올리기

먼저 docker run 단발성 실행이 아니라,
운영 환경에서 그대로 가져갈 수 있는 docker-compose.yml을 기준으로 설명하겠습니다.
핵심은 세 가지입니다.

1) 데이터는 반드시 호스트 볼륨에 저장할 것
2) 환경 변수로 초기 계정/DB를 명시할 것
3) 백업을 위한 별도 디렉터리를 미리 확보할 것

version: "3.9"

services:
  postgres:
    image: postgres:16
    container_name: my-postgres
    restart: always
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret-password
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./backup:/backup

volumes:
  pg_data:

위 설정에서 pg_data 볼륨은 실제 데이터 파일을, ./backup 디렉터리는
pg_dump, basebackup 등 백업 파일을 모아두는 용도로 사용합니다.
이렇게 구조를 분리해두면, 컨테이너를 지우거나 이미지를 교체해도
데이터와 백업 파일은 그대로 유지할 수 있습니다.



2. 논리 백업: pg_dump로 빠르게 찍어두기

가장 간단하고 많이 쓰는 백업 방식은 pg_dump를 사용하는 논리 백업입니다.
Docker 컨테이너 내부에서 실행해도 되고, 호스트에서 실행해도 됩니다.
운영에서는 보통 cron + 스크립트 조합으로 자동화합니다.

# 1) 컨테이너 내부에서 백업
docker exec my-postgres \
  pg_dump -U appuser -d appdb \
  -Fc -f /backup/appdb_$(date +%Y%m%d_%H%M).dump

# 2) 호스트에서 psql 클라이언트로 접속해 백업 (5432 포트 노출 시)
pg_dump -h 127.0.0.1 -p 5432 -U appuser -d appdb \
  -Fc -f ./backup/appdb_$(date +%Y%m%d_%H%M).dump

백업 파일 포맷은 -Fc (custom format)를 권장합니다.
이 포맷은 나중에 복구할 때 pg_restore로 테이블 단위 복구, 병렬 복구 등
유연한 옵션을 사용할 수 있기 때문입니다.



3. 논리 복구: 새 컨테이너에 데이터 되살리기

논리 백업의 장점은 새 컨테이너나 새 버전의 PostgreSQL에도 그대로 이식이 쉽다는 점입니다.
보통 운영에서 장애가 났을 때는 “빈 컨테이너 + 기존 백업” 조합으로 복구합니다.

# 1) 복구용 새 컨테이너 기동 (비어 있는 DB)
docker run -d --name my-postgres-restore \
  -e POSTGRES_USER=appuser \
  -e POSTGRES_PASSWORD=secret-password \
  -e POSTGRES_DB=appdb \
  -v pg_restore_data:/var/lib/postgresql/data \
  -v $(pwd)/backup:/backup \
  postgres:16

# 2) 컨테이너 내부에서 복구 실행
docker exec -it my-postgres-restore bash -c \
  "pg_restore -U appuser -d appdb /backup/appdb_20250101_0200.dump"

실제 운영에서는 복구 속도보다 중요한 것이 복구 절차의 재현성입니다.
스크립트와 README를 함께 관리해 두면,
새로운 팀원이 투입되더라도 같은 절차로 복구를 진행할 수 있습니다.



백업/복구 흐름 다이어그램



4. 물리 백업: basebackup + WAL 아카이브

논리 백업은 구조 변경에 유연하지만, 데이터가 많을수록 시간이 오래 걸립니다.
수십 GB 이상, 수억 건 이상의 데이터를 다루는 서비스라면
물리 백업 + WAL 아카이브 조합을 검토해야 합니다.

컨테이너 환경에서도 Postgres의 pg_basebackup을 그대로 사용할 수 있습니다.

# 컨테이너 내부에서 basebackup 실행 예시
docker exec my-postgres \
  pg_basebackup -U appuser -D /backup/base_$(date +%Y%m%d) -Ft -z

다만, 물리 백업은 클러스터 통째로 복제하는 개념이라
컨테이너 라이프사이클보다 DB 자체를 더 길게 가져가는 환경에서 의미가 큽니다.
개인/소규모 서비스 단계에서는 논리 백업만으로도 충분한 경우가 많습니다.



5. Docker 환경에서 자주 터지는 장애 패턴 3가지

5-1) 볼륨 없이 컨테이너만 교체했다가 데이터 증발

가장 흔한 사고입니다.
초기 테스트 환경을 만들 때 데이터 디렉터리를 볼륨으로 분리하지 않고,
컨테이너 파일시스템 안에 둔 상태로 docker rm을 해버리면
컨테이너 삭제와 함께 DB 전체가 사라집니다.

그래서 /var/lib/postgresql/data는 반드시 호스트 볼륨이나 외부 스토리지에 두고,
컨테이너는 언제든지 갈아 끼울 수 있는 “껍데기”로만 사용해야 합니다.

5-2) 타임존과 로케일 설정 누락

    environment:
      TZ: Asia/Seoul
      LANG: en_US.utf8

타임존과 로케일을 명시하지 않으면,
운영 중에 로그 시간과 애플리케이션 시간이 엇갈리거나
한글 정렬/인덱스 문제를 겪을 수 있습니다.
Docker 기반 DB라도 서버 시대에 하던 기본 설정은 그대로 적용해야 합니다.

5-3) 로그와 백업 디스크 공간 모니터링 부재

PostgreSQL은 WAL, 로그, 백업 파일이 누적되면서
생각보다 빠르게 디스크를 채웁니다.

Docker 볼륨이 꽉 차면 컨테이너는 그대로 멈춰 버리고,
단순 재시작으로 해결되지 않는 경우가 많습니다.
그래서 운영에서는 df, du 모니터링 + 오래된 백업 정리 스크립트를 함께 두는 것이 안전합니다.



6. 버전 업그레이드 전략: “새 컨테이너 + 데이터 이관”

Docker 환경에서 PostgreSQL을 업그레이드할 때 가장 실수하기 좋은 지점은,
기존 데이터 디렉터리를 그대로 새 버전 컨테이너에 마운트하는 것입니다.
메이저 버전이 바뀌면 데이터 디렉터리 포맷이 달라질 수 있기 때문에
정석은 항상 새 컨테이너 + 논리 백업/복구입니다.

# 1) 기존 버전에서 논리 백업 수행
docker exec my-postgres \
  pg_dump -U appuser -d appdb -Fc -f /backup/appdb_before_upgrade.dump

# 2) 새 버전 컨테이너 기동
docker run -d --name my-postgres-v17 \
  -e POSTGRES_USER=appuser \
  -e POSTGRES_PASSWORD=secret-password \
  -e POSTGRES_DB=appdb \
  -v pg_data_v17:/var/lib/postgresql/data \
  -v $(pwd)/backup:/backup \
  postgres:17

# 3) 새 컨테이너에서 복구
docker exec my-postgres-v17 \
  pg_restore -U appuser -d appdb /backup/appdb_before_upgrade.dump

이 패턴을 스테이징 환경에서 미리 여러 번 반복해보면,
실제 운영 업그레이드 때도 비교적 안정적으로 진행할 수 있습니다.

 

버전 업그레이드 & 데이터 이관

 

7. 운영 체크리스트 요약

  • 데이터 디렉터리는 반드시 볼륨으로 분리했는가?
  • 논리 백업(pg_dump) 자동화가 되어 있는가?
  • 복구 절차를 스테이징에서 실제로 검증해봤는가?
  • 디스크 사용량, WAL/로그 폭증을 모니터링하고 있는가?
  • 버전 업그레이드는 “새 컨테이너 + 데이터 이관” 전략으로 설계했는가?

Docker + PostgreSQL 조합은 잘만 설계하면
배포와 롤백이 빠르고, 실험 환경을 만들기도 쉬운 구조입니다.

이 글에서 정리한 백업·복구·업그레이드 전략을 기반으로,
각자의 서비스 상황에 맞는 운영 패턴을 만들어 보시길 바랍니다.



운영에서 제일 중요한 한 줄

Docker로 Postgres를 운영할 때 가장 중요한 한 줄은 이겁니다.

“컨테이너는 언제든 갈아끼우고, 데이터는 볼륨으로 영구화한다.”

이 원칙만 지키면 업그레이드/복구/이관이 모두 쉬워지고,
운영 중 사고가 나도 “데이터만 살리면 끝나는 구조”가 됩니다.
반대로 이 원칙이 깨지는 순간, Docker의 장점이 전부 사라집니다.



👉 1편: Ubuntu 24.04에서 Nginx로 무료 SSL(HTTPS) 적용

👉 2편: UFW 방화벽 실전 규칙

👉 3편: systemd 서비스로 스프링부트 배포

👉 4편: Tomcat 10 설치+튜닝 체크리스트

👉 5편: Gradle 빌드 최적화로 빌드 50% 줄이기

👉 6편: Spring Security 6 JWT 로그인/리프레시 토큰

👉 7편: JPA N+1 자동 감지 테스트 + 해결

👉 8편: Docker로 PostgreSQL 운영(백업/복구/업그레이드)

👉 9편: Micrometer+Prometheus+Grafana 대시보드

👉 10편: Testcontainers로 DB 통합테스트

 

 

👉 1편: Ubuntu 24.04에서 Nginx로 무료 SSL(HTTPS) 적용

👉 2편: UFW 방화벽 실전 규칙

👉 3편: systemd 서비스로 스프링부트 배포

👉 4편: Tomcat 10 설치+튜닝 체크리스트

👉 5편: Gradle 빌드 최적화로 빌드 50% 줄이기

👉 6편: Spring Security 6 JWT 로그인/리프레시 토큰

👉 7편: JPA N+1 완전 정복: 자동 감지부터 fetch join·EntityGraph·batch_size 실전 해결 전략

👉 8편: Docker로 PostgreSQL 운영(백업/복구/업그레이드)

👉 9편: Micrometer+Prometheus+Grafana 대시보드

👉 10편: Testcontainers로 DB 통합테스트