Nginx? 동시접속 처리에 특화된 웹 서버 프로그램

  • 기존의 Apache Web Server(이하 아파치)는 꽤나 오래전에 탄생(1995년)에 하였음

  • 클라이언트 요청 하나당 connection을 만들어 요청을 처리하는 구조였음
    → connection을 생성하기 위해 서버의 프로세스를 생성함

  • 이 프로세스를 만드는 작업이 꽤나 오래걸리는 작업이었기 때문에 미리 프로세스를 만든 후, 요청이 들어오면 해당 프로세스를 사용하는 방식이었음

  • 그러나 시대가 변하며 clients의 요청이 폭발적으로 많아지며 C10K(connection 10000)의 문제가 발생하게 됨
    • connection 마다 프로세스가 하나씩 맡다 보니, 이는 곧 메모리 부족 및 CPU 자원을 너무 많이 할당하게 됨 → 더이상 프로세스를 생성할 수 없음

    • (심지어 요청당 connection의 유효시간까지 있게 되니…)

비동기식 이벤트 기반 접근 방식

 

Master Process Worker Process (실제로 일 하는 프로세스)
- 엔진 엑스 설정 파일을 읽고, 유효성을 검사

- Worker Process를 생성하고 관리.

- Worker Process의 갯수는 설정 파일에서 정의됨.

- 미리 워커 프로세스의 개수를 지정하지 않으면 사용가능 한 CPU 코어의 개수의 맞게끔 자동으로 개수를 조정해줌.
- 생성된 워커 프로세스는 각자 지정된 listener 소켓을 배정 받아 새로운 클라이언트 요청이 들어오면 커넥션을 형성하고 처리함.

- 커넥션은 지정된 Keep Alive 시간만큼 유지되며, 이 시간 동안은 이전 클라이언트 요청을 계속 처리 할 수 있음

- 새로운 요청이 들어오면 새로운 커넥션을 형성하거나, 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리함.
  • 즉, 각 요청에 대해 다른 전용 프로세스 또는 스레드를 생성하는 대신, 하나의 워커 프로세스에서 여러 연결 및 요청을 처리한다.

  • 일반적으로 워커 프로세스는 CPU 코어당 하나가 생성되기 때문에 기존 아파치 서버보다 훨씬 적은 메모리가 사용되며, 작업 전환에 CPU 주기가 낭비 되지 않는다.(context switching 감소)

  • 수백만 건의 동시 요청을 성공적으로 처리하고 매우 확장이 잘 됨

NGINX 워커 프로세스 내부

엔진엑스가 비동기 처리 방식 구조로 한 개 또는 고정된 프로세스만 생성하여 사용.

  • 각 작업자 프로세스는 마스터 프로세스(Nginx 구성 기반)구성으로 초기화 되며, 마스터 프로세스에서 리스너 소켓과 함께 제공되며,
  • 이벤트를 기다리는 것으로 시작된다.
    • 작업을 받는 역할 → 리스너 소켓
  • 각 연결은 프로토콜마다 각각 맞는 상태 시스템에 연결된다.

  • HTTP 상태 시스템이 가장 일반적으로 사용되지만 NGINX는 스트림(원시 TCP) 트래픽 및 여러 메일 프로토콜에 대한 상태 시스템도 구현한다.
    • 여러 프로토콜을 다 처리할 수 있다는 얘기(하나의 작업 프로세스에서)

결국 워커 프로세스의 작동 순서는

1. 워커 프로세스는 리스너 및 커넥트 소켓에서 이벤트(사용자 요청)를 기다림

 

2. 소켓에서 이벤트가 발생하고 이를 처리함 

 

이때, queue를 확인 한 뒤, 새로운 이벤트가 있다면 처리함 (loop)

  1. 많은 이벤트를 수신한 다음 필요한 작업을 수행하며 하나씩 처리하게 됨,
  2. 따라서 모든 처리는 하나의 스레드에 있는 큐에 대한 루프에서 수행된다.
    • 대부분의 경우 매우 빠르며 엔진엑스는 즉시 대기열의 모든 이벤트를 처리한다.

 

스레드 풀

  • 그러나 위의 비동기 처리 방식에서 문제점이 하나 있다. (비단 모든 비동기 처리 방식의 쟁점이겠지만..)
    • 바로 블로킹의 발생 → 무거운 작업이 발생하면 상당한 시간 동안 이벤트 처리 주기를 중지해버림(ex: Disk I/O)
    • (동기 방식으로 데이터베이스에서 응답을 받는 경우) → 액세스까지 시간이 걸릴 수 있음
    • 이러한 작업을 처리하는 동안 워커 프로세스가 다른 작업을 수행할 수 없으며 사용 가능한 시스템 리소스가 더 있고,
    • 큐의 이벤트가 해당 리소스를 사용할 수 있더라도 다른 이벤트를 처리할 수 없게됨.

이해를 돕기 위한 예시   🤩

  • 위와 같이 가게 앞에 대기하고 있는 손님이 많은 영업사원을 생각해보자.
  • 대기열,기다리는 줄(큐)의 첫 번째 사람은 매장에 없는 창고에 있는 것을 요구한다.(무거운 작업)
  • 영업 사원은 상품을 배달하기 위해 창고로 이동하는데, 이 때 대기열의 사람들은 영업 사원이 올 때까지 기다려야한다.

  • 이것과 거의 흡사한 상황이 엔진엑스에서 메모리에 캐시 되어 있지 않은, 디스크 I/O 작업 요청 상황이다.
  • 대기열(큐)에 있는 다른 작업들은 디스크 작업이 필요하지 않을 수 있지만, 해당 무거운 작업이 끝날 때까지 강제로 기다려야 한다.
  • 결과적으로 대기 시간이 증가하고 시스템 리소스가 완전히 활용되지 않는다.

 

이해를 돕기 위한 예시 2

  • 스레드 풀을 사용하며 영업 사원은 더 똑똑 해졌다.
  • 전과 같은 상황으로, 손님이 매장과 멀리 떨어져있는 창고의 상품을 요청할때, 배송사원은 요청을 받고
  • 다른 배송 서비스에 손님의 주문을 맡겨버린다 → 배송 서비스에서 주문을 처리함
    • 해당 요청을 한 손님만 주문 완료를 기다리게 됨
  • 영업 사원은 무거운 작업을 다른 배송 서비스에 맡겨버리고 다음 손님을 받게 된다. 
    • NGINX 측면에서 스레드 풀은 배송 서비스의 기능을 수행한다.
    • 작업 대기열과 작업을 처리하는 스레드로 구성된다.
    • 워커 프로세스(영업 사원)이 긴 작업을 수행해야 하는 경우 작업을 자체적으로 처리하는 대신 스레드풀 대기열에 작업을 배치하여 맡긴다.

엔진엑스 역할

1. 정적 파일 처리하는 HTTP 서버로서의 역할

  • HTML, CSS , JavaScript, 이미지와 같은 정보를 웹 브라우저에 전송하는역할을 함
    • 만약 웹 서버를 이용하지 않는다면 요청 때마다 이러한 정보들을 웹 앱 서버에서 제공하기 때문에 시간도 시간이지만, 서버의 부하가 심해짐

2. 응용 프로그램 서버(WAS)에 요청을 보내는 리버스 프록시 역할

  • 리버스 프록시 란 외부 클라이언트에서 서버로 접근 시, 중간에서 중개자 역할을 하여 내부 서버로 접근할 수 있도록 도와주는 서버이며, 이를 활용했을때 얻을 수 있는 장점은 다음과 같다.
    • 보안
      • 외부 사용자로부터 내부망에 있는 서버의 존재를 숨길 수 있다.
      • 모든 요청은 리버스 프록시 서버에서 받으며, 매핑되는 내부 서버로 요청을 전달함.
      • 또한 Nginx는 SSL 설정도 가능하기 때문에, 사용자의 요청과 응답 등과 같은 정보를 암호화해서 통신이 가능하다.
      • 그렇지만, client ↔ Nginx만 SSL 설정을 하고 나머지 업스트림 서버와의 통신은 HTTP 통신으로만 할 수 있다고도 한다.
    • 로드밸런싱
      • 리버스 프록시 서버가 내부 서버에 대한 정보를 알고 있으므로,
      • 각 서버의 상태에 따라 부하를 분산시키며 요청을 전달할 수 있다. (여러 부하분산 알고리즘 사용)

엔진엑스로 정적 컨텐츠 서비스하기

  • 엔진엑스 설치 후 /etc/nginx/conf.d/default.conf에 생성된 기본 HTTP 설정 파일
server {
	listen 80 default_server;
    server_name www.example.com;
    
    location / {
    			root /usr/share/nginx/html;
                # alias /usr/share/nginx/html;
                index index.html index.htm;
    }


}
  • 이 설정은 HTTP 프로토콜과 80 포트를 사용해 /usr/share/nginx/html/ 경로에 저장된 정적 콘텐츠를 제공한다.
    • server블록은 새로운 server 블록을 선언하여, 엔진엑스가 처리할 새로운 컨텍스트를 정의함.
    • listen 80은 엔진엑스가 80 포트로 들어오는 요청을 수신하게 하고, 이 블록에 정의된 내용이 80 포트에 대한 기본 컨텍스트가 되도록 default_server 매개 변수를 사용한다.
      • 예제는 단일 포트만 사용하지만, 필요에 따라 포트 범위를 지정해 줄 수 있다.
    • server_name 지시자에는 서버가 처리할 호스트명이나 도메인명을 지정한다.
      • 만약 설정이 default_Server 매개변수를 통해 지정되지 않았다면, 엔진엑스는 요청 호스트 헤더값이 server_name 지시자에 지정된 값과 같을 때만 server 블록 에 지정된 내용을 수행한다.
      • 즉, 서버가 사용할 도메인이 정해지지 않았다면 default_server 매개변수를 사용해 기본 컨텍스트를 정의하고 server_name 지시자를 생략할 수 있다.
    • location 블록은 URL의 경로를 기반으로 한다.
      • 엔진엑스는 요청된 URI에 가장 적합한 location 블록을 찾는다.
      • root 지시자 는 주어진 컨텍스트에서 콘텐츠를 제공할 때 서버의 어떤 경로에서 파일을 찾을지 알려준다.
      • 엔진엑스는 root 지시자에 정의된 경로에 수신된 URI 값을 합쳐 요청된 파일을 찾는다.
        • root지시자 정의된 경로 + 80포트로 수신된 URI
      • location 지시자에 URI 접두어를 사용했다면 이 값도 root 지시자에 지정한 값과 합쳐진다.
        • 이렇게 동작하지 않도록 하려면 root 지시자 대신 alias 지시자를 사용한다.
    • index 지시자는 URI에 더는 참고할 경로 정보가 없을 때 엔진엑스가 사용할 기본 파일 혹은 확인할 파일 목록을 알려준다.
  •  

'Nginx' 카테고리의 다른 글

Nginx와 arm64  (0) 2023.08.04

+ Recent posts