2015년 6월 30일 화요일

우분투14.04 에서 Django + uWSGI + Nginx 설치하기

AWS EC2 Instance 를 생각지도 못한 일로 새로 설치하고 기존에 셋팅되어 있던 것들이 전부 날라가서 웹서버부터 다시 설치해야 했다. 이참에 다시 공부한다는 생각으로 구글링해서 나름 괜찮은 블로그 포스팅을 찾았다. 영문이라 번역도 해볼겸 기록으로 남긴다.

개요

Django 는 Python 애플리케이션이나 웹사이트를 개발하는데 도움을 주는 강력한 웹 프레임워크이다. Django 는 코드를 자체적으로 테스트하기 위한 간단한 웹서버를 가지고 있는데, 제품출시를 위해 이것보다는 좀 더 보안이 좋고 강력한 웹서버가 필요하다.

본 가이드에서는 우분투 14.04 에 Django 애플리케이션을 지원하는 몇가지 Component 들을 어떻게 설치하고 설정하는지 시연할 것이다. 그리고 Django 애플리케션을 위한 uWSGI 애플리케이션 컨테이너 서버에 대해서 설정하고, uWSGI 와의 역프록시를 위한 Nginx를 설치해서 Django 애플리케이션의 보안과 성능을 높이고자 한다.

선결 조건과 목표

본 가이드를 마치기 위해 새로운 우분투 14.04 서버 인스턴스와 sudo 를 사용할 수 있는 root 가 아닌 계정이 필요하다. 우분투 서버 설정은 이곳 에서 배울수 있다.

두개의 다른 가상환경에 Django 를 설치하고 각각의 설정을 따로 핸들링하도록 할 것이다. 두개의 샘플 프로젝트를 생성함으로써 멀티 프로젝트 환경에 한걸음 다가설 수 있다.

Django 애플리케이션을 설치한 후 uWSGI 애플리케이션 서버를 설치하고 설정할 것이다. 이 서버는 HTTP 를 통한 사용자 리퀘스트를 Django 애플리케이션이 처리할수 있는 Python 으로 변환하는 인터페이스 기능을 한다. 그리고 uWSGI 앞단에 Nginx를 설치해서 고성능의 연결 핸들링 메카니즘과 보안모듈을 손쉽게 탑재할수 있는 이점을 가져간다.

자, 이제 시작해보자.

VirtualEnv 와 VirtualEnvWrapper 설치 및 설정하기

먼저 Django 프로젝트와 필요한 사항들을 각각의 가상환경에 설치한다. 이를 위해서, Python 가상 환경을 생성해주는 virtualenv 를 설치하고 virtualenv 작업을 효율적으로 개선시켜주는 virtualenvwrapper 를 설치하자.

이 두 모듈을 Python 패키지 매니저인 pip 를 통해서 설치한다. pip 는 우분투 레포지토리에서 apt-get 으로 설치할 수 있다.
$ sudo apt-get update
$ sudo apt-get install python-pip
본 가이드에서는 Python 2.x 버전을 사용한다. Python 3.x 버전을 사용하는 사용자는 python3-pip 패키지로 설치할 수 있다. 그리고 pip 대신 pip3 명령어를 사용하면 된다.

이제 pip 가 설치되었으면, virtualenv virtualenvwrapper 를 설치할 수 있게 된다.
$ sudo pip install virtualenv virtualenvwrapper
두개의 모듈이 설치되었으면, 이제 shell 에서 virtualenvwrapper 스크립트 사용을 위한 설정을 해야한다. 가상환경은 간편하게 접근하기 위해서 home 폴더안에 Env 폴더에 위치시킨다. WORKON_HOME 이라는 환경변수를 생성하고, 이것으로 virtualenvwrapper 스크립트를 실행한다.

만약 Python3와 pip3 를 사용한다면, 아래 라인을 shell 초기화 스크립트에 추가해야 한다.
$ echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3"
어떤 Python 버전을 사용하는 가에 상관없이, 아래 라인을 실행하자.
$ echo "export WORKON_HOME=~/Env" >> ~/.bashrc
$ echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc
이제 설정한 Shell 초기화 스크립트를 실행하면 된다.
$ source ~/.bashrc
그리고나면, 가상환경 정보 저장을 위한 Env 폴더가 home 폴더안에 생성되게 된다.

Django 프로젝트 생성하기

이제 가상환경툴이 생겼다. 이것으로 두개의 가상환경을 생성하고 각각의 가상환경에 Django를 설치하고 프로젝트를 생성해보자.

첫번째 프로젝트 생성하기

virtualenvwrapper 스크립트를 이용해서 간단하게 가상환경을 생성할 수 있다.

첫번째 가상환경을 생성해보자
$ mkvirtualenv firstsite
위 명령어는 가상환경을 생성 후 가상환경에 포함된 Python과 pip 를 설치하고 가상환경을 동작시킨다. 가상환경이 동작되면 현재 새로운 가상환경이 동작하고 있다고 알려주는 프롬프트로 아래와 같이 변경된다. 괄호안의 값은 위에서 설정한 가상환경의 이름으로 나온다.
(firstsite)user@hostname:~$
이제부터 pip 로 설치하는 모든 모듈은 전체시스템이 아니라 가상환경 상에 설치가 되어지고 프로젝트 별로 패키지를 관리할 수 있게 된다.

sudo 없이 pip 를 이용해 가상환경에 Django 를 설지하고, 첫번째 Django 프로젝트를 만들어보자.
(firstsite)user@hostname:~$ pip install django
(firstsite)user@hostname:~$ django-admin.py startproject firstsite
home 폴더 안에 firstsite 라는 하위폴더가 생성된다. 샘플 프로젝트를 설정하기 위해서 firstsite 폴더로 이동하자
(firstsite)user@hostname:~$ cd ~/firstsite

프로젝트에서 사용 할 SQLite 데이터베이스를 초기화 하자. 원할경우 SQLite 대신 다른 데이터베이스를 설정해도 되지만, 본 가이드에서는 언급하지 않겠다.
$ ./manage.py migrate
$ python manage migrate
이제 프로젝트 폴더에 db.sqlite3 라는 데이터베이스 파일을 가지게 될 것이다. 그리고 어드민 계정을 하나 생성하자.
$ ./manage.py createsuperuser
$ python manage createsuperuser
username, email address, password, confirm password 를 입력하게 된다.

다음으로, 프로젝트 설정파일을 오픈하자. (원문에서는 nano를 썼는데, 아무래도 vi 가 손에 익어서 vi 로 대체 하였다.)
$ vi firstsite/settins.py
Nginx 로 Django 사이트를 서비스하려면, 사이트에서 사용할 static 파일들을 저장할 폴더를 설정하는 것이 필요하다. Nginx 가 직접 static 파일들을 제공하도록 하면 성능에도 긍정적인 영향을 주게 된다. Django 에게 static 파일들을 프로젝트 기본폴더 안 static 폴더안에 위치시키라고 알려주기 위해 아래 라인을 설정파일의 맨하단에 추가한다.
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
추가가 끝나면 저장하고 Django 사이트에서 사용할 static 파일들을 해당폴더에 수집하는 작업이 필요하다.
$ ./manage.py collectstatic
$ python manage collectstatic
yes 를 입력하고 확인하면 static 파일들을 수집하고 프로젝트 폴더에 static 폴더가 생성된다.

위 일련의 과정이 마무리되면, 이제 첫번째 프로젝트를 개발서버로 실행할 수 있다.
$ ./manage.py runserver 0.0.0.0:8000
$ python manage runserver 0.0.0.0:8000
개발서버 8000번 포트를 이용해서 시작되는데, 아래와 같이 서버의 도메인이름 혹은 IP 주소뒤에 8000 포트를 붙여서 브라우저에서 열어보자.
http://server_domain_or_IP:8000

It worked! 페이지가 제대로 보이면 성공한 것이다.
http://server_domain_or_IP:8000/admin
URL 맨끝에 /admin 을 붙이면 어드민 페이지 접속할수 있고, 아까 생성했던 어드민 계정으로 로그인 할 수 있다.

이제 실행중인 개발서버를 CTRL-C 로 종료시키고, 두번째 프로젝트를 생성해 보자.

두번째 프로젝트 생성하기

두번째 프로젝트 역시 첫번째와 동일하다. 프로젝트를 한번 생성해봤기 때문에 이번 섹션은 간단하게 축약해서 설명하도록 하겠다.

다시 home 폴더로 돌아와서 두번째 가상환경을 생성하고 Django 를 설치하자.
(firstsite)user@hostname:~/firstsite$ cd ~
(firstsite)user@hostname:~$ mkvirtualenv secondsite
(secondsite)user@hostname:~$ pip install django
(secondsite)user@hostname:~$ django-admin.py startproject secondsite
(secondsite)user@hostname:~$ cd secondsite
(secondsite)user@hostname:~/secondsite$
새로운 가상환경을 생성하면 첫번째 가상화면은 종료되고, 생성한 새로운 가상화면으로 변경된다. 여기서 설치한 Django 는 이전에 설정했던 것과는 완전히 분리된다. 즉, 필요에 따라 독립적으로 관리할 수 있게된다.

첫번째 프로젝트와 마찬가지 방식으로 설정해보자.
$ ./manage.py migrate
$ ./manage.py createsuperuser
$ vi secondsite/settings.py
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
$ ./manage.py collectstatic
$ ./manage.py runserver 0.0.0.0:8080
http://server_domain_or_IP:8080
http://server_domain_or_IP:8080/admin

가상환경 종료하기

가상환경 종료를 위해서는 아래 명령어를 입력하면 된다.
$ deactivate

가상환경 종료 후 설정한 가상환경을 다시 불러서 작업을 하기 위해서는 workon 명령어를 사용하면 된다.
$ workon firstsite
또는,
$ workon secondsite
마찬가지로 종료하기 위해서는 deactivate 명령어를 사용하면 된다.

uWSGI 애플리케이션 서버 설정하기

이제 두개의 Django 프로젝트 설정을 마쳤고, uWSGI 설정을 할 준비가 되었다. uWSGI 는 Django 애플리케이션과 WSGI 라는 표준 인터페이스로 통신하는 애플리케이션 서버이다. 우분투에서 Nginx 와 uWSGI 를 설정하느 더 자세한 사항은 여기에서 확인할 수 있다.

uWSGI 설치하기

위에서 제공한 링크와는 다르게, 본 가이드에서는 uWSGI 를 전체시스템에 설치할 것이다. 이렇게 함으로써, 멀티 Django 프로젝트를 관리할때 충돌을 줄여줄 수 있다. uWSGI 를 설치하기 전에 uWSGI 와 의존성이 있는 Python Development 파일을 먼저 아래와 같이 설치해야 한다.
$ sudo apt-get install python-dev
development 파일들이 설치되면, pip 를 통해서 전체시스템에 uWSGI를 설치하자.
$ sudo pip install uwsgi
설치 후 uWSGI를 간단하게 테스트 해볼수 있다. 아래와 같이 첫번째 프로젝트를 테스트해보자.
$ uwsgi --http :8080 --home /home/user/Env/firstsite --chdir /home/user/firstsite -w firstsite.wsgi
여기에서, 가상환경 ~/Env, 프로젝트폴더 firstsite 그리고 프로젝트폴더에 포함된 wsgi.py 파일을 사용할수 있게 uWSGI 에 알려주었다. 위 테스트에서는 8080 포트를 이용해서 HTTP 로 프로젝트를 서비스했고, 도메인 혹은 IP 에 :8080 을 붙여서 브라우저에서 오픈하면 runserver 와 동일하게 Django 사이트가 동작하는 것을 확인할 수 있다.(그렇지만 /admin 의 static 엘리먼트들은 아직 동작하지 않는다.) CTRL-C로 테스트를 종료할 수 있다.

설정파일 생성하기

커맨드라인에서 uWSGI 를 동작시키는 것은 테스트 할때 유용하다. 그렇지만, 실제 배포환경에서는 특별히 도움이 되지 않는다. 그래서 uWSGI 를 독립된 애플리케이션들을 자동으로 관리해주는 "Emperor mode" 로 동작시킬 것이다.

우선, 설정파일을 저장할 폴더를 생성하자. 전체시스템에 적용되어야 하기 때문에 /etc/uwsgi/sites 폴더를 생성하고 설정파일을 저장할 것이다. 폴더를 생성하고 이동하자.
$ sudo mkdir -p /etc/uwsgi/sites
$ cd /etc/uwsgi/sites
위 폴더에 서비스할 프로젝트의 설정파일을 각각 생성해야 한다. uWSGI 프로세스는 다양한 포맷의 설정파일을 가져올수 있지만, 본 가이드에서는 간단하게 .ini 파일을 사용할 것이다.
$ sudo vi firstsite.ini
[uwsgi]
project = firstsite
base = /home/user

chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

master = true
processes = 5

socket = %(base)/%(project)/%(project).sock
chmod-socket = 664
vacuum = true
설정파일의 시작은 무조건 [uwsgi] 로 시작하여야 하고 모든 설정은 이 섹션아래에 있어야 한다. project 이름과 home 폴더는 재사용하기 위해서 변수 project base 로 만들어준다.

chdir 에 프로젝트 경로, home 에 가상환경의 경로를 설정하고, 프로젝트와 어떻게 상호작용할지를 알려준다(프로젝트폴더에 있는 wsgi.py 에서 "application" 을 임포트한다.)

마스터 프로세스와 5개의 워커를 설정해준다.

다음으로, uWSGI 가 어떻게 네트웤을 연결해야할지를 설정한다. 위 테스트에서는 HTTP 와 네트웤 포트를 사용했지만, Nginx 를 역프록시로 사용할 것이기 때문에 더 좋은 옵션이 있다.

모든 모듈들이 하나의 서버에서 동작하기 때문에 네트웤 포트를 사용하는 대신 더욱 안전하고 성능좋은 유닉스소켓을 사용할 수 있다. 유닉스소켓은 HTTP 를 사용하지 않고, 다른 서버와 통신하기 위해 디자인된  바이너리 프로토콜인 uWSGI의 uwsgi 프로토콜을 사용한다. Nginx 는 기본적으로 uwsgi 프로토콜을 사용할수 있기 때문에 유닉스소켓을 사용하는 것이 가장 좋은 선택이라고 볼수있다.

또한 웹서버가 쓰기권한을 가질수 있도록 소켓의 권한을 변경하여야 한다.

마지막으로 서버가 Stop 되었을때 자동으로 소켓파일이 삭제되도록 vacuum 옵션을 준다.

이것으로 첫번째 프로젝트의 uWSGI 설정이 완료되었다.

설정파일에 변수를 사용하는 것의 장점은 재사용하기가 놀랍도록 간단하다는 것이다. 첫번째 프로젝트의 설정파일을 복사해서 두번째 프로젝트의 설정파일로 만들어 보자.
$ sudo cp /etc/uwsgi/sites/firstsite.ini /etc/uwsgi/sites/secondsite.ini
두번째 프로젝트 설정파일을 열고 project 변수의 이름을 두번째 프로젝트의 이름으로 변경해주면 된다.
$ sudo vi /etc/uwsgi/sites/secondsite.ini
[uwsgi]
project = secondsite
base = /home/user

chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

master = true
processes = 5

socket = %(base)/%(project)/%(project).sock
chmod-socket = 664
vacuum = true
이제 두번째 프로젝트 설정파일도 준비되었다.

uWSGI 를 위한 Upstart 스크립트 생성하기

Django 프로젝트를 서비스하기 위해서 필요한 설정파일이 준비되었다. 그렇지만, 아직 자동화된 프로세스는 아니기 때문에 부팅시에 uWSGI를 자동으로 시작시켜주기위한 Upstart 스크립트를 생성할 것이다.

Upstart 스크립트 파일은 /etc/init 폴더에 생성한다.
$ sudo vi /etc/init/uwsgi.conf
description "uWSGI application server in Emperor mode"

start on runlevel [2345]
stop on runlevel [!2345]

setuid user
setgid www-data

exec /usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
uWSGI 서비스를 위한 설명문을 작성하고 자동으로 동작될때의 런레벨을 알려준다. 여기서는 2,3,4,5의 런레벨로 설정한다.

다음으로 프로세스가 어떤 유저와 그룹으로 동작될지를 설정한다. 유저는 지금까지 파일을 생성한 권한이 있는 사용자로 설정을 하도록 하고 그룹은 www-data 로 설정한다. uWSGI 설정파일에서의 소켓설정은 웹서버가 소켓에 쓰기권한을 가지고 있어야 한다.

마지막으로, 실제 실행 명령어를 입력한다. 설정파일이 있는 폴더를 입력하고 uWSGI를 Emperor 모드로 시작한다. uWSGI 가 해당 파일들을 읽고 각 프로젝트를 서비스하게 된다.

완료되면 저장하고 종료하자. Nginx를 설치하기전까지는 www-data 그룹이 없기 때문에 아직 uWSGI 를 시작하지 않는다.

Nginx 설치하기 및 역프록시 설정하기

uWSGI 설정을 마치고 사용할 준비가 되었다. 이제 Nginx를 설치하고 역프록시로 설정해보자.
$ sudo apt-get install nginx
Nginx 가 설치되면 각 프로젝트에 대한 서버블록 설정파일을 만들수 있다. 첫번째 프로젝트에 대한 서버블록 설정파일부터 만들어보자.
$ sudo vi /etc/nginx/sites-avaliable/firstsite
server {
    listen 80;
    server_name firstsite.com www.firstsite.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/firstsite;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/user/firstsite/firstsite.sock;
    }
}
첫번째 프로젝트가 접속할 서버 도메인 이름과 포트 숫자를 지정한다. 다음으로, Favicon 이 없어도 오류를 발생시키지 않도록 설정하고, 정적파일의 폴더를 지정한다.
uswgi_pass 를 이용해서 트래픽을 프로젝트 폴더에 있는 firstproject.sock 소켓파일로 전달하도록 지정한다. 또한, include 를 이용해 네트웤 연결 핸들링에 필요한 uwsgi 파라메터를 포함시킨다.

위에서 생성한 설정파일을 복사해서 두번째 프로젝트용 Nginx 설정파일을 만든다.
$ sudo cp /etc/nginx/sites-available/firstsite /etc/nginx/sites-available/secondsite
설정파일을 오픈해서 firstsite 를 secondsite 로 변경하고 server_name 에 사용할 도메인을 입력한다.
$ sudo vi /etc/nginx/sites-avaliable/secondsite
server {
    listen 80;
    server_name secondsite.com www.secondsite.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/secondsite;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/user/secondsite/secondsite.sock;
    }
}
다음으로, Nginx 의 sites-enabled 폴더에 위 설정파일들의 링크를 생성한다.
(기본적으로 설정파일들을 만들때는 sites-available 폴더에 만들어 놓고, 서비스를 운영할 때 설정파일들을 sites-enabled 폴더로 링크복사해서 사용한다.)
$ sudo ln -s /etc/nginx/sites-available/firstsite /etc/nginx/sites-enabled
$ sudo ln -s /etc/nginx/sites-available/secondsite /etc/nginx/sites-enabled
Nginx 설정파일이 정상적으로 만들어졌는지 체크해보자.
$ sudo service nginx configtest
설정파일이 문제가 없으면 Nginx 를 재시작해서 설정파일을 다시 로딩한다.
$ sudo service nginx restart
아직 uWSGI 를 시작한적이 없기때문에 uWSGI 역시 동작시킨다.
$ sudo service uwsgi start
이제 전에 만들었던 두 프로젝트 모두에 접근할수 있게 된다. 기대한대로 일반 사이트와 어드민 사이트가 제대로 동작할 것이다.

결론

본 가이드에서, 각각의 가상환경을 가진 두개의 Django 프로젝트를 생성하고 설정했다.  또한 각각의 가상환경을 사용하는 독립적인 프로젝트를 서비스하기 위해 uWSGI 도 설정했다. 그리고나서, 클라이언트 연결을 핸들링하고 클라이언트 리퀘스트에 따라 알맞은 프로젝트를 서비스하는 역프록시 역할의 Nginx 도 설정했다.

Django 는 제공되는 수많은 모듈들로 프로젝트와 애플리케이션 생성을 간단하게 만들어주고, 개발자는 특별한 요소에만 집중할수 있도록 해준다. 본 가이드에서 설명하고 있는 일반적인 툴체인들을 지렛대삼아 싱글서버에서 더욱 쉽게 Django 애플리케이션을 서비스 할수 있길 바란다.

댓글 3개:

  1. 안녕하세요...장고를 스터디ㅣ중입니다..도움이 많이되고 있습니다
    주신자료를 참조하여 ...django+ngix+gunicorn으로 설정은 했는대..

    이상하게 , 소스코드를 변경(모델에 필드추가)하고 저장하면 브라우에서 바로바로 변경이 않되고..우분투를 OS를 restart 해야 변경된 사항이 브라우져에서 보입니다.

    예전에..python manage.py runserver 0:8000 할때는 바로바로 ㅂ소스코드변경을 확인가능했느데..제가 뭘 setting을 잘 못했느지요? ..아니면 restart django 같은것이 혹시 있느지요?


    답글삭제
  2. Failed to start uwsgi.service: Unit uwsgi.service not found. 마지막 uwsgi start 명령어에서 이런 에러가 발생합니다...

    답글삭제