ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django]uWSGI (우분투 + 장고 + nginx)
    언어/파이썬 & 장고 2016. 1. 23. 23:00


    Django는 파이썬에서 제공하는 웹프레임워크입니다. Django에는 코드를 자체적으로 테스트하기 위한 간단한 웹서버를 가지고 있고, 이를 실행하는 명령어는 runserver입니다. 

    제품출시를 할 때에는 Django에서 제공하는 간단한 웹서버를 사용하는 것이 아닌, 보안이 더 좋고 강력한 웹서버가 필요합니다. 지금부터는 runserver로 Django 자체 웹서버를 실행하는 것이 아닌, uWSGI를 사용하여 Nginx와 연동하는 법을 설명하겠습니다.


    사양

    VIRTUAL BOXUbuntu-64bit 14.04 LTS
    RAM2GB
    HDD8GB
    pythonpython 3.4.3
    djangodjango 1.8.7


    uWSGI 지원 플랫폼/시스템


    개발에 앞서, uWSGI는 전 OS에 지원하는 것이 아닌, 특정 OS/System에만 지원을 합니다. 윈도우의 경우, uWSGI를 지원하지 않아서 pip로 다운로드가 불가능하며 동작하지 않습니다. 따라서 Virtual Box에 우분투 14.04를 설치하여 테스트 하였습니다. 

    Platforms/Systems

    Linux 2.6/3.x

    FreeBSD >= 7

    NetBSD

    OpenBSD

    DragonFlyBSD

    Windows Cygwin

    Mac OSX

    Solaris >= 10

    NexentaOS

    SmartOS

    OpenSolaris

    OpenIndiana

    OmniOS

    Debian/kFreeBSD

    GNU/Hurd


    virtualenv 세팅


    먼저 virtualenv를 설정해야 합니다. virtualenv를 사용하는 이유는 각 프로젝트 별로 필요로 하는 개발 환경이 다르기 때문입니다. 만약 이를 나누지 않고 하나로 사용하게 되면 전체 시스템에 패키지가 전체 설치가 되어 새로운 프로젝트에선 사용하지 않는 패키지가 존재하는 모습이 됩니다.


    아래 명령어를 실행시켜 pip을 설치합니다.

    $ sudo apt-get update
    $ sudo apt-get install python3-pip

    (본 가이드는 파이썬 3.4.3 버전을 사용하기 때문에 python3-pip를 다운받아 pip가 아닌, pip3 명령어를 사용합니다.)


    pip3이 설치가 되었으면 virtualenv를 설치할 수 있습니다.

    $ sudo pip3 install virtualenv


    설치가 완료된 다음, 다음과 같이 명령어를 작성하여 'uwsgi-tutorial'란 이름으로 virtualenv를 생성한 뒤, 해당 폴더로 들어가 활성화 시킵니다.

    $ virtualenv uwsgi-tutorial
    $ cd uwsgi-tutorial
    $ source bin/activate

    활성화가 제대로 되었을 경우, hostname@server:~/uwsgi-tutorial에서 (uwsgi-tutorial)hostname@server:~/uwsgi-tutorial 이렇게 바뀝니다.


    env가 활성화 되어있는 상태에서 django를 설치해줍니다. 

    $ pip3 install django==1.8.7


    설치가 완료된 후, 프로젝트를 생성한 다음, 해당 프로젝트로 이동합니다.

    $ django-admin.py startproject testproject
    $ cd uwsgi


    여기까지 정상적으로 진행했을 시, 파일은 다음과 같습니다.


    uWSGI 세팅


    먼저 uWSGI를 설치합니다. 이때, source bin/activate된 상태를 유지해야 됩니다.

    $ pip3 install uwsgi


    다음 test.py를 ~/uwsgi-tutorial에 생성합니다.

    #sudo vi test.py
    def application(env, start_response):
        start_response('200 OK', [('Content-Type','text/html')])
        return [b"Hello World"] # python3


    uwsgi로 test.py을 실행시켜 작동하는지 확인합니다.


    $ uwsgi --http :8000 --wsgi-file test.py


    127.0.0.1:8000을 접속하면 hello world가 생성되어 아래와 같은 구조로 통신하는 것을 확인할 수 있습니다.

    Web Client <-> uWSGI <-> Python


    testproject 프로젝트로 들어가 아래의 명령어를 실행시킨 후, 웹에서 확인을 해보면 It works라는 문구가 나오는 것을 알 수 있습니다.

    $ cd testproject
    $ python3 manage.py runserver 0.0.0.0:8000


    runserver로 실행시킨 것을 중지한 후, 이를 uWSGI로 변경해 실행시켜봅니다.

    $ uwsgi --http :8000 --module testproject.wsgi

    runserver로 실행시킨 것과 같이 웹에서 확인해보면 같은 문구가 나오는 것과 아래와 같은 구조로 통신하는 것을 알 수 있습니다.

    Web Client <-> uWSGI <-> Django


    Nginx 세팅


    이 가이드에서는 웹서버를 Nginx를 선택하였습니다.

    Nginx설치 후, virtualenv 폴더로 이동해 testproject_nginx.conf를 작성합니다.

    $ cd ~/uwsgi-tutorial
    $ sudo vi testproject_nginx.conf


    생성한 .conf파일에 필요한 부분을 고쳐줍니다.

     # testproject_nginx.conf
    # the upstream component nginx needs to connect to
    upstream django {
        # server unix:///path/to/your/mysite/mysite.sock; # for a file socket
        server 127.0.0.1:8001; # for a web port socket (we'll use this first)
    }
    # configuration of the server
    server {
        # the port your site will be served on
        listen      8000;
        # the domain name it will serve for
        server_name .jinhwan.com; # substitute your machine's IP address or FQDN
        charset     utf-8;
        # max upload size
        client_max_body_size 75M;   # adjust to taste
        # Django media
        location /media  {
            alias /home/jinhwan/uwsgi-tutorial/testproject/media;  # your Django project's media files - amend as required
        }
        location /static {
            alias /home/jinhwan/uwsgi-tutorial/testproject/static; # your Django project's static files - amend as required
        }
        # Finally, send all non-media requests to the Django server.
        location / {
            uwsgi_pass  django;
            include     /home/jinhwan/uwsgi-tutorial/testproject/uwsgi_params; # the uwsgi_params file you installed
        }
    }
    
    
    


    다음 심볼릭 링크를 생성합니다.

    $ cd /etc/nginx/sites-enabled
    $ sudo ln -s /home/jinhwan/uwsgi-tutorial/testproject_nginx.conf /etc/nginx/sites-enabled/


    다음 프로젝트 폴더로 이동한 다음 settings.py 파일을 아래와 같이 추가하고

    STATIC_ROOT = os.path.join(BASE_DIR, "static/")

    아래 명령어를 실행시킵니다.

    $ python3 manage.py collectstatic

    실행시킨 후, yes를 입력하면 static 폴더가 생성됩니다.

    해당 프로젝트 내에 uwsgi_params 파일을 만든 다음, https://github.com/nginx/nginx/blob/master/conf/uwsgi_params이 안에 있는 데이터를 넣고 저장합니다.

    Port을 사용한 uWSGI


    앞서 만들어 놓은 test.py로 nginx와 uWSGI 연동을 확인하는 명령어입니다. 

    $ uwsgi --socket :8001 --wsgi-file test.py


    웹에서 확인하기 위해선 URL:8001로 확인하는 것이 아닌, 8000 포트로 확인합니다. 아래와 같은 구조로 되어 있으면서 사용자가 8000번 포트로 접속하면 작성한 testproject_nginx.conf 파일 내용과 같이 소켓을 통해 8001번을 호출하게 됩니다. 8001번은 위 uWSGI에서 사용한 포트이므로 test.py을 실행하여 클라이언트에게 보여주게 됩니다.  

    the web client <-> the web server <-> the socket <-> uWSGI <-> Python

    Unix socket을 사용한 uWSGI


    앞서 uWSGI를 사용한 방법은 TCP port socket을 사용한 방법이였습니다. 이 방법을 사용하는 이유는 간단하기 때문입이다. 하지만 포트를 사용하는 것보단 유닉스 소켓을 사용하는 것이 오버헤드가 더 적기 때문에 좋습니다.

    앞에서 작성한 testproject_nginx.conf를 수정합니다.

    server unix:///tmp/testproject.sock; # for a file socket
    # server 127.0.0.1:8001; # for a web port socket (we'll use this first)

    nginx를 재시작 한 다음, 아래의 명령어를 실행합니다.

    $ uwsgi --socket /tmp/testproject.sock --wsgi-file test.py --chmod-socket=666

    --chmod-socket=666을 사용하는 이유는 권한이 없어 Permission Denied를 방지하기 위해 모든 사용자가 해당 파일에 대해 쓰기, 읽기 권한을 줍니다.

    (666 대신 664를 입력해도 되지만 664를 입력해도 에러가 나서 666밖에 사용하지 않았습니다.)


    만약 에러코드에 대해 확인해 보고 싶은 경우 /var/log/nginx/error.log 에서 확인할 수 있습니다.


    Unix Socket을 사용해 Django 실행


    먼저 프로젝트로 이동한 후, 해당 명령어를 입력, 웹에서 8000포트를 입력하면 It worked라는 문구가 나옵니다.

    $ cd testproject
    $  uwsgi --socket /tmp/testproject.sock --module testproject.wsgi --chmod-socket=666

    .ini파일을 사용하여 uWSGI 실행


    .ini파일을 설정하여 사용하는 이유는 uWSGI를 실행시키기 위해  공통적으로 사용되는 옵션들을 정리하여 보다 쉽게 구성하기 위함입니다.

    또한 매번 긴 파일 경로를 사용하지 않아도 됩니다.


    먼저 testproject_uwsgi.ini를 생성한 다음, 내부를 아래와 같이 작성합니다.

    # mysite_uwsgi.ini file
    [uwsgi]
    # Django-related settings
    # the base directory (full path)
    chdir           = /home/jinhwan/uwsgi-tutorial/testproject/
    # Django's wsgi file
    module          = testproject.wsgi
    # the virtualenv (full path)
    home            = /home/jinhwan/uwsgi-tutorial
    # process-related settings
    # master
    master          = true
    # maximum number of worker processes
    processes       = 1
    # the socket (use the full path to be safe
    socket          = /tmp/testproject.sock
    # ... with appropriate permissions - may be needed
     chmod-socket    = 666
    # clear environment on exit
    vacuum          = true


    다음, 아래의 명령어를 실행시켜 확인합니다.

    $ uwsgi --ini testproject_uwsgi.ini


    worker processes의 갯수는 어느정도가 적당할까?

    프로세스나 쓰레드의 갯수를 설정하기위한 규칙이나 제한은 없습니다. 해당 갯수는 시스템이나 응용 프로그램에 매우 의존적입니다. 이것의 갯수를 정하는 것은 processes = 2 * cpucores와 같은 단순한 수식으로는 적합하지 않습니다.

    항상 해당 app에 대해 다양한 설정과 끊임없는 모니터링을 하여 실험을 할 필요가 있습니다. uwsgitop은 이러한 것들을 도와주는 좋은 도구가 될 수 있습니다.

    시스템 전체에 uWSGI 설치


    시스템이 부팅되었을 때, 자동으로 uWSGI를 시작하기 위해선 virtualenv만 아닌, 시스템 전체에 설치가 되어야 합니다.


    아래 명령어로 virtualenv를 나간 후, 설치합니다.

    $ deactivate
    $ sudo pip3 install uwsgi


    다음 uWSGI사용방법은 동일합니다.

    Emperor mode


    uWSGI는 'emperor' 모드를 실행할 수 있습니다. 이 모드는 uWSGI config files들의 directory를 감시하고 설정 파일을 발견했을 때, 각각 ('vassals')이라는 인스턴스를 생성합니다.

    config file들이 수정될 때마다, emperor는 vassal을 자동적으로 재시작 합니다. emperor mode를 사용하기 위해선 .ini파일을 생성해야 합니다.


    아래와 같이 명령어를 실행시킵니다. (실행 위치 상관 X) 마지막 명령어를 실행시키면 /etc/uwsgi/vassals/ 안에 심볼릭 링크가 생성된 것을 볼 수 있습니다.

    $ sudo mkdir /etc/uwsgi
    $ sudo mkdir /etc/uwsgi/vassals
    $  sudo ln -s /home/jinhwan/uwsgi-tutorial/testproject_uwsgi.ini /etc/uwsgi/vassals/


    다음 아래 명령어로 웹서버를 실행시킵니다.

    uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data

    문서에서는 sudo를 사용해야 할지도 모른다고 나와있지만 직접 사용했을 때, sudo가 없어도 되는 것을 알 수 있습니다.

    위 명령어의 옵션:

    emperor : vassals(config files)가 어디에 있는지 

    uid: 시작하고 난 뒤, 프로세스의 유저 id

    gid: 시작하고 난 뒤, 프로세스의 그룹 id

    시스템 부팅될 때, uWSGI 자동 실행


    이를 사용하기 위해선,  virtualenv에 uwsgi만 깔려 있어야 하는 것이 아닌, 시스템 전체에 uwsgi가 깔려 있어야 합니다. 


    /etc 폴더 안의 rc.local에 다음과 같이 경로를 작성합니다.

     #!/bin/sh -e
    #
    # rc.local
    #
    # This script is executed at the end of each multiuser runlevel.
    # Make sure that the script will "exit 0" on success or any other
    # value on error.
    #
    # In order to enable or disable this script just change the execution
    # bits.
    #
    # By default this script does nothing.
    /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data daemonize /var/log/uwsgi-emperor.log
    exit 0

    (재부팅시 에러가 납니다. - 위 코드 확인 필요)

    댓글