과거, 대학시절 2학년 2학기를 종강하고 대학 동기와 42seoul의 webserv라는 프로젝트를 진행하기로 했다. 이 webserv 프로젝트의 요구사항은 nginx와 같이 I/O Multiplexing 기술을 활용한 서버를 만드는 과제이다.
HTTP 프로토콜 기반이기에 HTTP 관련 RFC723X 문서들을 읽고나서 이게 내가 읽는 것인지 읽힘을 당하고 있는지 모르는 듯 읽어나갔다. 그것보다 양이 엄청나다..
이후 많은 학습과 더불어 설계를 진행하고 대략적인 프로젝트를 진행하다 학부 생활과 병행하는 것이 한계가 있어 무산되었다...

이후 4학년이 끝날 무렵 지금까지 해온 프로젝트에 Nginx 사용이 종종 있었다. 이에 Nginx가 무엇인지 정리하려 한다.

사전 지식

block, non-block, Sync, Async 차이

A -> B : A가 B를 호출했다고 가정

blocking

요청을 보낸 후, 그 작업이 완료될 때 까지 아무것도 하지 않고 기다려야 하는 상태이다.

  • A는 B를 호출한 뒤, B가 작업을 완료할 때까지 아무것도 못하고 있음

NonBlocking

요청을 보낸 후, 요청이 끝날 때까지 기다릴 필요 없이 다른 일을 할 수 있는 상태이다.

  • A는 B를 호출한 뒤, B가 작업을 완료하든 말든 A 작업 계속 진행

동기(synchronous)

작업이 순서대로 진행되며, 요청이 완료되면 응답을 확인하거나 작업이 끝나는 시점을 직접 확인한다. 이는 작업이 일련의 순서를 따르며 작업의 순서가 보장된다. (작업의 순서를 보장한다는 것은 '현재 작업의 응답'을 받는 시점과 '다음 작업을 요청'하는 시점을 맞추는 것이다. 다음 작업 자체가 순서가 있다는 것을 의미하고, 이전 작업이 완료되기 전까지 다음 작업이 수행되지는 않는다. )

  • A가 B를 호출한 뒤, A가 직접 작업 완료 상태를 확인함. 즉 함께 작업을 한다는 생각

blocking과의 혼동을 느낄 수 있는데 blocking 되는 동안 다른 작업이 끼어들 수 있냐 없냐의 문제를 보면 순서의 개념으로 sync/Async와 관련된다 보면 된다.

비동기(asynchronous)

작업이 진행되는 동안 요청자가 따로 확인할 필요 없이, 완료되면 알아서 결과를 알려준다. 작업 순서를 고려하지 않고 동시에 작업이 가능하다.

  • A가 B를 호출한 뒤, A는 B의 완료 여부를 확인하지 않고, B가 작업이 끝나면 A에게 결과를 전달
synchronous & blocking: A가 B의 작업을 기다리며 상태를 계속 확인. B가 완료될때 까지 A는 다른 작업 불가
synchronous & non-blocking: A는 B의 작업을 기다리지 않고 A의 일을 하면서, A는 B의 작업 상태를 계속해서 확인
asynchronous & blocking: A는 B의 작업을 기다림. B는 작업이 완료되면 A에게 결과 전달.
asynchronous & nonblocking: A는 B의 작업을 기다리지 않고 A의 일을 함. 그러던 중 B는 A에게 작업 결과를 전달.

block, non-block: 현재 작업이 차단되는가에 관점
synch, asynch: 작업이 순차적인가에 관점

사용자, 서버, 운영체제 등의 관점에 따라 블로킹, 논블로킹, 동기, 비동기가 달라질 수 있다. 

I/O Multiplexing이란 무엇인가?

I/O 작업은 단순 단일 server 내에서 일어나는 읽기/쓰기 뿐만 아니라 server-Client간 네트워크 통신에도 적용되는 개념이다. I/O Multiplexing은 각 모델중 하나이다.

다중화 작업으로 여러 I/O 작업을 독립적으로 관리하는 방법이다.

I/O Multiplexing 모델 (epoll, kqueue, select)


각각의 요청은 blocking 되지만, 비동기적으로 여러 소켓이 동시에 동작합니다. 소켓 작업이 완료되면 event처럼 callback을 전달하는데, 서버에서는 전체 callback을 하나로 관리하고 완료된 소켓을 찾아 후속 작업을 처리합니다. 즉, (epoll, kqueue, select) 다중 접속 처리에 관여한다.

  • Nginx는 Non-blocking I/O와 I/O Multiplexing을 결합하여 하나의 워커 프로세스가 다수의 연결을 처리할 수 있다. 이를 통해 각 연결에 대해 별도의 스레드나 프로세스를 생성하지 않고도 높은 동시성을 제공한다.

webServer

WS(Web Server) : 클라이언트의 요청에 따라 적절한 자원을 전달해준다. (Apache, Nginx)
WAS(Web Application Server) : 웹서버의 부하분산을 위해 더 고도화된 서버. (Tomecat)

  • WAS 동작 방식
  1. container는 ws로 부터 클라이언트의 요청인 HttpRequest를 전달받는다.
  2. container는 배포 web.xml를 참조하여 해당 서블릿에 대한 스레드를 생성하고, 요청 및 응답 객체 생성.
  3. container는 요청에 맞는 서블릿 호출
  4. 호출된 서블릿의 작업을 담당하는 스레드는 요청에 따라 해당 함수 호출.
  5. 4의 결과에 따른 동적 페이지를 응답 객체에 담아 컨테이너로 전달
  6. 5에서 전달받은 응답 객체를 HttpResponse 형태로 변환하여 WS에 전달하고, 생성되었던 스레드, 응답/요청 객체를 삭제

정적 자원, 동적 자원의 차이로 보는 시각도 있지만 JSP, PHP 등 동적 컨텐츠 생성 플러그인을 지원하여 WS/WAS 모두 동적 데이터 전달이 가능하다.


Nginx란 무엇인가?

등장 배경

  • C10K 문제: 동시접속자가 늘어날 수록, 사용자들이 동시다발적으로 I/O 작업을 많이 하다보니 사용자들이 사용하는 서버의 자원은 점점 늘어나게 되고, 서버는 수많은 동시다발적 처리에 대한 I/O 처리를 견디다 못해 결국 다운이 되는데 이러한 현상을 C10K 문제라 한다.
  • 이 문제의 적합한 nginx의 비동기 Non-blocking 이벤트 기반 서버가 향후 빛을 보게 된다.

Nginx를 사용하는 이유

과거 멀티쓰레드를 사용하던 시절에는 Blocking I/O 방식 처리를 진행하였다. 그렇기에 동시적으로 많은 작업을 처리하는것에 있어 부적합 하였다. 즉, 요청이 많을 수록 작업 처리 방식이 무겁고, 오랜 시간이 소요된다.

  • 기존 apache blocking 방식의 문제점
    • 요청 하나당 하나의 쓰레드가 생성되어 커넥션마다 프로세스를 할당하기에 메모리 부족으로 이어진다.
    • 무거운 작업이 만약에 서버에 요청된다면 해당 작업이 처리될 때 까지 결국 다른 요청은 아무것도 못하고 대기해야 하는 문제점
  • nginx의 Non-blocking 방법
    • 요청들을 이벤트 기반 처리를 진행하여 비동기 Non-Blocking 방식으로 프로세스 작업이 끝날 때 까지 대기하지 않는다.
    • 이벤트들이 큐 형식으로 worker process에게 전달되며 이 이벤트들은 큐에 담긴 상태로 비동기 상태 대기를 한다. worker process에서 이 이벤트를 하나씩 꺼내어 처리해 나가 쉬지 않고 일을 하게된다.
      • 여러개의 커넥션을 전부 Event Handler를 통해 비동기로 처리
  1. Nginx는 요청에 대한 작업을 처리할 때, 스레드가 직접적으로 작업을 하진 않고 이 작업들을 '이벤트 핸들러' 라는 곳으로 보낸다.
  2. 이벤트 핸들러에서 작업을 마치게 되면 먼저 작업이 완료된 순서되로 Queue에 쌓이게 된다.
  3. 매번 Event Loop은 Queue를 체크하면서 완료된 작업이 있는지 체크합니다. 이를 통해 CPU는 최대한 IDLE(어떠한 프로그램에 의해서도 사용되지 않는 상태)가 아닌채로 구동.
  4. Queue에 완료된 작업이 있을 경우 이를 클라이언트에게 응답.

멀티 쓰레드: 하나의 프로그램에 동시에 여러 작업을 진행, 프로세스 하나의 자원을 공유하여 처리 속도가 빠르지만, 한정된 자원을 쪼개서 사용하기에 쓰레드별로 자원을 효율적으로 분배해야 한다.
apache에도 Non-blocking 모델이 존재한다


nginx

  • 사전에 서버 개발자가 쓰레드 개수를 생성(Thread pool)하고, 해당 쓰레드 풀 내에 한정된 쓰레드 풀만을 가지고 HTTP 요청에 대한 작업을 처리한다.

    • 쓰레드가 너무 적을 경우 작업을 처리함에 있어 쓰레드가 부족할 수 있음
    • 쓰레드가 너무 많을 경우, 일하지 않는 낭비 쓰레드가 생기게 됨
  • nginx는 하나의 프로세스에서 여러개의 클라이언트와의 통신을 처리가 가능하고, 내부적으로 커널 큐를 사용해 작업의 순서를 보장함. 매우 큰 작업이 발생 시 해당 작업만 따로 쓰레드 풀에 넣어서 관리한다.

  • 하나의 마스터 프로세스에서 여러개의 워커 프로세스를 통해 이벤트 기반 처리를 활용하여 비동기 Non-Blocking 방식으로 프로세스 작업이 끝날 때까지 대기하지 않는다.

    • 비동기: 요청된 작업이 들어오고 큐에서 작업이 완료되면 이를 클라이언트에 응답
    • Non-block: 각 이벤트(요청)들은 서로의 작업에 관여하지 않음

Nginx's Architecture


그래서 nginx를 어디서 어떻게 사용하는가?

  • Nginx를 외부의 요청을 받아 뒷단의 서버로 요청을 전달하는 리버스 프록시 역할로 사용
    • 직접 프로젝트에 적용해서 사용했던 예시이다. nginx를 ec2 내부에서 설치하고 사용자의 요청을 받아서 각 8081, 8082에 프록시 해주어 무중단 배포를 진행하였다.
    • proxy: 내부 네트워크에서 인터넷을 접속 시 빠른 엑세스나 안전한 통신을 확부하기 위해 중계 해주는 서버를 의미한다.
  • 로드밸런싱 설정을 간단하고 저렴하게 가능하다. 라운드 로빈, 해시 등 분산을 위한 알고리즘 적용 가능하다.

  • 설정 방식은 nginx 공식 문서에 잘 나와있다.

  • [nginx 공식문서] https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/

  • 리버스 프록시로 서버 앞단에 배치하여 게이트웨이 역할을 통해 보안적인 측면에서 활용 가능하다.

    • 서버 앞단에 배치하여 요청의 유효성을 검증.
    • 클라이언트와 서버 간 직접 통신을 차단하여 민감한 정보를
      보호.
    • HTTPS 설정 및 방화벽과의 연계를 통해 보안성을 높임.
    • SSL 터미네이션 클라이언트와는 https, 서버와는 http 통신. 이 구조를 통해 뒷단의 서버가 복호화 과정을 감당하지 않고 비즈니스 처리에 더 집중할 수 있도록 한다. 보통 뒷단 서버와 Nginx를 같은 서브넷에 속하게 해서 보안이슈를 해결한다.

HTTP 프로토콜을 사용해서 전달되는 컨텐츠를 캐싱할 수도 있다. 이 외에도 HSTS(HTTP Strict Transport Security), CORS(Cross-Origin Resource Sharing)처리, TCP/UDP 커넥션 부하 분산, HTTP/2 등의 일을 한다.


apache vs nginx

apache

  • 아파치는 멀티프로세스 기반으로 각 요청마다 새로운 스레드를 생성하여 적은 수의 요청에 적합하다. 모듈화 되어 있어 확장성이 뛰어나다.

nginx

  • nginx는 이벤트 기반 처리로 단일 스레드에서 비동기적으로 다중 요청 처리를 진행하며 대규모 트래픽에 적합하다.

Nginx가 트래픽을 분산 처리하여 Apache 서버 확장 가능하며, Nginx를 앞단에 필터 역할을 통해 보안적 강화가 가능하고, 터미네이션과 같이 성능 최적화가 가능하다.

0개의 댓글