Docker로 Django 애플리케이션 구축하기

이재홍 http://www.pyrasis.com 2014.08.02 ~ 2014.09.20

Django Dockerfile 작성하기

Django 설치가 끝났으니 예제 Django 애플리케이션을 생성합니다.

~$ django-admin.py startproject exampleapp

exampleapp 디렉터리가 생성되었습니다. 앞에 설치한 Oracle 인스턴트 클라이언트 rpm 파일을 exampleapp 디렉터리 아래로 이동합니다. Django 이미지에도 Oracle 인스턴트 클라이언트를 설치해야 하므로 rpm 파일이 필요합니다.

$ mv oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm exampleapp/
$ mv oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm exampleapp/

exampleapp/exampleapp 디렉터리 아래에 있는 settings.py 파일을 열고 다음과 같이 수정합니다.

~/exampleapp/exampleapp/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.oracle',
        'NAME': 'XE',
        'USER': 'system',
        'PASSWORD': os.getenv('DB_ENV_ORACLE_PASSWORD'),
        'HOST': os.getenv('ORACLE_HOST') or 'db',
        'PORT': '1521',
    }
}
  • ENGINE: Oracle을 사용하기 위해 django.db.backends.oracle을 설정합니다.
  • NAME: 데이터베이스 이름입니다. XE를 설정합니다.
  • USER: system을 설정합니다.
  • PASSWORD: 환경 변수의 DB_ENV_ORACLE_PASSWORD를 사용하도록 설정합니다. docker run 명령의 --link 옵션으로 컨테이너를 연결했을 때 연결한 컨테이너의 환경 변수는 <별칭>_ENV_<환경 변수> 형식입니다. 우리는 컨테이너를 연결할 때 별칭을 db로 하고, 데이터베이스 컨테이너에서 환경 변수는 ORACLE_PASSWORD를 사용할 것이기 때문에 DB_ENV_ORACLE _PASSWORD가 됩니다.
  • HOST: or 연산자를 이용하여 개발 환경과 데이터베이스 컨테이너에서 사용할 데이터베이스 호스트를 각각 설정합니다. 개발을 할 때는 환경 변수의 ORACLE_HOST에 데이터베이스 컨테이너의 IP 주소를 설정합니다. 그리고 데이터베이스 컨테이너를 연결할 때는 별칭을 db로 할 것이므로 db로 설정합니다.
  • PORT: 1521번을 설정합니다.

MySQL, PostgreSQL 사용하기
다음은 MySQL 사용 설정입니다.

~/exampleapp/exampleapp/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'exampleapp',
        'USER': 'root',
        'PASSWORD': os.getenv('DB_ENV_MYSQL_ROOT_PASSWORD'),
        'HOST': os.getenv('MYSQL_HOST') or 'db',
        'PORT': '3306',
    }
}

다음은 PostgreSQL 사용 설정입니다.

~/exampleapp/exampleapp/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'exampleapp',
        'USER': 'postgres',
        'PASSWORD': os.getenv('DB_ENV_POSTGRESQL_PASSWORD'),
        'HOST': os.getenv('POSTGRESQL_HOST') or 'db',
        'PORT': '5432',
    }
}

다음 내용을 Dockerfile로 저장합니다.

~/exampleapp/Dockerfile

FROM centos:centos7

RUN yum install -y http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-2.noarch.rpm
RUN yum install -y python-pip python-devel nginx gcc

RUN echo "daemon off;" >> /etc/nginx/nginx.conf
ADD exampleapp.conf /etc/nginx/conf.d/exampleapp.conf

WORKDIR /tmp
ADD oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm /tmp/
ADD oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm /tmp/
RUN yum install -y oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm
RUN yum install -y oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm
RUN rm -rf *.rpm

ENV ORACLE_HOME /usr/lib/oracle/12.1/client64
RUN echo "/usr/lib/oracle/12.1/client64/lib" > /etc/ld.so.conf.d/oracle.conf && ldconfig
RUN pip install django gunicorn cx_Oracle

ADD ./ /var/www/exampleapp
WORKDIR /var/www/exampleapp
RUN chmod +x entrypoint.sh
RUN rm -rf *.rpm

EXPOSE 80

ENTRYPOINT ./entrypoint.sh

지금까지 우분투 14.04를 기반으로 이미지를 구성해보았으니 이번에는 CentOS 7을 기반으로 하겠습니다.

  • FROM으로 centos:centos7를 기반으로 이미지를 생성하도록 설정합니다.
  • yum install 명령으로 EPEL 패키지를 설치하고, python-pip, python-devel, nginx, gcc를 설치합니다.
  • nginx를 데몬이 아닌 foreground로 실행하도록 설정합니다. nginx를 데몬 상태로 실행하면 Docker 컨테이너가 바로 정지되므로 주의합니다.
  • /etc/nginx/conf.d 디렉터리에 exampleapp.conf 파일을 추가합니다.
  • /tmp 디렉터리에 Oracle 인스턴트 클라이언트 rpm 파일을 추가한 뒤 yum install 명령으로 설치합니다. 그리고 설치가 끝나면 rpm 파일을 삭제합니다.
  • ENV로 ORACLE_HOME 환경 변수를 설정합니다. /usr/lib/oracle/12.1/client64는 Oracle 인스턴트 클라이언트가 설치된 경로입니다.
  • /etc/ld.so.conf.d 디렉터리에 oracle.conf 파일을 생성하고, ldconfig 명령을 실행하여 Oracle 인스턴트 클라이언트의 동적 라이브러리를 사용할 수 있도록 설정합니다.
  • pip install 명령으로 django, gunicorn, cx_Oracle을 설치합니다.
  • Django 애플리케이션 디렉터리를 /var/www/exampleapp 디렉터리에 추가합니다.
  • entrypoint.sh 파일을 실행할 수 있도록 권한을 설정합니다.
  • EXPOSE에 80을 설정하여 80번 포트에 접속할 수 있도록 합니다.
  • ENTRYPOINT에 ./entrypoint.sh 파일을 설정하여 컨테이너가 시작되었을 때 스크립트 파일을 실행합니다.

Django 애플리케이션 디렉터리(exampleapp)를 추가하기 전에 oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm, oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm 파일을 미리 /tmp 디렉터리에 추가하여 설치합니다. exampleapp 디렉터리를 추가한 뒤 rpm 파일을 설치하면 exampleapp 디렉터리의 내용이 바뀔 때마다 rpm 파일을 설치하게 되므로 이미지 생성 시간이 오래걸립니다. 자세한 내용은 ‘17.2 Rails Dockerfile 작성하기’의 ‘Docker 이미지 생성 시간 줄이기’ 부분을 참조하기 바랍니다.

MySQL, PostgreSQL 사용하기

다음은 MySQL 사용 설정입니다.

~/exampleapp/Dockerfile

RUN yum install -y python-pip python-devel nginx gcc mysql-devel

RUN pip install django gunicorn MySQL-python

다음은 PostgreSQL 사용 설정입니다.

~/exampleapp/Dockerfile

RUN yum install -y python-pip python-devel nginx gcc postgresql-devel

RUN pip install django gunicorn psycopg2

이제 Nginx 설정 파일을 작성합니다. 다음 내용을 exampleapp.conf 파일로 저장합니다.

~/exampleapp/exampleapp.conf

upstream gunicorn {
  server unix:/tmp/gunicorn.sock;
}

server {
  listen 80;
  server_name _;

  location /static/ {
    alias /var/www/exampleapp/static/;
  }

  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    if (!-f $request_filename) {
      proxy_pass http://gunicorn;
      break;
    }
  }
}
  • upstream 항목에는 Gunicorn의 유닉스 소켓 /tmp/gunicorn.sock을 설정합니다.
  • server 항목을 설정합니다.
    • listen 80을 설정하여 80번 포트를 사용합니다.
    • Nginx는 정적 파일(html, js, css 등)을 전송할 것이므로 Django 애플리케이션 디렉터리 아래의 static 디렉터리를 설정합니다(각자 상황에 맞게 수정합니다).
    • Nginx로 들어온 요청이 파일명이 아니면 앞에서 설정한 유닉스 소켓(http://gunicorn)으로 보냅니다. 이렇게 설정하면 정적 파일은 Nginx가 전송하고, 나머지 RESTful API는 Django가 처리하게 됩니다.

다음 내용을 entrypoint.sh로 저장합니다.

~/exampleapp/entrypoint.sh

#!/bin/bash

gunicorn exampleapp.wsgi -b unix:/tmp/gunicorn.sock -D
nginx
  • gunicornexampleapp.wsgi를 설정합니다. <Django 애플리케이션 이름>.wsgi 형식입니다.
    • -b 옵션을 사용하여 /tmp/gunicorn.sock에 유닉스 소켓을 생성합니다.
    • -D 옵션을 사용하여 데몬 모드로 실행합니다.
  • 앞에서 nginx.confdaemon off;로 설정했으므로 Nginx 웹 서버를 foreground로 실행합니다. 여기서 Nginx를 foreground로 실행하지 않으면 docker run -d로 컨테이너를 생성해도 바로 정지되므로 주의합니다.

docker build 명령으로 이미지를 생성합니다.

~/exampleapp$ sudo docker build --tag django .

저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

30 November 2014