Django WSGI, ASGI, 배포 - python cgi, wsgi, asgi

정현우·2021년 10월 21일
14

Django Basic to Advanced

목록 보기
15/38
post-thumbnail

Django WSGI, ASGI

  • 사실 Django에 국한되는 것이아니라, python에서 web-backend (요즘 주로 restful한 interface로 API를 개발할때) 구성할때 애플리케이션을 연결하는 방법이다.

Spring 에 비유하면, 만들어진 웹 서비스를 Tomcat 등의 "WAS"로 배포를 한다. Python에서는 이러한 WAS를 제공해주지 않고 run time환경에서 돌아간다. 그렇다면 어떤 방법으로 웹 서버와 Python 애플리케이션을 연결할 수 있을까?

1. Common Gateway Interface (CGI)

  • 들어가기 전에 아래 인용문을 꼭 한 번 읽어보길 바란다.

Django applications are WSGI applications. WSGI means Web Server Gateway Interface (modern and better version of old CGI, that's why sometimes called FastCGI). So you need an Application Server in the first place which can serve WSGI applications. Gunicorn and uWSGI are common and mostly used application servers for hosting Django projects in production.

Now where's and who's the web server?
In production, its a common practice to have a common web server like Nginx or Apache to run on port 80 (the common HTTP port where browser hits when you don't mention any port in URI) and support secure connections in 443 (HTTPS). This web server (a particular server block or more commonly a VirtualHost) is configured to act as a reverse proxy for all requests meant for your application server. In simpler terms, your web server simply passes on the requests meant for your django application to the same and let the client get back the response the application server sent back. There's another important function of the web server and that is to serve static web asset files (Images, Fonts, CSS, JS). Same way whenever it gets a request at a particular url (like for example /static) it knows Nginx/Apache itself has to respond back with the resource requested.

To keep the application server running in background we need to register those as Services in the operating system (I have experience with Linux and not much with Windows).
You can read more about reverse proxies and working of this web servers and WSGI. The question suggests that you are pretty new to these and I appreciate your inquisition if you are ready to study and perhaps host a few django apps in Linux servers yourself. I purposely left the discussion incomplete about how the Application Server is identified by the web server to encourage research on your end.

PS: Although this question is Django specific, the approch of having an Application Server running in some other port and Web servers used as reverse proxy is generic. Infact NodeJS or any other web applications are served in similar ways. Also I am pretty aware there are other approaches to it and that it is often modified depending on the load balancing and other optimisations for scaling. Sometimes Docker or similar containerization tools are used but the idea is same even then.
(from : https://www.quora.com/Does-Django-need-a-web-server )

1) CGI란

  • 웹 서버 상에서 사용자 프로그램을 동작시키기 위한 조합이다. 존재하는 많은 웹 서버 프로그램은 CGI의 기능을 이용할 수 있다.

  • 웹 서버 프로그램의 기능의 주체는 미리 준비된 정보를 이용자(클라이언트)의 요구에 응답해 보내는 것이다. 그 때문에 서버 프로그램 그룹에서는 정보를 그 장소에서 동적으로 생성하고 클라이언트에 송신하려하는 조합을 작성하는 것이 불가능했다. 서버 프로그램에서 다른 프로그램을 불러내고, 그 처리 결과를 클라이언트에 송신하는 방법이 고안되었다. 이를 실현하기 위한 서버 프로그램과 외부 프로그램과의 연계법을 정한 것이 CGI이다.

  • CGI는 환경변수나 표준입출력을 다룰 수 있는 프로그램 언어에서라면 언어의 구별을 묻지 않고 확장하여 이용하는 것이 가능하나, 실행속도나 텍스트 처리의 용이함 등의 균형에 의해 펄이 사용되는 경우가 많았다. 최근에는 펄뿐 아니라 파이썬, 루비 등도 널리 쓰이고 있다.

2) python에서 CGI

  • 기본적으로 웹 서버 자체는 정적인 페이지 밖에 보여주지 못한다. "웹서버와 웹어플리케이션서버에 대한 이해 필요"

  • 우리가 인터넷에서 보는 동적인 페이지들은 웹 서버가 전적으로 처리하는 것이 아니라 웹 어플리케이션(장고, 플라스크 등으로 작성된 프로그램)의 도움으로 보게 된다. 그래서 웹 서버와 웹 어플리케이션은 서로 소통을 할 수 있어야 한다. 하지만 아파치, Nginx 등의 웹 서버는 런타임 환경의 파이썬 코드를 이해하지 못한다. 그래서 "생(코드 날것)"으로 웹 서버와 파이썬 웹 어플리케이션은 소통을 할 수가 없다.

  • 웹 서비스의 가장 일반적인 모습이지만 과거에는 정적 HTML 파일 하나를 가지고 웹 서비스를 하였기 때문에 이러한 요소(CGI)가 필요하지 않았다. 웹 서버가 정적 파일 등을 사용자에게 다운로드 방식으로 제공(브라우저가 해당파일을 랜더링하는 것 뿐)하는 것이 전부인데, 웹에 대한 수요가 증가함에 따라서 웹 서버가 처리할 수 없는 정보, DB서버의 데이터를 동적으로 다루는 것의 행위 등이 요청되었을 경우 그 처리를 외부 애플리케이션이 할 수 있도록 호출함으로써 중계 역할을 해준다.

  • 그러나 CGI는 "매번 요청에 대해 프로세스를 생성(fork)" 하게 된다. 예를 들어 위 그림과 같이 다수의 사용자가 동시에 요청할 경우 새로운 프로세스를 생성하고, 삭제하는 것이 빈번해지는데 이는 커널 리소스를 계속 생성/삭제하기 때문에 오버헤드가 심해지고, 성능 저하의 원인이 될 수 있다.

3) python FastCGI

  • 위 같은 기본적인 CGI 단점을 보완하기위해 등장한 것이 FastCGI이다.

  • FastCGI는 몇 번의 요청이 들어와도 하나의 프로세스만을 가지고 처리하게 된다. 즉 메모리에 단 하나의 프로그램만을 적재하여 계속 재활용하기 때문에 CGI에 비하여 오버헤드가 월등하게 감소한다.

  • Java의 Tomcat 또한 Web Server + FastCGI를 채택한 형태로 다수의 사용자 접속에 유리하다. 우리는 JVM 계열의 언어로 서버 프로그램을 작성하면 JBoss, Tomcat 등의 웹 애플리케이션 서버를 사용하는 게 보편적이지만 이 외에도 우리가 CGI를 수동으로 붙여서 사용할 수 있는 방법도 있다. => 물론 해당 CGI를 만들어야 하긴 한다.

  • 그러나 Python에서는 이러한 WAS가 별도로 존재하지 않으며 결국 우리가 CGI, FastCGI 등을 이용해서 원하는 WAS 형태를 만들어 사용해야 한다. 그래서 등장한 것이 Python만의 게이트웨이 인터페이스다!


2. Python WSGI, ASGI

  • 위에 말한 것과 같은 맥락으로, python에 CGI를 모방해 도입한 것이 WSGI, ASGI이다.

1) WSGI

Web Server Gateway Interface

  • Django(MVT 디자인패턴)를 기준으로 보는 모습이다. WSGI는 CGI와 동일하게 웹 서버와 애플리케이션 중간에 위치하는데, CGI와 다른 점은 CGI는 매 요청마다 프로세스를 생성한다는 점이고, WSGI는 한 프로세스에서 모든 요청을 받는다.

  • 위에서 말한 것 과 같이 CGI는 매 요청마다 Fork 등의 함수를 통해 커널 리소스를 추가/반납하고, WSGI는 많은 요청을 콜백(callback)으로 받아 처리하게 된다.

  • WSGI는 웹 어플리케이션을 호출할 때 요청(request)의 헤더 부분을 "환경 정보로 전달"하며 이 때 "콜백 함수"도 같이 전달합니다. 그 후 웹 어플리케이션이 요청을 처리하고 콜백 함수로 응답하는 방식입니다.

  • 이런 WSGI를 도입하기 위한 대표적인 두 가지 방법

    • nginx, apache에서 내장 모듈로 제공하는 server-often high profile 방식 (ex: mod-wsgi, mod-python 등)
    • Python 코드로 작성된 Web App Server (ex: gunicorn, uvicorn, cherrypy 등)
  • 결국 WSGI는 웹 서버와 애플리케이션 사이에 미들웨어 역할을 하며 기술적으로는 웹 서버도 WSGI에 대한 작동 코드가 필요하고, 애플리케이션 또한 WSGI에 대한 작동 코드가 필요한 Client-Server Model을 응용한 것.

  • 즉, 웹 서버가 애플리케이션의 코드를 직접적으로 읽을 수 없으므로 중간의 미들웨어가 해당 코드를 읽어서 결과를 대신 반환해주는 이라고 쉽게 이해할 수 도 있다.

Django WSGI

  • 이제 wsgi가 무엇인지 이해를 했으니 해당파일의 존재 이유를 파악할 수 있다. 사실 장고의 내장 서버는 WSGI의 기능을 자체적으로 포함하고 있다는 것이다 -> python manage.py runserver

  • WSGI 서버는 장고와 통신할 때 '프로젝트명/config/wsgi.py' 경로에 있는 이 파일을 통해 장고 어플리케이션을 호출한다.

  • 이 파일은 웬만해선 딱히 건들 필요가 없지만 혹시나 프로젝트의 디렉터리 구조를 조금 손봐서 settings.py 파일이 이동하게 된다면 그 경로를 반드시 수정 해야 한다.

"""
WSGI config for mysite project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

# application의 settings.py의 경로를 찾아간다! 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

application = get_wsgi_application()

2) ASGI

Asynchronous Server Gataway Interface

  • Python에서는 asyncio, coroutine과 같은 비동기 처리를 지원한다. 그러나 WSGI는 동기 함수 처리만을 지원하여 여러 작업을 동시에 처리하는 것에 한계가 있습니다. 가령 현대 웹 서비스에서는 웹 소켓 등을 사용한 실시간 채팅 서비스 등을 할 수도 있는데, WSGI로는 이러한 서비스를 구현하는 데 어려움이 생길 수 밖에 없는 것.

  • Celery 등을 활용하여 대용량 트래픽 처리 요구하는 서비스가 불가능한 것은 아니라고 한다. 하지만 추적, 유지 보수, 기본적인 구현 측면에서 쉬운 길이 아니라는 것.

  • 따라서 최근에는 Django 3.0 뿐만 아니라 FastAPI 등의 프레임워크에서도 ASGI 인터페이스를 적용하였으며 뒤따라 Falcon 프레임워크도 3.0부터 ASGI 개발에 들어갔다.

  • 운영 아키텍처로 봤을 때는 크게 다르지 않지만 WSGI와 다르게 ASGI는 기본적으로 요청을 비동기로 처리한다는 점이 크게 다르다. 따라서 WSGI에서는 지원되지 않는 Websocket 프로토콜과 HTTP 2.0을 지원한다.

  • "단일 비동기 방식으로 구성"되어 있고, send, receive 함수를 제공하는데 이 함수들을 비동기적으로 호출하여 이벤트를 처리합니다. 여러 송수신 이벤트를 허용 할 수 있을 뿐만 아니라 백그라운드 코루틴도 허용하므로 redis 큐와 같은 외부 트리거 이벤트를 읽는 것과 같은 어플리케이션의 다른 작업도 수행할 수 있다.

  • ASGI는 WSGI의 상위 집합 개념으로 설계되었으며 asgiref 라이브러리를 사용해 ASGI 서버 내에서 WSGI를 실행할 수 있다.

ASGI - uvicorn

  • 이러한 대표적인 ASGI Web-App에는 python에서 uvicorn이 있다. uvicorn은 ASGI 기반의 웹 애플리케이션 서버로써 그 내장 모듈로 uvloop을 사용한다. uvloop에서 uv는 libuv 즉, Javascript V8에서 사용되는 비동기 모듈을 사용한 것이다.

  • ASGI는 Cython 기반으로 C++ 언어로 작성되어 매우 빠른 속도를 제공한다는 것이 특징인데다가 libuv를 사용하여 비동기 처리를 하니 Node.js와 같은 비동기 처리 속도를 어느 정도 누릴 수 있다는 장점이 있다.

Django ASGI

  • WSGI에서 언급한 내용과 동일하다. asgi.py 파일 존재 목적과 해당 파일에서 settings값을 설정해주고, 바꿔야 할 순간에 대해 기억하자.

3. Django 배포하기

django 배포의 A to Z 가 아니라 우선 기본적인 "방향과 방법"에만 집중해서 "어떻게 배포를 할지" 에 대한 얘기만 하려고 한다.

  • 여기서 Django를 사용한다면, 런타임 언어에 "왜 배포를하지?" 라는 의문이 생길 수 도 있다. 우선 기존 Django의 배포에 대해 다 보고, 이해를 다시 처음부터 하는 것을 추천한다. 그리고 방대한 공식문서를 꼭 이용하자

  • Django는 현재 WSGI와 ASGI의 두 가지 인터페이스를 지원한다.

    • WSGI는 웹 서버와 애플리케이션 간의 통신을 위한 주요 파이썬 표준이지만 동기식 코드만 지원한다.
    • ASGI는 당신의 Django 사이트에서 비동기 Python 기능과 비동기 Django 기능을 개발하면서 사용할 수 있게 해주는 새로운 비동기 친화적인 표준이다.
  • 응용 프로그램에 대해 static files를 어떻게 처리할 것인지와 error reporting을 어떻게 처리할 것인지도 고려해야 한다.

  • 마지막으로 애플리케이션을 운영 환경에 배포하기 전에 '배포 체크리스트'를 확인하여 구성이 적합한지 확인해야 한다. 공식 문서를 참고하면 좋다.

1) 공식 문서 Checklist 내용 발췌

일단 python manage.py check --deploy 부터 해보세요!

  • 인터넷은 적대적인 환경입니다. 여러분의 장고 프로젝트를 배포하기 전에, 보안과 성능, 연산에 관련해서 설정을 다시 살펴볼 시간이 필요합니다.

  • 장고는 보안 기능을 많이 갖고 있습니다. 몇몇은 내장된 기능으로 항상 켜져있습니다. 다른 몇몇은 선택할 수 있는데 그 이유는 항상 보안 기능들이 적절하지 않거나 개발에 불편함을 가져올 수 있기 때문입니다. 예를 들어 강제로 HTTPS를 활성화 하는 것은 모든 웹사이트에 적합하지 않을 수도 있고, 로컬 개발환경에선 비실용적일 수도 있습니다.

  • 성능 최적화는 편의를 위한 또다른 범주의 거래입니다. 예를 들어, 캐싱은 상용 환경에서 유용할 수 있지만 로컬 개발 환경에서는 실용적이지 않을 수 있습니다. 오류 보고의 필요성에도 큰 차이가 있습니다.

  • 설정에 대한 체크리스트는 다음과 같습니다.

    • Django에서 예상되는 수준의 보안을 제공하도록 설정해야 합니다.
    • 각 환경에 맞도록 설정해야 합니다.
    • 선택적인 보안 기능들을 활성화시켜야 합니다.
    • 성능 최적화가 적용돼야 합니다.
    • 에러 보고가 제공되어야 합니다.
  • 이 설정들 중 대부분은 예민할 수 있고 감추어져야 합니다. 만약 여러분이 프로젝트의 소스코드를 배포한다면, 대부분은 개발 환경에 최적화된 설정으로 배포하고 상용 환경에서 사용할 별도의 설정을 사용할 것입니다.

  • 이후 특별 환결 설정과 HTTPS, 성능 최적화, 오류 알림에 대해서는 꼭 읽어보는 것을 추천한다.

2) WSGI - Gunicorn

  • Gunicorn(Green Unicon)는 UNIX용 Pure-Python WSGI 서버다. 종속성이 없으며 《파이프》를 사용하여 설치할 수 있다.

  • python install gunicorn을 실행하여 gunicorn을 설치한다.

  • 일반적인 WSGI 애플리케이션으로 Gunicorn에서 Django 실행

    • Gunicorn이 설치되면 Gunicorn 서버 프로세스를 시작하는 Gunicorn 명령이 나온다.
    • 가장 간단한 gunicorn의 호출은 일반적인 Django 프로젝트의 경우 다음과 같이 보이는 《application》이라는 WSGI 애플리케이션 객체가 있는 모듈의 위치를 통과하는 것이다.
    • gunicorn myproject.wsgi
    • 이는 127.0.0.1:8000에서 "하나의 스레드"를 8000번 포트를 리스닝하는 하나의 과정을 시작할 것이다. 프로젝트가 Python 경로에 있어야 한다. 동일한 디렉토리에서 실행하는 가장 간단한 방법이다.
  • gunicorn [project명].wsgi -b 0.0.0.0 —workers 1 과 같은 방식으로 WAS 인스턴스를 러닝한다. Nginx, Gunicorn 배포 글을 추천한다.

  • 화해에서도 WSGI를 기반으로 gunicorn 활용해서 BE API를 배포한다. 화해의 gunicorn A to Z 글을 꼭 추천한다. 디테일한 부분이 gunicorn의 공식 가이드보다 읽을 만 하다!

3) ASGI - uvicorn

  • 유비콘 공식문서

  • python -m pip install uvicorn gunicorn 설치하기.

  • Uvicorn이 설치되면 ASGI 응용 프로그램을 실행 하는 uvicorn 명령을 사용할 수 있다. Uvicorn은 ASGI 응용 프로그램 개체를 포함하는 모듈의 위치와 그 뒤에 응용 프로그램이 호출되는 위치 (콜론으로 구분)를 사용하여 호출해야 한다.

  • 일반적인 Django 프로젝트의 경우 Uvicorn을 호출하면 다음과 같다.

    • gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker
    • gunicorn을 통하여 uvicorn 워커를 실행한다. 공식문서와 이 글로 대신한다.
  • 그리고 보통 gunicorn 러닝을 데모나이징해서 service 를 만들어서 구동을 하는 형태가 많다.

4) 근데 앞단에 왜 web-server(nginx 등)를 둘까

  • 일단 결론은 "nginx와 같은 웹서버 자체의 강점" 때문이다. 웹서버에 대한 deep dive 내용은 따로 다룰 예정이라 "그래서 왜 앞단에 두는데?" 에 대한 결론 중심으로 알아보자!

(1) 정적 파일 서비스

  • Django는 동적인 웹 애플리케이션을 위해 설계되었지만 정적 파일(이미지, CSS, JavaScript 파일 등)을 처리하는 데에는 더 효율적인 웹 서버가 적합하다.

  • Nginx는 정적 파일을 처리하는 데 특화되어 있으며, 이를 위해 Django 애플리케이션에 대한 요청 중에서 정적 파일 요청을 처리하는 역할을 할 수 있다. 이렇게 함으로써 애플리케이션 서버의 부담을 줄이고, 정적 파일 제공에 더 빠른 응답을 할 수 있다.

(2) 로드 밸런싱

  • Nginx는 로드 밸런싱 기능을 지원하여 여러 개의 Django 애플리케이션 서버(예: gunicorn 프로세스) 사이에서 부하를 분산시킬 수 있다. 로드 밸런싱을 통해 애플리케이션의 처리량을 향상시킬 수 있으며, 트래픽이 많을 때 확장성과 가용성을 높일 수 있다.

(3) 보안

  • Nginx는 보안 설정을 통해 Django 애플리케이션을 보호할 수 있다. 예를 들어, SSL/TLS 인증서를 설정하여 암호화된 연결을 제공하거나, 웹 애플리케이션 방화벽과 같은 보안 기능을 추가할 수 있다. 또한, Nginx는 요청 필터링, 접근 제어 등의 기능을 제공하여 애플리케이션에 대한 보안 측면을 강화할 수 있다.

(4) 캐싱

  • Nginx는 프록시 서버로 동작할 수 있으며, 이를 통해 애플리케이션의 응답을 캐싱할 수 있다. 정적 콘텐츠나 동적 콘텐츠의 일부를 캐시에 저장하면, 동일한 요청에 대해 애플리케이션 서버에 접근하지 않고 캐시에서 바로 응답할 수 있다. 이를 통해 응답 시간을 단축시키고, 애플리케이션 서버의 부하를 줄일 수 있다.

출처

profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글