일하는/Cloud, Web

[Django][Docker] Django + Nginx Container 만들기

김논리 2021. 12. 13. 16:07

Django는 WAS(Web Application Server)의 일종으로, DB 조회 등의 다양한 로직 처리 및 동적인 컨텐츠 제공을 위해 어플리케이셔을 수행해주는 미들웨어라고 생각하면 된다. Django 혼자만으로는 웹 서버를 구동할 수 없다. (django에서 제공하는 runserver는 개발용으로 사용되는 경량 웹 서버로, 실제 production 용으로는 적합하지 않다.)

실제 Django 어플리케이션을 서비스할 때에는 NginX, Apache 등의 웹 서버를 사용하고, 그 웹 서버와 Django 어플리케이션을 연결해 주는 Gunicorn, uWSGI 등의 WSGI를 이용하여 배포해야 한다.

 

 

Gunicorn과 같은 WSGI만 있어도 HTTP Request를 처리할 수 있지만, Web Server에서 제공하는 다음과 같은 기능들은 지원되지 않으므로, 실제 production 환경에서는 Web Server를 함께 사용하는 것이 좋다.

  • Static, Dynamic Request에 대한 처리: media, css등 static한 요청을 직접 처리하고, dynamic한 요청을 WSGI로 넘긴다. (WSGI로 요청이 넘어가는 순간 크게 느는 자원 사용을 줄일 수 있다.)
  • 적은 메모리 사용: NginX는 C로 구현되어 있기 때문에 속도나 메모리 사용 측면에서 뛰어나다. - SSL Termination: HTTPS에 대한 처리가 가능하다.

웹 서버와 WSGI를 함께 연동하여 서비스를 제공하면, 동시에 많은 요청을 처리할 수 있고, 훨씬 안정화된 서버를 구축할 수 있다.

 

Gunicorn

 

다음 명령어를 통해 Gunicorn 모듈을 설치할 수 있다.

(env) $ pip install gunicorn

설치가 완료되면, Gunicorn을 이용하여 Django 어플리케이션을 실행시켜 보자.

(env) $ gunicorn --bind 0.0.0.0:8000 config.wsgi:application

실행이 완료되면, 브라우저를 통해 접속이 가능한지 확인한 후, 해당 모듈을 requirements.txt 에 추가한다.

(env) $ pip freeze > requirements.txt

Dockerfile for Django

 

Django 어플리케이션을 위한 Docker 파일을 생성해 보자. 프로젝트 최 상위 위치에 Dockerfile 을 생성하고 다음과 같이 작성한다.

FROM python:3.6

# 컨테이너 내 프로젝트 root directory 설정
WORKDIR /usr/src/app

# 필요한 module 설치
COPY requirements.txt ./
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

# 프로젝트 코드 복사
COPY . .

### 이 아래 command들은 docker-compose에 작성할 내용이므로, 확인 후 삭제한다.
# 포트 설정
EXPOSE 8000
# gunicorn 실행
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"]

이미지를 빌드해 보고 정상적으로 수행이 되는지 확인해 보자.

$ docker build -t 'account'/'image':'version' .

account, image, version 에는 적절한 값을 넣어 빌드한다.

$ docker run -it -d -p 8000:8000 'account'/'image':'version'

로컬 PC의 브라우저에서 127.0.0.1:8000 으로 접속해 보자. 페이지에 접근이 가능하면, 정상적으로 빌드 및 실행이 된 것이다. (아직 static file들은 처리가 되지 않았으므로, format이 깨져 보일 수 있다.)

Docker-compose

 

하나의 Container안에 Djanago 어플리케이션과 NginX를 함께 실행할 수도 있지만, 각각의 Container로 분리하고 따로 관리하는 것이 효율적이다. Docker-compose를 이용하면 쉽게 여러 개의 Container를 생성하고, 이들을 연결해 줄 수 있다.

우선 NginX 를 위한 conf 파일을 작성해 보자. 원하는 위치에 적당한 이름의 디렉터리를 생성하고(이 예제에서는 프로젝트 최 상위 위치에 nginx 라는 디렉터리를 생성), nginx.conf 파일을 생성하고 다음과 같이 작성한다.

# nginx/nginx.conf
upstream web {
    ip_hash;
    server web:8000;
}
server {
    location / {
        proxy_pass <http://web/;>
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;          
    }
    location /static {
        alias /.static_root/;
    }
    listen 80;
    server_name localhost;
}

upstream  proxy_pass 에 작성된 web 은 이후 docker-compose에서 작성할 서비스 이름을 의미한다. (listen 포트를 80으로 설정하고, upstream 으로는 8000으로 전달하므로, 브라우저에서는 포트번호 없이 접속이 가능하다.)

Dockerfile과 마찬가지로 프로젝트 최 상위 위치에 docker-compose.yaml 파일을 생성하고 다음과 같이 작성한다.

# docker-compose.yml
version: "2"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80/tcp"
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./.static_root:/.static_root
    depends_on:
      - web
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: web
    command: >
        bash -c "python manage.py collectstatic --noinput
        && python3 manage.py migrate
        && gunicorn config.wsgi:application --bind 0.0.0.0:8000 --log-level=debug --access-logfile=- --log-file=-"
    volumes:
      - ./.static_root:/usr/arc/app/staticfiles
    expose:
      - "8000"

nginx와 web , 두 개의 서비스를 작성하였다. nginx 에서는 NginX 설정 파일 및 static 파일들의 경로를 설정해 준다. web 에서는 앞서 작성한 Dockerfile 을 설정해 주고, gunicorn을 통해 Django 어플리케이션을 실행시킨다. (DB를 이용하는 경우, DB도 별도의 서비스로 구성하는 것이 좋다.)

 

다음 명령어를 통해 빌드 및 실행을 할 수 있다.

$ docker-compose up --build