CGI 기술의 등장 배경과 WAS로의 발전
초창기 웹(WWW)은 웹 서버에 미리 만든 웹 페이지(정적 페이지)를 가공 없이 단순히 보여주는 것이 목적이었다.
그러나 많은 웹 사용자들은 미리 저장된 정보를 보는 것 뿐만 아니라, 유저의 이름을 웹 페이지에 나타내고 싶거나 서버에서 정보를 가공하여 유저의 요청에 동적으로 콘텐츠를 만들어주고 싶은 다양한 요구사항(동적 페이지)이 생기기 시작했다. 이런 요구사항에 따라 CGI가 등장하게 되었다.
CGI를 통해 서버 프로그램이 다른 프로그램을 불러내 동적인 정보를 처리하여 클라이언트에 송신할 수 있다.
정적(static), 동적(dynamic) 이란 용어는 사용자가 페이지를 요청하는 시점에 페이지 내용의 유지 여부에 따라 구분
- 정적 페이지: 누가, 언제 요구하더라도 항상 같은 내용을 표시하는 웹 페이지
- 동적 페이지: 동일한 url 요청이라도 다른 내용이 반환되는 페이지
CGI(Common Gateway Interface)는 웹 서버 상에서 사용자 프로그램을 동작시키기 위한 기술 규격
CGI의 등장으로 웹 서버에 미리 저장된 HTML을 제공하는 것 뿐만 아니라, 사용자의 동적 요청을 CGI규격을 준수한 프로그램에서 처리하고 그 결과를 HTML로 생성하여 웹 서버에 돌려 보낸다.
python, java, php 등의 프로그래밍 언어로 CGI 규격을 준수한 CGI Program을 만들면, 웹 서버에서 CGI Program을 호출하여 클라이언트의 요청에 대해 개별 프로세스를 생성하는 방식이다.
CGI를 사용하지 않으면 사용자가 만든 애플리케이션을 단순히 html 형식의 정적 페이지 결과를 보여줄 것이다. html은 어떤 연산 처리를 하는 프로그래밍 언어가 아닌 페이지 구조를 정의하는 마크업 언어이기 때문이다.
CGI 방식의 근본적인 문제점은 클라이언트의 요청이 있을 때마다 독립적인 프로세스를 생성한다는 것이다. 파이썬으로 CGI 프로그램이 작성되었으면 요청이 있을 때마다 파이썬 인터프리터를 각각 새로 구동하게 되고 요청이 많아질수록 프로세스가 많아져 시스템에 부하를 주게 된다.
예를 들어, 10명의 관광객(클라이언트)이 하나투어 버스(서버)를 통해 아프리카 초원(CGI 프로그램)을 구경(요청)하는데 관광객마다 1명씩 10명의 가이드가 붙어서 동일한 설명(프로세스)을 해주는 것이다.
CGI 방식의 대안 기술
위의 프로세스 생성 부하를 줄이기 위해 버스(서버)에 한 명의 전문 가이드(인터프리터)만 내장시켜 개별 가이드(개별 프로세스)를 만들지 않는 방식이 개발되었다.
즉 웹 서버에 스크립트 엔진(인터프리터)을 내장시켜 하나의 프로세스에서 여러 개의 요청을 처리한다. 파이썬은 파이썬 프로그램이 웹서버와 통신하기 위한 인터페이스로 CGI 방식을 기반으로 mod_wsgi 모듈을 개발하였다.
프로세스 생성 부하를 줄이는 또다른 방식은 애플리케이션을 처리하는 프로세스를 미리 데몬으로 기동시켜 놓은 후, 웹 서버의 요청을 데몬에서 처리하는 것이다.
데몬은 백그라운드 프로세스의 일종으로 메모리에 상주하면서 멈추지 않고 계속 작동하는 대기 중인 프로세스다.
파이썬의 경우 데몬 방식에도 mod_wsgi 모듈을 사용한다. mod_wsgi 모듈은 위에서처럼 웹 서버 내장 방식으로도, 별도의 데몬 방식으로도 실행이 가능하다.
웹 서버의 내장 모듈로 제공하는 방식 vs 별도의 WSGI 서버 방식
별도의 WAS(Gunicorn) 서버를 따로 두면, 웹 서버는 리버스 프록시를 통해 로드 밸런싱을 할 수 있는 장점이 있다.
리버스 프록시(Reverse Proxy)는 인터넷과 WAS 서버 중간에서 WAS 서버를 대신해 중복 요청되는 정적 파일은 웹 서버에서 처리하고, 동적인 처리는 WAS 서버로 전달하는 역할을 한다. 또한 사용자의 요청이 많아지면 서버에 부하가 발생하게 되는데, 웹 서버는 로드(부하) 밸런싱(분산)을 통해 여러 대의 WAS 서버로 요청을 분산 처리할 수 있다. 즉, 웹 서버 1대에서 여러 대의 WAS에게 일을 시키는 것이다.
🔩 Load Balancing 구현 방법
보통 네트워크 OSI 7계층의 L4, L7 계층에서 로드밸런싱을 구현한다.
1) L4의 Transport Layer(IP와 PORT) 계층에서는 bentist.tistory.com:8080/ 혹은 bentist.tistory.com:8081/ 으로 접근 시, 서버 A와 서버 B로 요청을 고르게 나누어준다.
2) L7의 Application Layer(HTTP, FTP) 계층에서는 bentist.tistory.com으로 접근 시, /category와 /search 처럼 URL이나 쿼리 경로에 따라 처리할 WAS 서버를 결정해준다.
ex. 도커 컴포트 파일에서 NGINX 포트 설정으로 Load Balancing 구현
version: "3"
services:
nginxproxy:
image: nginx
ports:
- "8080:8080"
- "8081:8081"
WAS(Web Aplication Server) 등장: 더이상 웹 서버에서 프로그램을 호출 X
애플리케이션을 별도의 데몬으로 처리하는 방식이 발전함에 따라 애플리케이션 전용 데몬인 웹 애플리케이션 서버 방식으로 발전했다. WAS 방식은 웹 애플리케이션 서버에서 애플리케이션 프로그램을 실행하여 실행 결과를 웹 서버에 전달하는 역할을 한다.
웹 서버와 웹 애플리케이션 서버가 분리됨에 따라 웹 서버는 리버스 프록시, 캐시 기능, 정적 페이지 처리 등의 역할을 하고, WAS는 동적 페이지만 처리하도록 역할을 분담하여 훨씬 더 많은 요청을 처리할 수 있게 되었다.
cf. WAS는 웹 클라이언트로부터 직접 요청을 받아 처리하는 웹 서버의 기능도 있지만, 성능(로드 밸런싱)과 보안 측면에서 웹 서버와 WAS를 보통 분리해서 사용한다.
파이썬에서 웹 서버와 연동하기 위해 사용하는 mod_wsgi, uwsgi, gunicorn같은 프로그램들은 별개의 애플리케이션 전용 데몬으로 동작한다는 점에서 웹 애플리케이션 서버라고 할 수 있다.
로컬 개발 환경에서는 지금까지 python manager.py runserver 처럼 장고의 내장 서버를 구동하는 방식을 사용했다. 장고의 내장 서버는 웹 서버와 WSGI 서버(WAS)의 기능을 모두 포함하고 있다. 내장 서버는 기능이 단순하고 '대량 요청'이나 '동시 요청'을 효율적으로 처리하지 못하므로 실제 배포 시에는 아래와 같이 서버를 분리해야 한다. |
http 요청 -> 웹서버 (nginx, apache)
if static:
static 파일 -> 응답
else:
Development: 동적 요청 <-> flask/django(wsgi 내장서버 포함)
Production: 동적 요청 <-> wsgi(uwsgi, gunicorn)서버 <-> django python 프로그램
-> 웹서버 -> 응답
웹 서버에 동적 페이지 요청이 들어오면 웹 서버는 파이썬 프로그램을 호출해야 한다. 하지만 웹 서버는 파이썬 프로그램을 호출할 수 있는 기능이 없다. 어떻게 파이썬 프로그램을 호출해서 해석해야 하는지 모르기 때문이다.
WSGI는 파이썬 스크립트가 웹서버와 효율적으로 통신하기 위해 만들어진 인터페이스로, WSGI 규격만 맞추면 어떤 웹 서버에서도 파이썬 애플리케이션을 실행할 수 있다. 그러나 Apache나 Nginx같은 범용 웹 서버는 WSGI 처리 기능이 없어서 웹 서버와 파이썬 웹 애플리케이션 중간에서 WSGI 통신 규격을 처리해주는 WSGI 모듈 혹은 WSGI 서버가 반드시 필요하다. 웹 서버에 동적 요청이 발생하면 웹 서버가 WSGI 서버를 호출하고, WSGI 서버는 파이썬 프로그램을 호출하여 동적 페이지 요청을 대신 처리하는 것이다.
WSGI 서버는 웹 서버와 WSGI 애플리케이션 중간에 위치한다. 그래서 WSGI 서버는 WSGI 미들웨어(middleware) 또는 WSGI 컨테이너(container)라고도 한다.
- Django 개발 환경에서의 활용
웹 서버의 요청을 받은 WSGI 서버가 장고 프로그램을 호출하려면 장고로 만든 프로그램도 WSGI 서버와 통신될 수 있도록 WSGI 규격에 따라 애프리케이션을 작성해야 되는데, startproject 명령을 실행하면 자동으로 생성된 wsgi.py 파일이 이 역할을 한다.
# django_project/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
application = get_wsgi_application()
객체명은 반드시 application이어야 하며, 웹 서버는 이 WSGI 규격에 따라 만들어진 application 객체를 호출하여 장고의 애플리케이션을 실행할 수 있게 되는 것이다.