[Django] Nginx uWSGI Django 연결하기

업데이트(2018.05.31) : nginx<->uwsgi에서 http를 socket으로 오류 정정

업데이트(2018.04.16) : 포맷 변경 및 내용 조금 보충

업데이트(2018.02.20) : .conf 파일의 생성위치와 symlink 부분을 수정하였습니다.

Ubuntu에서 Nginx, uWSGI 그리고 Django를 연결하자.


환경


사전 준비사항

  • Django에서 manage.py runserver를 통한 서버 환경 실행 경험 필요
  • pip 사용법 숙지
  • Django 프로젝트 생성(이 문서에서는 LinkU라는 프로젝트 명으로 진행합니다.)
  • Server와 Port의 개념
  • [선택] pyenv 및 virtualenv 사용법 숙지(단, 여기서는 가상환경에서 진행합니다.)


[선택] pyenv와 virtualenv를 통해서 가상환경 설정하기

우선 uWSGI를 pip를 통해서 전체에서 설정을 해도 되지만 가상환경을 직접 설정해서 설치하고 돌려보겠습니다 우선 만드시려는 python 환경 프로젝트를 virtualenv를 통해서 생성해줍니다.

관련 생성은 위에 있는 링크를 타고 가시면 참고 하실 수 있으며 pyenv와 virtualenv가 설칭되어있다고 가정할 때 간단한 예시는 아래와 같습니다.

$ pyenv virtualenv [python version] [project virtualenv name]

프로젝트 가상환경의 이름이 LinkU이고 python version이 3.5.2일 때는 아래와 같습니다.

$ pyenv virtualenv 3.5.2 LinkU


[선택] 가상환경 실행

가상환경을 실행하고 uWSGI를 설치해 보겠습니다.

$ pyenv activate LinkU // LinkU에는 설정하실 가상환경 이름이 들어가면 됩니다.


프로젝트 환경에 uWSGI 설치하기

uWSGI는 pip를 통해서 설치가 가능합니다.

(LinkU)$ pip install uwsgi


uWSGI를 통해서 Django 서버 돌리기

사용했던 python manage.py runserver 대신에 uWSGI명령어를 통해서 실행 해보도록 하겠습니다. 생성한 Django 프로젝트로 이동해서 manage.py 있는곳으로 가줍니다.

다음 아래와 같은 방식을

(LinkU)$ python manage.py runserver

다음처럼 할 수 있습니다. linku.wsgi는 wsgi.py를 포함하고 있는 폴더명으로 해주시면 됩니다.

# linku.wsgi는 wsgi.py를 포함하고 있는 폴더명으로 해주시면 됩니다.
(LinkU)$ uwsgi --http :8000 --module linku.wsgi


명령어에 대해 설명하면

  • http는 http 통신을 하겠다는 의미이며 8000포트를 사용한다는 것입니다.
  • http가 아닌 Nginx와 함께 UNIX Socket 방식을 사용하고 싶다면 --socket :[port number]의 형태로 이용하면 되며 아래에 더 자세한 예제가 나옵니다.
  • module은 wsgi 모듈을 통해서 실행하겠다는 의미이며
  • 단순하게 파일을 사용하려면 --wsgi-file [app.py(python 실행 파일 이름)]의 형태로도 사용가능합니다.

조금 더 추가적인 설명을 드리자면

  • 외부에서 8000 포트로 들어온 http요청을 uWSGI가 받아서
  • 그 요청을 처리하는 애플리케이션에(이 경우에는 Django 혹은 Django Rest Framework) 넘겨주고 처리를 해준 후에 응답을 해줍니다.
  • 현재 구조 : web Client <-> uWSGI(port 8000) <-> Django


Nginx에 upstream 설정해주기

이제 외부에서 특정포트로 Nginx를 통해 http 요청을 받았을 때 그 요청을 uWSGI를 통해서 Django로 넘겨 봅시다.


프로젝트 폴더에 uwsgi_params 추가

Nginx 설정 파일들 폴더(/etc/nginx)에 있는 uwsgi_params파일을 복사해서 manage.py가 있는 프로젝트 폴더에 복사해 추가합니다.

package를 통해서 설치한 경우에는 다음과 같습니다.

$ cp /etc/nginx/uwsgi_params [Django project folder]

만약에 없다면 uwsgi_params 파일을 생성해서 다음 아래 코드를 복사해서 붙여넣습니다. 이 코드는 nginx github에서 가져온 코드입니다.

uwsgi_params

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;


nginx에서 사용 할 conf 파일 만들기

이제 nginx에서 사용할 설정 파일을 만들어서 /etc/nginx/sites-available 폴더에 추가해봅시다.

  • /etc/nginx/sites-available로 이동해서 아래와 같이 파일을 생성해줍니다.
(LinkU) $ touch linku_backend_nginx.conf // 설정 파일의 이름은 자유입니다.


해당 파일을 vim 혹은 에디터를 통해서 아래처럼 설정해줍니다.

  • 프로젝트 이름과 가상환경 사용할 port에 따라서 설정 파일이 다를수도 있습니다! (당연한 이야기)

linku_backend_nginx.conf

# linku_backend_nginx.conf

# upstream(proxy) 설정
upstream django{
    #1 uWSGI를 이용한 django 서버가 listening 할 ip주소와 port 번호를 적어주시면 됩니다. upstream에 다른 외부 서버를 연결할수도 있지만 여기서는 로컬에 있는 django에 보내니 주소가 127.0.0.1이고 포트는 8001로 설정하였습니다.
    server 127.0.0.1:8001;
}

# configuration of the server
server {
    #2 django가 아니라 외부에서 어떤 port를 listening 할지 정해줍니다.
    listen      [Port Number];
    #3 실행하는 서버의 IP주소 혹은 Domain을 적어주시면 됩니다.
    #4 이 server_name을 여러개 만들어서 subdomain도 각각 다르게 처리 가능합니다.
    server_name [IP Address];
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    #5 Django media파일 경로
    location /media  {
        alias /home/linku/LinkU/linku_backend/media;
    }

    #6 Django static파일 경로
    location /static {
        alias /home/linku/LinkU/linku_backend/static;
    }

    #7 media와 static을 제외한 모든 요청을 upstream으로 보냅니다.
    location / {
        #8 uwsgi_pass [upstream name] (위에 upstream으로 설정한 block의 이름)
        uwsgi_pass  django;
        #9 uwsgi_params의 경로
        include /home/linku/LinkU/linku_backend/uwsgi_params;
    }
}
  • []로 만들어 놓은 부분은 개인의 작성하시는 분에 따라서 직접 넣어주셔야 합니다.
  • #1 uWSGI를 이용한 django 서버가 listening 할 ip주소와 port 번호를 적어주시면 됩니다. upstream에 다른 외부 서버를 연결할수도 있지만 여기서는 로컬에 있는 django에 보내니 주소가 127.0.0.1이고 포트는 8001로 설정하였습니다.
  • #2 django가 아니라 외부에서 어떤 port를 listening 할지 정해줍니다.
  • #3 실행하는 서버의 IP주소 혹은 Domain을 적어주시면 됩니다.
  • #4 이 server_name을 여러개 만들어서 subdomain도 각각 다르게 처리 가능합니다.
  • #5 Django media파일 경로
  • #6 Django static파일 경로
  • #7 media와 static을 제외한 모든 요청을 upstream으로 보냅니다.
  • #8 uwsgi_pass upstream name
  • #9 uwsgi_params의 경로



nginx 설정파일들이 있는 폴더로 이동

Ubuntu에서 apt-get을 통해 nginx 설치하기 및 간단한 정리에 기록을 해두었지만 sites-enablednginx.conf를 통해서 nginx의 설정이 가능합니다.

해당 폴더와 파일의 경로는 위 문서에 기록한 것처럼 /etc/nginx,/usr/local/nginx/conf, /usr/local/etc/nginx중에 하나에 위치합니다.

nginx_folder


symlink는 쉽게 생각해 바로가기와 같다고 보면 편합니다.

$ sudo ln -s /etc/nginx/sites-available/mysite_nginx.conf /etc/nginx/sites-enabled/

지금까지 진행한 linku_backend_nginx.conf에 대해서 symlink를 만드는 경우의 예시는 아래와 같습니다.

$ sudo ln -s /etc/nginx/sites-available/linku_backend_nginx.conf /etc/nginx/sites-enabled/

추가된 모습

symlink


Static file들 모으기

Django 프로젝트 폴더에 있는 settings.py에 아래와 같은 코드를 추가합니다. media에 관한 설정도 안되어 있다면 같이 해주도록 합시다.

settings.py

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

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
...

프로젝트 폴더에서 collectstatic 명령어을 통해서 static file들을 모아줍니다.

$ python manage.py collectstatic


설정파일 적용 및 테스트

nginx 재시작

# 다음 3가지 명령어 중에 하나만 하시면 됩니다.
$ sudo service nginx restart
$ sudo systemctl restart nginx
$ sudo /etc/init.d/nginx restart

아래와 같이 OK가 뜬다면 제대로 실행이 된것입니다.

restarting


uWSGI 실행

  • 여기서 Port 번호는 위에 conf파일에 있는 upstream 블록의 Port 번호와 같아야합니다.
(LinkU)$ uwsgi --socket :8001 --module linku.wsgi


이제 도메인을 통해서 domain.com:port를 통해서 접속하면 여기서의 Port는 위에 conf파일에 있는 server 블록에 있는 Port번호 입니다. 장고에서 해당하는 응답을 보여줌을 확인 할 수 있습니다.


uWSGI가 실행이 되지 않는다면

nginx error log(/var/log/nginx/error.log)를 확인해서 아래와 같은 오류가 나온다면

connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission denied)


다음 아래 두 명령어중에 하나를 시도해보시면 됩니다. 권한 떄문에 생기는 문제인데 nginx의 사용자 그룹에 현재 접속한 유저를 추가해주셔야 됩니다.

uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=666 # (very permissive)

또는

uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=664 # (more sensible)


완료된 상황 및 구조

현재까지 완료된 상황을 보면 아래와 같습니다.

  • Web Client <-> Nginx(Web Server) <-> uWSGI(port:8001) <-> Django(Application Server)


추후 계획


참고자료

[Linux] 쉘 스크립트에서 멀티프로세스(혹은 스레드) 기능 사용하기

> 백그라운드로 명령어를 실행해서 병렬적으로 실행되는 멀티 프로세스 환경을 만들어보자.## 환경- Linux 기반 시스템- Bash shell(/bin/bash)## 멀티프로세스? 병렬처리? 멀티스레드? 백그라운드 프로세스?- 여기서 진행할 방식...… Continue reading