Django 배포 전략 비교

문서 | 김 상기 | 2026.03.05
공개 v1.0.0





Django: VirtualHost vs Docker

Django 배포 전략 비교

Ubuntu 환경에서 VirtualHost 방식 vs Docker 방식 — 정의·구조·비교·권장사항

① 정의

🖥️ VirtualHost 방식

Native / Bare-metal 배포

Ubuntu 서버에 Python, Django, Gunicorn을 직접 설치하고 Nginx의 VirtualHost(server block)로 도메인별 트래픽을 분기하는 전통적인 배포 방식.

  • Ubuntu OS 위에 직접 설치
  • Nginx → Gunicorn → Django 체인
  • systemd로 프로세스 관리
  • virtualenv로 Python 환경 격리
  • 도메인당 별도 소켓 파일(.sock)

🐳 Docker 방식

Container 기반 배포

Django 앱과 모든 의존성을 컨테이너 이미지로 패키징하여 격리된 환경에서 실행. docker-compose로 Nginx·Django·DB·Redis를 통합 관리.

  • OS와 완전히 격리된 컨테이너
  • docker-compose로 서비스 오케스트레이션
  • Dockerfile로 환경 코드화
  • 서비스별 독립 컨테이너 운영
  • 이미지 단위 버전 관리
② 아키텍처 구조

🖥️ VirtualHost 구조

[ 클라이언트 ] │ HTTP/HTTPS ▼ [ Nginx ] ← VirtualHost 분기 ├─ site-a.com → gunicorn-a.sock └─ site-b.com → gunicorn-b.sock │ ▼ [ Gunicorn ] (systemd 관리) │ ▼ [ Django App ] (venv) │ ▼ [ PostgreSQL ] (OS 직접 설치)

🐳 Docker 구조

[ 클라이언트 ] │ HTTP/HTTPS ▼ [ nginx 컨테이너 ] ← 리버스 프록시 ├─ site-a.com → django_a 컨테이너 └─ site-b.com → django_b 컨테이너 │ ▼ [ Django + Gunicorn ] (컨테이너) │ ▼ [ PostgreSQL 컨테이너 ][ Redis 컨테이너 ] (모두 docker network로 통신)
③ 항목별 상세 비교
비교 항목 🖥️ VirtualHost 🐳 Docker
초기 설정 단순 OS 패키지 설치 수준 중간 Docker, compose 학습 필요
환경 격리 부분적 venv로 Python만 격리. OS 패키지는 공유 완전 OS 수준의 완전한 격리. 의존성 충돌 없음
확장성(Scale) 어려움 수동 설정 변경 필요. 수평 확장 복잡 우수 docker-compose scale 또는 Swarm/K8s 연동
배포 속도 느림 git pull → pip install → restart 수동 절차 빠름 이미지 빌드 후 docker-compose up -d
롤백 어려움 이전 상태 복원이 복잡하고 리스크 큼 쉬움 이전 이미지 태그로 즉시 롤백 가능
리소스 사용 효율적 컨테이너 오버헤드 없음. 성능 우수 약간 높음 컨테이너 레이어 오버헤드 (미미한 수준)
개발/운영 일관성 불일치 "내 PC에서는 됐는데..." 문제 발생 동일 개발·스테이징·운영 환경 완전 동일
SSL/HTTPS Certbot + Nginx 직접 설정 Certbot 컨테이너 or Nginx Proxy Manager 사용
로그 관리 journalctl, /var/log 직접 확인 docker logs, ELK Stack 등 연동 용이
DB 관리 안정적 OS에 직접 설치, 성능 최적 주의 필요 볼륨 마운트 설정 필수 (데이터 영속성)
팀 협업 불편 신규 팀원 환경 구성에 긴 시간 편리 docker-compose up 한 줄로 즉시 개발 가능
CI/CD 연동 SSH 배포 스크립트 작성 필요 표준화 GitHub Actions, GitLab CI 템플릿 풍부
모니터링 htop, netstat 등 수동 확인 Portainer, Grafana, Prometheus 연동 용이
적합한 규모 소규모 1~3개 사이트 중~대규모, 다중 서비스
④ 언제 무엇을 선택할까?

🖥️ VirtualHost 추천 상황

  • 혼자 운영하는 소규모 1~3개 사이트
  • 서버 리소스가 극히 제한적인 경우 (1GB RAM 이하)
  • Docker 학습 비용이 부담스러운 경우
  • 레거시 시스템 유지보수
  • 빠른 1회성 프로토타입 서버 구성

🐳 Docker 추천 상황

  • 팀 단위 협업 프로젝트
  • CI/CD 파이프라인을 구성하는 경우
  • 여러 Django 프로젝트를 한 서버에서 운영
  • 스테이징/운영 환경의 일관성이 중요할 때
  • 미래에 Kubernetes 확장을 고려하는 경우
  • 빠른 롤백이 필요한 서비스
⑤ 최상의 구성 (Best Practice) — Docker Compose
  프로젝트 디렉터리 구조
my-project/ ├── docker-compose.yml # 서비스 오케스트레이션 ├── docker-compose.prod.yml # 운영 환경 오버라이드 ├── Dockerfile # Django 이미지 빌드 ├── .env # 환경변수 (git 제외!) ├── .env.example # 환경변수 템플릿 ├── nginx/ │ ├── nginx.conf │ └── conf.d/ │ └── default.conf └── app/ ├── manage.py ├── requirements.txt └── config/ ├── settings/ │ ├── base.py │ ├── local.py │ └── production.py └── wsgi.py
  Dockerfile
# 1단계: 빌드 스테이지 (의존성 설치) FROM python:3.12-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --upgrade pip \ && pip install --no-cache-dir -r requirements.txt # 2단계: 실행 스테이지 (최소 이미지) FROM python:3.12-slim ENV PYTHONDONTWRITEBYTECODE=1 \ # .pyc 파일 생성 방지 PYTHONUNBUFFERED=1 # 로그 실시간 출력 WORKDIR /app # 빌더에서 설치된 패키지만 복사 (이미지 크기 최소화) COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY --from=builder /usr/local/bin /usr/local/bin COPY ./app . RUN python manage.py collectstatic --noinput --settings=config.settings.production EXPOSE 8000 CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "config.wsgi:application"]
  docker-compose.yml (개발 + 운영 공통)
version: '3.9' services: # ── Django 애플리케이션 ────────────────────────── web: build: . command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3 volumes: - ./app:/app # 개발 시 코드 실시간 반영 - static_volume:/app/staticfiles - media_volume:/app/media env_file: - .env depends_on: db: condition: service_healthy # DB 준비 완료 후 시작 redis: condition: service_started # ── Nginx 리버스 프록시 ────────────────────────── nginx: image: nginx:1.25-alpine ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d - static_volume:/static - media_volume:/media - ./certbot/conf:/etc/letsencrypt # SSL 인증서 depends_on: - web # ── PostgreSQL ─────────────────────────────────── db: image: postgres:16-alpine volumes: - postgres_data:/var/lib/postgresql/data # 데이터 영속성! environment: POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} healthcheck: # DB 헬스체크 test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] interval: 10s timeout: 5s retries: 5 # ── Redis (Celery/캐시용) ────────────────────── redis: image: redis:7-alpine volumes: - redis_data:/data # ── Celery Worker (비동기 작업) ─────────────── celery: build: . command: celery -A config worker --loglevel=info volumes: - ./app:/app env_file: - .env depends_on: - db - redis volumes: # Named volumes 정의 postgres_data: redis_data: static_volume: media_volume:
  nginx/conf.d/default.conf (VirtualHost 역할)
# 업스트림: Django 컨테이너 upstream django { server web:8000; # docker-compose 서비스명 사용 } server { listen 80; server_name your-domain.com; # Static 파일 직접 서빙 (Django를 거치지 않음) location /static/ { alias /static/; expires 30d; add_header Cache-Control "public, immutable"; } location /media/ { alias /media/; } # Django 앱으로 프록시 location / { proxy_pass http://django; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 10M; } }
  .env (환경변수 파일 — .gitignore에 반드시 추가!)
# Django DJANGO_SETTINGS_MODULE=config.settings.production SECRET_KEY=your-super-secret-key-here DEBUG=False ALLOWED_HOSTS=your-domain.com,www.your-domain.com # Database DB_NAME=mydb DB_USER=myuser DB_PASSWORD=strongpassword123 DATABASE_URL=postgresql://myuser:strongpassword123@db:5432/mydb # Redis REDIS_URL=redis://redis:6379/0 # Email EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 EMAIL_HOST_USER=your@gmail.com EMAIL_HOST_PASSWORD=app-password
  운영 배포 명령어
# 처음 서버 설정 $ docker-compose up -d --build # DB 마이그레이션 $ docker-compose exec web python manage.py migrate # 슈퍼유저 생성 $ docker-compose exec web python manage.py createsuperuser # 새 코드 배포 (무중단에 가까운 방식) $ git pull $ docker-compose up -d --build --no-deps web # web 서비스만 재빌드 # 로그 확인 $ docker-compose logs -f web $ docker-compose logs -f nginx # 이전 버전으로 롤백 $ docker-compose down $ docker tag myproject_web:latest myproject_web:backup $ docker-compose up -d
⑥ 최종 권장사항

🎯 솔로 개발자 / 소규모 클라이언트 프로젝트 최적 전략

  1. Docker Compose를 기본 전략으로 채택 초기 학습 비용이 있지만, 두 번째 프로젝트부터는 압도적으로 빠르고 안전합니다. 환경 재현성과 롤백 능력이 실무에서 핵심 가치입니다.
  2. 한 서버에서 여러 사이트: Nginx Proxy Manager 활용 docker-compose 기반으로 여러 프로젝트를 운영할 때, Nginx Proxy Manager 컨테이너 하나로 SSL 자동 갱신 + 도메인 분기를 GUI로 관리할 수 있습니다.
  3. PostgreSQL은 컨테이너 vs 호스트 직접 설치 고민 소규모라면 컨테이너로 충분합니다. 데이터 볼륨을 named volume으로 관리하고, 정기 pg_dump 백업 스크립트를 cron으로 설정하세요.
  4. CI/CD: GitHub Actions + Docker Hub 연동 main 브랜치 push 시 자동으로 이미지를 빌드하고 서버에 배포하는 파이프라인을 구성하면 배포가 버튼 하나로 완성됩니다.
  5. 현재 VirtualHost 환경이라면 점진적 마이그레이션 기존 서비스는 건드리지 않고, 신규 프로젝트부터 Docker로 전환하세요. 서버에 Docker를 설치해도 기존 Nginx + Gunicorn 환경은 그대로 동작합니다.

실행하기

평가 & 리뷰

★ 0

0개의 평가


평가하려면 로그인하세요.

댓글 (0)

댓글을 작성하려면 로그인하세요.
💬

아직 댓글이 없습니다.

전체 메뉴
로그인이 필요합니다

SANGKI.COM의 모든 기능을 이용하시려면 로그인해주세요.