Web Server부터 Nginx까지

 

서론

Nginx(엔진엑스) 에 대해 찾아보는 와중 기존 내가 알고있던 웹 서버(Web Server)에 대한 개념이 상충하는 것이 있어 이를 정리하고자 한다. 본 글에서는 Web ServerWAS의 개념 비교부터 시작하여 가장 보편적으로 사용되는 Web Server 엔진 및 실제 사용될 수 있는 프로젝트 구성을 Docker-compose를 통해 구성해보고자 한다.

 
 

본론

Web Server

위키백과에 정의된 웹 서버에 대한 정의는 다음과 같다

 서버(Web server)는 다음의 두 가지 뜻 가운데 하나이다.

  1. 웹 서버: 웹 브라우저와 같은 클라이언트로부터 HTTP 요청을 받아들이고, HTML 문서와 같은 웹 페이지를 반환하는 컴퓨터 프로그램
  2. 웹 서버 (하드웨어): 위에 언급한 기능을 제공하는 컴퓨터 프로그램을 실행하는 컴퓨터

웹 서버(web server)는 HTTP 또는 HTTPS를 통해 웹 브라우저에서 요청하는 [HTML 문서나 오브젝트(이미지 파일 등)을 전송해주는 서비스 프로그램을 말한다. 웹 서버 소프트웨어를 구동하는 하드웨어도 웹 서버라고 해서 혼동하는 경우가 간혹 있다.

출처 : https://ko.wikipedia.org/wiki/웹_서버

위에 정의된 바와 같이 웹 서버HTTP, HTTPS 통신 프로토콜을 통해 사용자가 요청하는 HTML 문서오브젝트, 즉 정적인 파일들을 전달해주는 서비스 프로그램을 말한다. 다만 이러한 정적인 페이지들의 경우 과거에는 충분하였을 지 모르나 기술이 발달한 요즘 시대에는 부족한 것이 사실이다. 이러한 태생적인 한계를 극복하기 위한 것이 WAS(Web Application Server) 이다.

 
 

WAS (Web Application Server)

마찬가지로 위키백과에 정의된 웹 어플리케이션 서버에 대한 정의는 다음과 같다.

웹 애플리케이션 서버(Web Application Server, 약자 WAS)는 웹 애플리케이션과 서버 환경을 만들어 동작시키는 기능을 제공하는 소프트웨어 프레임워크이다.[1] 인터넷 상에서 HTTP를 통해 사용자 컴퓨터나 장치에 애플리케이션을 수행해 주는 미들웨어(소프트웨어 엔진)로 볼 수 있다. 웹 애플리케이션 서버는 동적 서버 콘텐츠를 수행하는 것으로 일반적인 웹 서버와 구별이 되며, 주로 데이터베이스 서버와 같이 수행이 된다. 한국에서는 일반적으로 “WAS” 또는 “WAS S/W”로 통칭하고 있으며 공공기관에서는 “웹 응용 서버”로 사용되고, 영어권에서는 “Application Server” (약자 AS)로 불린다.

출처 : https://en.wikipedia.org/wiki/Application_server

웹 애플리케이션 서버(이후 WAS)는 우리가 통상적으로 인식하고 있는 웹 서버의 개념과 동일하다고 보면 된다. 사용자의 요청이 단순 정적인 파일 서빙으로 해결이 안 될 경우, 즉 사용자마다 서로 다른 페이지를 보여줘야 허거나 특정 비지니스 로직, 혹은 DB 와의 상호작용이 필요한 경우가 이에 해당된다.

출처 : https://dkswnkk.tistory.com/503?category=551275

 

WAS는 위 이미지와 같이 Web ServerWeb Container로 이루어져 있다. 다만 통상적인 개발 과정에서는 WASWeb Server를 분리하여 개발하는 경우가 잦다. 아래와 같은 구조를 의미한다.

출처 : https://dkswnkk.tistory.com/503?category=551275

 

즉 1차적으로 사용자가 요청한 내용을 Web Server에서 처리한다. 해당 요청이 단순 정적인 파일 서빙으로 해결될 수 있는 문제의 경우 Web Server가 자체적으로 해당 파일을 서빙하여 해결한다. 다만 비즈니스 로직이나 DB 조회가 필요한 동적인 파일의 경우 WAS에 요청하여 해당 요청을 처리한다.

 

이와 같은 구조로 구성할 경우 아래와 같은 장점을 얻을 수 있다.

1. WAS 서버 부하 방지

WAS 서버는 실제 비즈니스 로직 및 DB 조회를 담당하는데, 단순 정적 파일 또한 WAS에서 서빙할 경우 다른 작업으로 인해 불필요한 딜레이가 발생할 수 있다.

2. 로직 분리를 통한 보안 강화

일반적인 사용자들은 Web Server에만 접근하고 WAS는 알 필요가 없다. 따라서 사용자들에게 노출되는 부분은 Web Server로 한정지어 불필요한 위험으로부터 분리하여 비즈니스 로직 등 중요 데이터들을 안전하게 지킬 수 있다.

3. 다수의 WAS 서버 연결

동일한 서비스를 제공하는 복수의 WAS 서버를 연결하여 특정 서버에 가해지는 부하를 줄일 수 있다 (Load Balancing). 또한 특정 한 서버가 다운 되더라도 여분의 서버를 통해 안정적인 서비스를 제공할 수 있다 (Fail Over)

4. 여러 웹 어플리케이션과의 연결

서로 다른 기능을 담당하는 WAS를 연결하여 복수의 서비스를 제공할 수 있다

 
 

Nginx

Nginx(엔진엑스)란 Web Server의 일종으로, 현재 가장 상용화 된 방식 중 하나이다. 과거엔 Apahce(아파치)를 활용하여 Web Server를 구현하였는데, Apache는 쓰레드 방식으로 하나의 요청당 하나의 쓰레드가 새로 생성되어 해당 요청을 처리하는 방식이다. 이러한 방식의 경우 쓰레드를 생성하고 또 요청을 처리하기까지 많은 오버헤드가 발생하게 되어 사용에 꺼려지고 있다.

Nginx는 이와 대조적으로 사전 정의된 설정값(*.conf)에 따라 Worker Process가 생성되고, 이 Worker Process가 일정 시간(keep alive)동안 유지되어 사용자의 요청(Connection)을 처리하는 방식이다. 이렇게 보면 Apache와 큰 차이가 없어 보이지만 가장 큰 핵심은 사용자의 요청을 비동기적으로 처리한다는 것이다. 사용자의 요청은 Queue에 대기상태로 쌓이고 유휴상태인 Worker Process가 해당 작업을 맡아 처리하는 방식이다. 이를 Event-Driven 방식이라 한다.

출처 : https://whatisthenext.tistory.com/123

 

두 Web Server간의 성능 차이를 비교해보면 아래 이미지와 같다.

Connection 수에 따른 메모리 사용량 비교
출처 : https://statuslist.app/nginx/variables/

 

동시 요청된 Connection 수에 따른 처리 속도 비교
출처 : https://statuslist.app/nginx/variables/

 
 

Nginx to WSGI

WSGI(위스키)는 Web Server Gateway Interface의 약자로, Web ServerPython 기반의 WAS를 연결해주기 위한 통신 규약이다. 일반적인 Apache, Nginx와 같은 Web ServerPython을 모르기 때문에 Django(장고)나 Flask(플라스크) 기반으로 구현된 WAS에 요청을 보낼 수 없다. WSGI는 그 사이에서 이를 중계해주는 역할이다.

가장 일반적으로 DjangoGunicorn(지유니콘)을 사용한다. 다만 Django를 경험해본 사람들은 Gunicorn을 사용하지 않고도 Django 기반 백엔드 서버에서 HTTP 통신을 정상적으로 이루었을 것이다. 이는 python manage.py runserver 명령어를 통해 WAS를 실행할 경우 기본적으로 Django 내장 WSGI 서버를 통해 사용자의 HTTP 요청을 처리하기 때문이다. 그럼에도 불구하고 장고 내장 WSGI 서버가 아닌 Guicorn이란 별도의 WSGI 서버를 통해 요청을 처리하는 이유는 아래와 같다.

Gunicorn has many features that Django’s built-in server is lacking:

  • gunicorn can spawn multiple worker processes to parallelize incoming requests to multiple CPU cores
  • gunicorn has better logging
  • gunicorn is generally optimized for speed
  • gunicorn can be configured to fine grades depending on your setup
  • gunicorn is actively designed and maintained with security in mind

출처 : https://stackoverflow.com/questions/35657332/django-difference-between-using-server-through-manage-py-and-other-servers-like

 
 

실습

실습은 Docker-compose를 통해 Postgres, Django, Nginx 각각에 해당하는 Docker Container를 만들어 구성된 환경에서 진행되었다. 본 실습은 Docker-compose를 통해 각 Container간의 연결에 초점을 맞춘 관계로 Django 프로젝트 설정에 관한 내용은 생략하도록 하겠다. local_settings.py를 통해 DB 관련 설정을 해주었단 것만 주의하면 큰 문제는 없을 것이다. 프로젝트 디렉토리 구조는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# > tree

.
├── bin
│ ├── compose
│ │ ├── django
│ │ │ └── Dockerfile
│ │ └── nginx
│ │ └── nginx.conf
│ ├── docker-compose.yml
│ ├── requirements.txt
│ └── restart_docker.sh
└── practice
├── manage.py
├── practice
│ ├── __init__.py
│ ├── asgi.py
│ ├── local_settings.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py # Django 기본 WSGI 서버
└── practice_site
├── __init__.py
├── admin.py
├── apps.py
├── fixtures
│ └── initial_data.json
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── tests.py
├── urls.py
└── views.py

Docker 컨테이너 구성을 위한 docker-compose.yml 파일은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# ./bin/compose/docker-compose.yml

version: '3'

services:
practice_database: # DB 서비스
container_name: practice_database
image: postgres:15
environment:
- POSTGRES_DB=practice
- POSTGRES_USER=practice
- POSTGRES_PASSWORD=practice

practice_web: # Django 서비스
container_name: practice_web
build:
context: .
dockerfile: ./compose/django/Dockerfile # 별도의 Dokcerfile을 통해 빌드
volumes:
- ..:/app # docker-compose 기준 상대참조하여 /app 디렉토리 내부로 마운트
working_dir: /app/practice
command: >
sh -c "
echo yes | python manage.py collectstatic && # static 파일을 빌드하기 위한 명령어
wait-for-it.sh practice_database:5432 && # DB 설정 완료까지 대기
python manage.py makemigrations &&
python manage.py migrate &&
python manage.py loaddata initial_data.json &&
gunicorn sweat.wsgi:application --workers=20 --timeout=300 --bind 0.0.0.0:8000
"
depends_on:
- practice_database

practice_nginx: # Nginx 서비스
container_name: practice_nginx
image: nginx:latest
ports:
- "80:80"
volumes:
- ../practice/static:/static # collectstatic 명령어를 통해 빌드된 파일을 마운트
- ./compose/nginx:/etc/nginx/conf.d # conf 설정 파일 마운트
depends_on:
- practice_web

Docker-compose 구성에 사용된 Dockerfilenginx.conf는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# .bin/compose/django/Dockerfile

FROM python:3.10.6
ENV PYTHONUNBUFFERED 1

RUN apt-get update -y

# wait-for-it 스크립트를 통해 데이터베이스 설정 완료를 보장
RUN wget -O /usr/local/bin/wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh
RUN chmod +x /usr/local/bin/wait-for-it.sh

# 디렉토리 설정 및 필수 패키지 설치
WORKDIR /app
ADD requirements.txt /app/
RUN pip install --upgrade pip \
&& pip install -r requirements.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ./bin/compose/nginx/nginx.conf

server {
listen 80;
server_name practice_web;

location / {
proxy_http_version 1.1;
proxy_pass http://practice_web:8000;
}

location /static/ {
alias /static/;
}

}

Docker 재실행을 위한 스크립트 restart_docker.sh는 다음과 같다

1
2
3
4
# ./bin/compose/restart_docker.sh

docker-compose -p practice down
docker-compose -p practice up -d

해당 스크립트를 통해 Docker를 재실행해보면 아래와 같은 결과를 Docker-Desktop에서 확인할 수 있다.

image

 
 

참고 문헌

Web Server와 WAS의 차이

WAS vs 웹 서버

Web Server와 Web Application Server

웹 서버 vs WAS

웹서버와 WAS(Web Application Server)

Web server performance comparison

WSGI 서버

WSGI란?

Nginx란?

Nginx란 무엇인가?

Nginx 이해하기 및 기본 환경 설정 세팅하기

Nginx 설치 및 nginx.conf, default.conf 이해하기

Nginix, Gunicorn 배포

Django Nginx 연결하기

Django 웹서버, Nginx 사용하기

Docker-compose로 Django 환경 구축하기

Docker-compose로 Django, Nginx, Gunicorn 연결후 AWS 배포하기

Docker + Nginx + gunicorn + django

Django: Difference between using server through manage.py and other servers like gunicorn etc. Which is better?

How to use NGINX variables + Reference List