파이썬 어플리케이션이 웹 서버와 통신하기 위한 명세가 WSGI

필요성
django는 web application server 구현을 위한 프레임워크이다.
- WAS는 동적인 컨텐츠를 사용자의 요구에 따라 DB에서 조회하고 로직에 따라 컨텐츠를 생성한다.
- 클라이언트는 서버에 동적인 컨텐츠만을 요구하는 것이 아니라 정적인 컨텐츠 또한 요구한다.
- WAS가 정적인 컨텐츠까지 담당하는 것은 자원 낭비이고 이를 위한 웹서버가 따로 필요하다.
Web server가 필요하다.
- 서버의 부하를 방지해준다. (로드 밸런싱)
- 동적인 데이터를 처리하기 위해 상대적으로 부하가 심한 WAS의 부하를 줄인다.
- 필요한 정적인 파일들은 WAS까지 가지 않고 Web Server단에서 빠르게 보내줄 수 있다.
- 하나의 서버에서 여러 서비스를 호스팅할 수 있다.(Sub Domain)
- Caching
- 이미지, CSS, HTML, JS와 같은 정적 데이터를 Caching하여 정적파일들의 응답 속도를 빠르게 할 수 있다.
- WAS의 보안강화
- 사용자 정보등이 담긴 DB를 관리하는 WAS에 대한 접근을 Web Server가 하므로 보안강화를 기대할 수 있다.
- 기존의 웹서버들(Apache, nginx) 그 자체로는 python을 이해하지 못한다.
- Apache의 경우 Java 기반 Web framework와 native하게 통신할 수 있다.
- 기존의 웹서버들은 Python application을 쉽게 호출 할 수 없다.
- Python은 bytes로 구성되는 HTTP 메시지를 이해할 수 없다.
- WSGI는 bytes로 구성되는 HTTP 헤더와 메타데이터를 Python
string
으로 매핑해준다.
- WSGI는 bytes로 구성되는 HTTP 메시지의 바디데이터를 Python
Bytestring
으로 매핑해준다.
- WSGI는 Web Server가 바라보는 입장에서는 WAS로, WAS 입장에서는 Web Server로 작동한다.
WSGI통신 과정
Web Server 입장
Web Server(WSGI)는 python application에 environ
과 start_response
를 제공해야한다.
application을 호출하는 함수를 통해 전달한다.
result = application(environ, start_response)
try:
for data in result:
if data:
write(data)
if not headers_sent:
write('')
finally:
if hasattr(result, 'close'):
result.close()
environ
HTTP request message를 parsing한 데이터이다.
CGI
환경변수들
- WSGI는 Python Dictionary 형태로 변환하여 제공한다
- HTTP Header 관련 정보들
REQUEST_METHOD
, SCRIPT_NAME
(호스트), PATH_INFO
(경로), QUERY_STRING
, CONTENT_TYPE
, CONTENT_LENGTH
, SERVER_PROTOCOL
, HTTP_
Variables
WSGI
변수들
wsgi.version
, wsgi.multithread
, wsgi.multiprocess
, wsgi.run_once
wsgi.url_scheme
: URL의 scheme 부분 (http, https, ftp 등등)
wsgi.input
: HTTP request body를 Bytestring
형태로 생성한 input stream
wsgi.errors
: output stream으로 error output을 str
형태로 저장한 file-like 객체
- HTTP response header를 생성하기 위한 함수로, response body를 작성하는
write(body_data)
callback을 return 한다.
status
와 response_header
를 인자로 가지고, exc_info를 optional하게 가진다.
status
- python
str
형태의 HTTP status를 나타낸다.
- e.g. "200 OK", "404 Not Found"
response_header
(header_name, header_value)
tuple의 python list
- response HTTP message의 헤더에 들어갈 값들을 리스트로 가진다.
- e.g.
[('Content-type', 'text/plain')]
exc_info
- application에서 생긴 exception(python
sys.exc_info()
tuple)을 re raise한다.
Application 입장
application 호출 callable 제공
- WAS는 Web server가 application을 호출할 수 있도록
environ
과 start_response
를 arg로 가지는 callable을 제공해야한다.
WSGI Middleware
- 위의 WSGI명세에 따라 server/gateway side와 application/framework side를 모두 구현하고 있는 하나의 프로그램
- 대표적으로 쓰이는 WSGI middleware에는 gunicorn과 uWSGI가 있으며 일부 python framework들은 WSGI를 내장하고 있다.
프레임워크 내장 WSGI
- django, flask의 경우 WSGI interface를 어느정도 구현해놓았으므로 바로 Web server를 붙여 써도 된다.
- 그러나 session, cookie, routing, authentication 등의 기능을 쉽게 적용하기 위해서는 gunicorn 혹은 uWSGI를 사용하는 것이 좋다.
gunicorn
- WSGI 명세와 필요한 기능들을 간단히 구현한 middleware이다.
- 간단하고 서버의 리소스를 적게 쓴다.
- 웹서버로 nginx와 붙여 쓰는 것이 권장된다.
uWSGI
- gunicorn에 더해 고급 기능들을 더 구현한 middleware
- 순수 c로 작성되어있다.
- 더 많은 기능들을 WAS상에서 구현하고 웹서버단은 간단히 하고자 할 때 유용할 것 같다.(추측)
서비스 구조의 변화
- 실제 서비스에서는 꼭 전통적인 WebServer - WSGI - WAS 구조를 따르는 것은 아니다.
- 다양한 배포환경에서 Apache나 NginX 웹서버를 설정하지 않는 경우가 존재한다.
서버리스 서비스
- 서버리스 서비스는 서버 셋업없이 web application code를 실행할 수 있는 FaaS 환경이다.
- 서버리스 서비스는 웹서버와의 연동이 아닌 클라우드 서비스 플랫폼의 다른 서비스들과 연동된다.
문제점
- WSGI는 Web Server로부터 Http Message를 Input으로 받는 경우를 전제로 한다.
- 하지만 Lambda는 서버리스 서비스이고 aws api gateway로부터 HTTP Message를 입력받지 않는다.
- lambda 실행을 위한 json 형태의 변수들을 입력받는다.
해결방법
zappa
와 같은 serverless 배포에 사용되는 프레임워크를 이용한다.
- WSGI가 HTTP message를 input으로 받도록 WSGI wrapper 기능을 제공한다.
Python application을 통한 정적 파일 serving
- static file을 서버에 저장하지 않고
CDN
서비스를 통해 serving되는 경우 고려할 수 있다.
Whitenoise
와 같은 library는 webserver를 거치지 않고 정적 파일을 제공할 수 있도록 한다.
- web server를 붙이기 힘든 배포환경일 때 유용하다.
- caching Header를 통해 CDN에서 정적파일이 효율적으로 직접 serving할 수 있게 한다.
- application이 CDN의 URL을, CDN이 application의 URL을 알 수 있게 연결한다.
- 여전히 WSGI를 이용해 file을
sendfile
syscall을 이용해 효율적으로 보낼 수 있다.
- static file의 serving또한 python으로 쉽게 테스트할 수 있고, git으로 관리된다.
참고
https://peps.python.org/pep-3333/
https://velog.io/@denhur62/nginx-와-uwsgi를-왜-사용할까
https://medium.com/analytics-vidhya/what-is-wsgi-web-server-gateway-interface-ed2d290449e
https://nitro04.blogspot.com/2020/01/django-python-asgi-wsgi-analysis-of.html
https://wayhome25.github.io/django/2018/03/03/django-deploy-02-nginx-wsgi/
https://tech.junhabaek.net/zappa와-github-action을-활용한-서버리스-django-application-aws-배포-트러블-슈팅-15604ed6bbcc
https://velog.io/@jimin_lee/Nginx와-Gunicorn-둘-중-하나만-써도-될까
http://whitenoise.evans.io/en/stable/