WS vs WAS

NGINX 포스트에서 갑자기 WS와 WAS가 가장 처음 나오는 게 의아할 수 있지만, NGINX를 이해하기 위해서는 가장 기본적으로 WS(Web Server)와 WAS(Web Application Server)를 반드시 이해하고 있어야 한다. NGINX는 WS의 일종이기 때문이다.

WS와 WAS의 차이점은 정적 파일만을 반환하는지, 동적 처리까지 이루어진 후 반환하는지에 있다. 아래 그림에서 클라이언트가 보낸 요청은 바로 WAS로 가지 않고 우선 WS에 먼저 도달한다. 여기서 WS는 요청을 WAS로 넘기기 전 요청을 분석(?)한다. 이 요청이 안전한 요청인지, 굳이 WAS까지 가지 않아도 WS에서 해결할 수 있는 요청인지 등등을 점검하는 것이다. 이 과정을 거치고 난 후에야 요청이 WAS에 도달하게 된다. 이후에는 일반적으로 우리가 알고 있는 요청에 대한 처리, 반환의 과정이 이루어진다.

위 그림에서 빨간 글씨는 각각의 역할을 수행하는 예시들에 해당한다. 그런데, 왜 모든 요청을 하나의 서버에서 모두 처리하지 않고 WS와 WAS를 분리하는 걸까? 이 질문에 대한 가장 대표적인 답변은 크게 3가지가 있는데, 하나는 WAS의 부담을 줄이기 위함이고 하나는 보안을 유지하기 위함이며, 하나는 '로드 밸런싱'이 가능하다는 점이다.

우선 WAS의 부담을 줄인다는 맥락에서는, WAS는 모든 핵심 비즈니스 로직이 수행되는 서버이고, 이 핵심 비즈니스 로직은 대부분 동적 처리를 요구한다. 그런데 모든 요청을 WAS가 모두 감당하면 문제가 발생할 확률이 높아질 수밖에 없다. 클라이언트의 요청이 복잡한 요청이든 단순한 요청이든 하나의 요청임에도 많은 복잡한 요청이 앞에서 먼저 대기하고 있어 단순한 요청조차 처리되지 못하고 처리 순서를 기다리는 병목 현상이나 과도한 트래픽에 따른 서버 장애 등의 문제가 발생할 수 있는 것이다.

그리고 보안을 유지하기 위함이라는 맥락에서는 WAS 자체가 외부에 드러나 있으면 공격받기가 쉽다. 여기서 WS가 대신 외부에 드러나 있게 되면 내부에 직접 공격을 하기가 어려워진다는 단순한 이야기다. 물론 실제로 이렇게 단순하지는 않지만 큰 틀은 여기서 크게 벗어나지 않는다.

마지막으로 로드 밸런싱이 가능하다는 점은 말 그대로 요청을 처리하는 서버가 여럿이라면, 클라이언트는 어느 상황에 어느 서버로 요청을 보내야 하는 지가 통일되어 있지 않은 상황이 된다. 즉 엔드 포인트가 늘어난다는 말이고 이는 관리에 있어 비효율로 이어진다. 하지만 클라이언트는 항상 WS로 요청을 보내고, WS에서 이 요청을 적절한 서버로 분배한다면 관리에 있어서 훨씬 효율적이다. 더 나아가 서버 장애가 발생해도 예비 서버를 즉각적으로 가동시킨다거나, 새 버전이 릴리즈될 때 무중단 배포도 가능하다.

메인 서버의 앞에서 보안 유지 기능과 로드 밸런싱 기능을 한다는 점에서, NGINX는 리버스 프록시(reverse proxy) 서버로서 기능한다고 말하기도 한다.

Apache vs NGINX

이제 NGINX로 넘어가 보자. 이미 언급했듯이 NGINX는 WS의 일종이다. 이 말은 WS에는 NGINX 이외에도 다른 WS들이 있다는 말이다. 이 중 NGINX 이전에 가장 많이 사용되던 WS가 Apache이고, NGINX는 Apache의 단점을 보완하는 것을 목적으로 등장했다. 그렇다면 Apache의 단점은 무엇이었고, NGINX는 어떻게 이 단점을 개선했을까? 자연스럽게 들 수밖에 없는 의문이다. 따라서 우선 Apache에 대해 먼저 알아보자.

아래는 Apache가 요청을 처리하는 방법이다. 요청이 1개당 connection을 하나씩 할당하고, 이 요청을 처리하기 위한 process가 하나씩 할당된다. process는 스레드 풀에서 스레드를 호출해 이 요청을 처리한다. 그리고 요청이 모두 처리되면 connection은 close된다. 그런데 process를 요청이 들어올 때마다 생성하기에는 process 생성에 시간이 오래 걸리기 때문에, PREFORK라는 방법으로 process를 사전에 생성해 둔 뒤, 요청이 들어올 때마다 할당하여 빠른 처리가 가능하도록 했다. 심지어 높은 확장성까지 갖춰 그야말로 Apache는 최고의 WS로 급부상했다.

하지만 컴퓨터 보급률이 증가함에 따라 C10K(connection 10000 problem)문제가 발생했는데, 이는 10000개의 connection 문제 즉 요청이 지나치게 많아졌기에 발생하는 문제였다. 사실 잘 생각해 보면 Apache의 위와 같은 처리 방식은 문제가 있다. process를 무한히 생성해 둘 수 없기 때문에 대규모 트래픽을 감당하기 어렵다는 점이다. 즉 메모리와 CPU 같은 하드 웨어의 발전에 소프트 웨어의 발전이 종속될 수밖에 없다.

따라서 Apache가 대규모 트래픽에 취약하다는 단점을 보완하기 위해 NGINX가 등장하게 된다. NGINX는 요청이 들어오면 먼저 connection을 생성하는데, 간단한 정적 파일에 대한 요청일 경우 리소스를 크게 소모하지 않기 때문에 스스로 처리하고, 반대로 리소스를 많이 소모하는 동적 요청은 NGINX로부터 Apache로 다시 connection을 생성하여 Apache에서 처리하도록 한다.

여기서 들 수 있는 의문은, 그렇다면 그냥 NGINX 위치에 또 하나의 Apache 서버를 두는 것과 무슨 차이가 있는지이다. 이는 Apache와 NGINX의 요청 처리 방식의 차이와 이에 따른 리소스 소모의 차이에서 나타난다. 아래는 NGINX의 요청 처리 방식이다.

  • Nginx는 기본적으로 nginx.conf라는 설정 파일에 따라 worker process를 생성하고, 이 worker process는 master process의 관리를 받는다.
  • worker process는 생성될 때 listen 소켓을 배정받는데, 여기에 새로운 클라이언트의 요청이 들어오면 connection을 형성하고 처리한다.
  • connection은 설정 파일에서 설정한 keep-alive_timeout 시간만큼 유지된다. 즉 Keep-Alive 시간 동안에는 동일한 Client로부터의 요청은 새로 connection을 생성하고, process를 할당하는 과정이 없다는 것이다.
  • 이 경우, 하나의 클라이언트에 대한 connection이 keep-Alive인 동안 리소스가 낭비되는 것이 아닌가 싶지만 형성된 connection으로 클라이언트로부터 keep-alive_timeout 시간 동안 아무런 요청이 없다면 해당 connection을 close하고 새로운 connection을 형성하거나 이미 생성되어 있던 다른 connection, 즉 해당 worker process에 할당되어 있던 다른 connection의 요청을 처리한다.
  • Apache와의 가장 큰 차이점은 NGINX와 달리 Apache는 스레드 기반으로 하나의 스레드당 하나의 connection만 유지될 수 있기에 '이미 생성되어 있던 다른 connection'이라는 게 있을 수 없다. connection이 생성될 때 스레드 풀에서 대기 중인 스레드를 할당하는 방식이기 때문이다. 쉽게 말해 요청들이 connection 생성 자체를 대기하고 있는 것이다. 따라서 C10K문제처럼 대규모 트래픽이 발생하면 요청들이 계속 처리되지 못하고 장기간 대기하게 되고, 이는 서버 장애까지 이어질 수 있다.
  • 반면 NGINX는 master process가 worker process를 생성하되 단일 스레드 방식으로 작동하는 대신 스레드마다 앞에 event handler가 앞선 요청의 connection이 close되지 않았더라도 connection을 생성한다. 그리고 worker process의 스레드는 이렇게 다중으로 생성되어 있는 connection을 동시에 처리한다. Apache가 요청 하나당 스레드를 하나씩 배분하여 병렬 처리를 하는 반면 NGINX는 스레드 하나가 여러 요청을 할당받아 병렬 처리를 하기 때문에 NGINX에서는 C10K 문제가 발생하지 않는다.
  • NGINX에서 이처럼 connection을 생성하고, 닫고, 처리하는 것을 '이벤트'라고 부르며 따라서 NGINX를 이벤트 기반 비동기 처리 웹 서버 어플리케이션이라고 부른다.

그렇다면, 이제 상당 부분 의문이 해소되었다. NGINX가 가진 Apache와의 가장 큰 강점은 하나의 스레드에게 여러 connection을 할당한다는 점이다. 하지만 단일 스레드만을 사용한다면 자연스럽게 성능에 대한 의문이 들 것이다. 놀랍게도 NGINX는 성능 면에서도 Apache를 크게 앞서는 모습을 보여준다. 아래는 동시 커넥션 수의 증가에 따른 메모리 사용률과 처리되는 초당 요청 수의 그래프다.

동시 커넥션 수당 메모리 사용률동시 커넥션 수에 따라 처리되는 초당 요청 수

동시 커넥션 수가 증가함에 따라 Apache의 메모리 사용율은 올라가는 반면 초당 처리되는 요청의 수가 증가하지는 않는다. 리소스는 많이 소모하지만 효율이 개선되지 않는다는 말이다. 반면 NGINX는 동시 커넥션 수가 증가하더라도 초당 처리되는 요청의 수가 감소하지만 메모리 사용율이 올라가지는 않는다. 이처럼 NGINX는 기존 Apache의 단점을 개선하는 것을 넘어 2023년 기준으로는 웹 서버 어플리케이션 순위를 역전하였다.

이처럼 NGINX는 이제 WS로서 가장 인기 있는 어플리케이션이 되었다. 하지만 Apache 역시 오래 사용된 WS이기 때문에 Spring 진영에서는 아예 Apache와 Tomcat을 묶어 하나의 WAS로 취급하고 있기도 하다. 또한 Apache는 확장성이 뛰어나다는 강점과 오래 사용된 WS라는 점에서 다양한 모듈과 거대한 커뮤니티를 형성하고 있다. 그리고 애초에 NGINX는 Apache를 보완하려는 목적으로 개발되었기 때문에 확장성 측면에서는 불리할 수밖에 없다. 이러한 장점과 단점을 잘 파악하고, 각 상황에 맞는 WS를 선택하는 것이 중요하다.

Reference

https://dkswnkk.tistory.com/513
https://icarus8050.tistory.com/57

profile
잘 읽어야 쓸 수 있고 잘 들어야 말할 수 있다

0개의 댓글