[Django] - Nginx + Gunicorn 연동 (1)

오동훈·2022년 12월 19일
3

Django

목록 보기
13/23

1. runserver

아래의 코드 한 줄 이면 서버 가동부터 재시작까지 모두 다해주는 우리 runserver 평생 같이 할 줄 알았는데 Django의 내장 서버 (runserver)는 실서비스에 부적합하다. 라는 이야기를 듣고 왜인지 찾아봤습니다.

python manage.py runserver

이전까지 저는 runserver를 이용해 백그라운드로 돌려도 상관없지 않나?라는 생각을 해 온 사람으로써 해당 글을 보고 다시 한 번 깨우치게 되었습니다🤦‍♂️
Django는 왜 runserver를 사용하지 않을까?

결론은 Django 내장 서버는 보안과 성능테스트를 거치지 않았기에 개발용으로만 사용하고, 실제 운영중인 환경 구축은 wsgi와 웹서버로 서비스하도록 권장하고 있습니다.

2. Gunicorn

Gunicorn은 Python WSGI로 WEB Server(Nginx)로부터 서버사이드 요청을 받으면 WSGI를 통해 서버 어플리케이션(Django)로 전달해주는 역할을 수행합니다. 종합적으로 정리해보면, 웹서버와 Django 사이에서 Request를 처리해주는 역할을 한다고 보면 됩니다. 이 과정에서 WSGI를 사용하는데 파이썬에서 대표적으로 uWSGI, gunicorn이 존재합니다. 두 개 중 gunicorn의 퍼포먼스가 조금 더 좋고 가볍다는 의견이 많기 때문에 저 역시 gunicorn을 사용해보려 합니다.

3. Gunicorn 사용법

1. Gunicorn 설치

아래의 명령 한 줄이면 단숨에 설치 가능합니다.

pip install gunicorn

2. Gunicorn 실행

gunicorn --bind 0:8000 [wsgi가 있는 앱 이름].wsgi:application

ex). gunicorn --bind 0:8000 config.wsgi:application

위의 명령을 쪼개서 봐보면 다음의 의미를 가지고 있고,
즉, python manage.py runserver 0.0.0.0:8000의 명령어를 대체했다고 볼 수 있습니다.

  1. --bind 0:8000 : 8000번 포트로 WSGI 서버를 수행한다는 의미
  2. config.wsgi:application : WSGI 서버와 연결된 WSGI 애플리케이션은 config/wsgi.py 파일의 application이라는 의미이다.

여기까지 진행해보면 서버가 오류없이 잘 시작되는 것을 확인해볼 수 있습니다. 다만 웹 브라우저로 접속 시 gunicorn은 동적인 부분을 담당하기 때문에 정적 파일들을 읽어오지 못하고 있는 것을 확인할 수 있습니다. 이 부분 처리는 웹 서버에서 다뤄보겠습니다.

3. Gunicorn 소켓

Gnincorn은 앞서 본 것 처럼 포트를 이용해 서버를 가동할 수 있습니다. 하지만 Unix 계열 시스템에서는 포트로 서비스하기보다는 유닉스 소켓을 사용하는 것이 빠르고 효율적입니다.

사용 방법은 크게 어렵지 않고 다음의 명령어를 입력해주면 됩니다.

gunicorn --bind unix:/tmp/gunicorn.sock config.wsgi:application

가동이 되는 것을 확인했으니 종료하고 다음 작업을 해보겠습니다.

Tip!
유닉스 소켓 방식으로 Gunicorn 서버를 실행하면 단독으로 Gunicorn 서버에 접속하여 실행할 수 없습니다.
유닉스 소켓 방식으로 실행한 Gunicorn 서버는 Nginx와 같은 웹 서버에서 유닉스 소켓으로 WSGI 서버에 접속하도록 설정해야 합니다.

4. Gunicorn 서비스

이번에는 AWS 서버에 Gunicorn을 서비스로 등록해보려 합니다. 그 이유는 Gunicorn의 시작, 중지를 쉽게 하고, 또 AWS 서버를 재가동할 때 Gunicorn을 자동으로 실행시켜주기 위함입니다. Gunicorn을 서비스로 등록하려면 서비스 파일을 작성해야 합니다.

# vi /etc/systemd/system/gunicorn.service 파일 생성

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=<계정명>
Group=<계정명>
WorkingDirectory=<프로젝트 경로. 즉 manage.py가 위치한 디렉토리 pwd>
ExecStart=<gunicorn이 설치된 가상환경 위치> \
        --workers 1 \
        --bind unix:/home/foo/django_test/run/gunicorn.sock \
        <wsgi.py가 있는 디렉토리 이름>.wsgi:application

[Install]
WantedBy=multi-user.target

여기서 --workers 1는 Gunicorn 프로세스를 1개 사용하라는 의미입니다.

5. 시스템 데몬 재시작

서비스를 새로 등록하거나 수정한 경우 데몬을 리로드 시켜주어야 합니다.

$ sudo systemctl daemon-reload

6. Gunicorn 서비스 실행 / 등록

$ sudo systemctl start gunicorn # 실행
$ sudo systemctl enable gunicorn # 등록

systemctl start [파일명] - systemd에 작성한 파일을 실행하는 명령어
systemctl enable [파일명] - 서버를 재시작할 때마다 자동으로 실행해주는 명령어

7. Gunicorn 서비스 상태 확인

정상적으로 등록이 완료되었다면 초록색의 active 상태로 확인 가능합니다.

$ sudo systemctl status gunicorn # 상태 확인

8. 이외 Gunicorn 명령어

$ sudo systemctl stop gunicorn #gunicorn.service 중지
$ sudo systemctl restart gunicorn #gunicorn.service 재시작

특히, Django framework 사용 시, views.py 등 django 내부 파일 수정 후에는 sudo systemctl restart gunicorn 명령어로 gunicorn 재시작 해주어야 서버에 반영됩니다.

9. static 파일

# config/settings.py
STATIC_ROOT = os.path.join(BASE_DIR, 'templates/')


# config/urls.py
from django.conf.urls.static import static

urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

여기까지 진행한 결과, Gunicorn 서비스 상태는 active로 돌아가고, 소켓 방식으로 가동되고 있는 서버에 접속한 페이지는 정상 작동 하지 않아야합니다. 이유는 소켓으로 연결했고 웹 서버에서 유닉스 소켓으로 연동해주어야 정상 작동합니다. 이제 그 연동을 진행보러 갑시다~


참고자료 📩
Django는 왜 runserver를 사용하지 않을까?
Django Gunicorn - wikidocs
Gunicorn - 화해 블로그
Gunicorn Docs
Gunicorn 정리 잘해주신 블로그님

profile
삽질의 기록들🐥

1개의 댓글

comment-user-thumbnail
2023년 11월 13일

에러 팁 -> 4. guicorn.service 에서 WorkingDirectory에 절대경로로 DJango프로젝트가 설정되어있으니 ExecStart 에서 [wsgi파일이있는폴더].wsgi:application 는 상대경로로 지정해주시면 됩니다..

답글 달기