본문 바로가기
운영체제(OS)/Docker

Postgresql 18.3 docker-compose.{ENV}.yml

by JLearn 2026. 3. 18.

Claude Code에서 작성한 개발/스테이징/운영 pdocker-compose.{ENV}.yml 파일 입니다.


개발 docker-compose.dev.yml 

# =============================================================
# PostgreSQL 18.3 - 개발 환경 (dev)
# 경로: /opt/postgres/dev | /data/postgres/dev | /wal/postgres/dev
# =============================================================
name: postgres-dev

services:
  postgres-dev:
    image: postgres:18.3
    container_name: postgres-dev
    restart: unless-stopped

    # ── 타임존 ─────────────────────────────────────────
    # 앱 서버, 로그 수집기와 반드시 통일
    environment:
      TZ: Asia/Seoul
      PGTZ: Asia/Seoul                            # PostgreSQL 내부 timezone
      POSTGRES_USER: dev_user
      POSTGRES_PASSWORD: dev_password_change_me   # 개발환경: 평문 허용
      POSTGRES_DB: devdb
      PGDATA: /var/lib/postgresql/data
      POSTGRES_INITDB_ARGS: >-
        --waldir=/var/lib/postgresql/wal
        --encoding=UTF8
        --locale=C
        --auth-host=scram-sha-256

    # ── 공유 메모리 ───────────────────────────────────────
    # Docker 기본 64MB → shared_buffers 설정값보다 반드시 크게
    # 공식 권장: shared_buffers × 1.2 이상
    # 개발 shared_buffers=128MB → shm_size=256MB (여유 2배)
    shm_size: 256mb

    # ── ulimits ──────────────────────────────────────────
    ulimits:
      # 파일 디스크립터: 접속수 × 3~5 + 여유 (개발 max_connections=50)
      nofile:
        soft: 1024
        hard: 4096
      # 메모리 잠금: huge_pages 또는 대형 SHM 영역 잠금에 필요
      memlock:
        soft: 67108864     # 64MB (bytes)
        hard: 67108864

    volumes:
      - /data/postgres/dev/pgdata:/var/lib/postgresql/data
      - /wal/postgres/dev/pgwal:/var/lib/postgresql/wal
      - /opt/postgres/dev/logs:/var/log/postgresql
      - /opt/postgres/dev/config/postgresql.conf:/etc/postgresql/postgresql.conf:ro

    command: >
      postgres -c config_file=/etc/postgresql/postgresql.conf

    ports:
      - "5432:5432"         # 개발: 로컬 직접 접근 허용

    networks:
      - postgres-dev-net

    deploy:
      resources:
        limits:
          memory: 1G
          cpus: "1.0"

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev_user -d devdb"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

networks:
  postgres-dev-net:
    driver: bridge

스테이징 docker-compose.staging.yml

# =============================================================
# PostgreSQL 18.3 - 스테이징 환경 (staging)
# 경로: /opt/postgres/staging | /data/postgres/staging | /wal/postgres/staging
# =============================================================
name: postgres-staging

services:
  postgres-staging:
    image: postgres:18.3
    container_name: postgres-staging
    restart: unless-stopped

    # ── 타임존 ──────────────────────────────────────────
    environment:
      TZ: Asia/Seoul
      PGTZ: Asia/Seoul
      POSTGRES_USER: staging_user
      POSTGRES_PASSWORD_FILE: /run/secrets/staging_db_password
      POSTGRES_DB: stagingdb
      PGDATA: /var/lib/postgresql/data
      POSTGRES_INITDB_ARGS: >-
        --waldir=/var/lib/postgresql/wal
        --encoding=UTF8
        --locale=C
        --auth-host=scram-sha-256

    # ── 공유 메모리 ───────────────────────────────────────
    # 스테이징 shared_buffers=512MB → shm_size=768MB (1.5배)
    shm_size: 768mb

    # ── ulimits ──────────────────────────────────────────
    ulimits:
      nofile:
        soft: 4096
        hard: 16384    # max_connections=100 × 5 + 여유
      memlock:
        soft: 536870912    # 512MB (bytes)
        hard: 536870912

    volumes:
      - /data/postgres/staging/pgdata:/var/lib/postgresql/data
      - /wal/postgres/staging/pgwal:/var/lib/postgresql/wal
      - /opt/postgres/staging/logs:/var/log/postgresql
      - /opt/postgres/staging/config/postgresql.conf:/etc/postgresql/postgresql.conf:ro

    command: >
      postgres -c config_file=/etc/postgresql/postgresql.conf

    ports:
      # 스테이징: 루프백만 노출 (외부접근은 SSH 터널 사용)
      - "127.0.0.1:5433:5432"

    networks:
      - postgres-staging-net

    secrets:
      - staging_db_password

    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2.0"
        reservations:
          memory: 2G

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U staging_user -d stagingdb"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 30s

    logging:
      driver: json-file
      options:
        max-size: "100m"
        max-file: "5"

secrets:
  staging_db_password:
    file: /opt/postgres/staging/secrets/db_password.txt

networks:
  postgres-staging-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

운영 docker-compose.prod.yml

# =============================================================
# PostgreSQL 18.3 - 운영 환경 (prod)
# 경로: /opt/postgres/prod | /data/postgres/prod | /wal/postgres/prod
# =============================================================
name: postgres-prod

services:
  postgres-prod:
    image: postgres:18.3    # ← 절대 latest 사용 금지, 버전 고정 필수
    container_name: postgres-prod

   # restart:  unless-stopped: 사용자 중지 이외에는 재시작
    restart: unless-stopped     # 운영: always: 어떤 상황에서도 재시작  

    # ── 타임존 ───────────────────────────────────────
    environment:
      TZ: Asia/Seoul
      PGTZ: Asia/Seoul
      POSTGRES_USER: prod_user
      POSTGRES_PASSWORD_FILE: /run/secrets/prod_db_password
      POSTGRES_DB: proddb
      PGDATA: /var/lib/postgresql/data
      POSTGRES_INITDB_ARGS: >-
        --waldir=/var/lib/postgresql/wal
        --encoding=UTF8
        --locale=C
        --data-checksums

        # ※ 외부(host) 접속 인증 방식 지정(SCRAM-SHA-256)  ※
        --auth-host=scram-sha-256 

    # ── 공유 메모리 ───────────────────────────────────────
    # 운영 shared_buffers=2GB → shm_size=3GB (1.5배 확보).

    # shared_buffers = 4GB → shm_size=6GB  
    # 부족하면 POSIX SHM 초기화 실패로 컨테이너 즉시 종료됨
    shm_size: 6gb

    # ── ulimits ───────────────────────────────────────
    ulimits:
      # nofile: max_connections(200) × 프로세스당 fd(5~10) + 여유
      # PostgreSQL 공식 권장: 최소 (max_connections + max_wal_senders) × 10
      nofile:
        soft: 65536
        hard: 65536
      # memlock: huge_pages 사용 또는 large_pages 설정 시 필수
      # unlimited 설정 시 컨테이너 메모리 잠금 무제한 허용
      memlock:
        soft: -1           # -1 = unlimited
        hard: -1
      # nproc: 백그라운드 워커 + autovacuum 워커 포함
      nproc:
        soft: 65536
        hard: 65536

    # ── 커널 파라미터 튜닝 ─────────────────────────────────────
    # compose v2에서 sysctls는 privileged 없이 사용 가능한 항목만
    sysctls:
      # TCP 백로그 큐: 순간 접속 폭주 대비
      net.core.somaxconn: 65535
      # 메모리 오버커밋: PostgreSQL 권장 (2=always deny → 안전 ※ 확인필요※)
      # 0=허용, 1=항상허용, 2=거부 (운영 DB는 2 권장) 

      # 확인필요: 0 (시스템 관리) 또는 1 설정 운영 후 2 변경여부 판단 필요
      vm.overcommit_memory: 0

    # OOM Killer 보호: 운영 DB는 메모리 부족 시 다른 프로세스 먼저 종료
    oom_score_adj: -500

    volumes:
      - /data/postgres/prod/pgdata:/var/lib/postgresql/data
      - /wal/postgres/prod/pgwal:/var/lib/postgresql/wal
      - /opt/postgres/prod/logs:/var/log/postgresql
      - /opt/postgres/prod/config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
      - /opt/postgres/prod/scripts:/scripts:ro

    command: >
      postgres -c config_file=/etc/postgresql/postgresql.conf

    # 운영: 포트 직접 외부 노출 금지
    # 앱 컨테이너와 내부 Docker 네트워크로만 통신
    expose:
      - "5432"

    networks:
      - postgres-prod-net
      - app-net           # 애플리케이션 컨테이너와 공유

    secrets:
      - prod_db_password

    deploy:
      resources:
        limits:

          # 이 선을 넘으면 컨테이너 강제 종료(보호)
          memory: 14G     # 호스트 RAM의 80% 이하
          cpus: "8.0"         # cpu 코어 수

          reservations:

          # 최소 8GB 서버에서 실행 조건: shared_buffers 크기만큼은 항상 보장 
          memory: 8G       # 최소 보장
          cpus: "4.0"

    healthcheck:
      test:
        - "CMD-SHELL"

     # - "pg_isready -U prod_user -d proddb && psql -U prod_user -d proddb -c 'SELECT 1' > /dev/null"

     # 실무에선 pg_isready만으로도 충분히 안정적임

       - "pg_isready -U prod_user -d proddb"
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

    logging:
      driver: json-file
      options:
        max-size: "200m"
        max-file: "10"

secrets:
  prod_db_password:
    file: /opt/postgres/prod/secrets/db_password.txt

networks:
  postgres-prod-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/24
  app-net:
    external: true          # 앱 compose와 공유 네트워크

댓글