NGINX: 성능 & 확장을 위한 설계 방법

NGINX 는 웹 성능 면에서 선두를 달리고 있으며 이는 모두 소프트웨어 설계 방식 때문입니다. 많은 웹 서버와 애플리케이션 서버가 단순한 스레드 또는 프로세스 기반 아키텍처를 사용하는 반면, NGINX는 최신 하드웨어에서 수십만 개의 동시 연결로 확장할 수 있는 정교한 이벤트 기반 아키텍처로 돋보입니다.

Inside NGINX 인포그래픽은 상위 수준 프로세스 아키텍처에서 드릴다운하여 NGINX가 단일 프로세스 내에서 여러 연결을 처리하는 방법을 설명합니다. 이 포스트에서는 모든 것이 어떻게 작동하는지 자세히 설명합니다.

목차

1. 장면 설정 – NGINX 프로세스 모델
2. 아키텍처가 중요한 이유는 무엇입니까?

3. NGINX는 어떻게 작동합니까?
4. NGINX Worker 프로세스 내부
5. 상태 머신 스케줄링
5-1. 블로킹 상태 머신
5-2. NGINX는 진정한 그랜드마스터입니다.
5-3. 차단 다중 프로세스 아키텍처보다 빠른 이유는 무엇입니까?
6. 구성 업데이트 및 NGINX 업그레이드
7. 결론

1. 장면 설정 – NGINX 프로세스 모델

NGINX(및 NGINX Plus) 마스터 프로세스는 작업자, 캐시 관리 및 캐시 로더의 세 가지 유형의 하위 프로세스를 생성합니다. 그들은 캐싱, 세션 지속성, 속도 제한 및 로깅을 위해 공유 메모리를 사용했습니다.

이 설계를 더 잘 이해하려면 NGINX가 실행되는 방식을 이해해야 합니다. NGINX에는 Master 프로세스(구성 읽기 및 포트 바인딩과 같은 권한 있는 작업 수행)와 여러 worker 및 helper 프로세스가 있습니다.

# service nginx restart
* Restarting nginx
# ps -ef --forest | grep nginx
root     32475     1  0 13:36 ?        00:00:00 nginx: master process /usr/sbin/nginx 
                                                -c /etc/nginx/nginx.conf
nginx    32476 32475  0 13:36 ?        00:00:00  _ nginx: worker process
nginx    32477 32475  0 13:36 ?        00:00:00  _ nginx: worker process
nginx    32479 32475  0 13:36 ?        00:00:00  _ nginx: worker process
nginx    32480 32475  0 13:36 ?        00:00:00  _ nginx: worker process
nginx    32481 32475  0 13:36 ?        00:00:00  _ nginx: cache manager process
nginx    32482 32475  0 13:36 ?        00:00:00  _ nginx: cache loader process

이 4core 서버에서 NGINX master 프로세스는 4개의 worker 프로세스와 on-disk 콘텐츠 캐시를 관리하는 두 개의 cache helper 프로세스를 생성합니다.

2. 아키텍처가 중요한 이유는 무엇입니까?

Unix 애플리케이션의 기본 기반은 스레드 또는 프로세스입니다. (Linux OS 관점에서 스레드와 프로세스는 대부분 동일합니다. 주요 차이점은 메모리를 공유하는 정도입니다.) 스레드 또는 프로세스는 운영체제가 CPU에서 실행하도록 예약할 수 있는 자체 포함된 명령 집합입니다. 핵심. 대부분의 복잡한 애플리케이션은 두 가지 이유로 여러 스레드 또는 프로세스를 병렬로 실행합니다.

  • 동시에 더 많은 컴퓨팅 코어를 사용할 수 있습니다.
  • 스레드와 프로세스를 사용하면 작업을 병렬로 수행하기가 매우 쉽습니다(예: 동시에 여러 연결 처리).

프로세스와 스레드는 리소스를 소비합니다. 이들은 각각 메모리 및 기타 OS 리소스를 사용하며 코어에서 교체되어야 합니다(컨텍스트 전환이라고 하는 작업). 대부분의 최신 서버는 수백 개의 작은 활성 스레드 또는 프로세스를 동시에 처리할 수 있지만 메모리가 소진되거나 높은 I/O 로드로 인해 많은 양의 컨텍스트 전환이 발생하면 성능이 심각하게 저하됩니다.

네트워크 애플리케이션을 설계하는 일반적인 방법은 스레드 또는 프로세스를 각 연결에 할당하는 것입니다. 이 아키텍처는 간단하고 구현하기 쉽지만 애플리케이션이 수천 개의 동시 연결을 처리해야 할 때 확장되지 않습니다.

3. NGINX는 어떻게 작동합니까?

NGINX는 사용 가능한 하드웨어 리소스에 맞게 조정된 예측 가능한 프로세스 모델을 사용합니다.

  • Master 프로세스는 구성 읽기 및 포트 바인딩과 같은 권한 있는 작업을 수행한 다음 소수의 자식 프로세스(다음 세 가지 유형)를 만듭니다.
  • 캐시 로더(cache loader) 프로세스는 시작 시 실행되어 디스크 기반 캐시를 메모리에 로드한 다음 종료됩니다. 보수적으로 예약되므로 리소스 요구 사항이 낮습니다.
  • 캐시 관리자(cache manager) 프로세스는 주기적으로 실행되며 구성된 크기 내에서 항목을 유지하기 위해 디스크 캐시에서 항목을 정리합니다.
  • Worker 프로세스가 모든 작업을 수행합니다! 네트워크 연결을 처리하고, 디스크에서 콘텐츠를 읽고 쓰고, upstream 서버와 통신합니다.

대부분의 경우 권장되는 NGINX 구성(CPU 코어당 하나의 worker 프로세스 실행)은 하드웨어 리소스를 가장 효율적으로 사용합니다. worker_processes 지시문에서 auto 매개변수를 설정하여 구성합니다.

worker_processes auto;

NGINX 서버가 활성화되면 worker 프로세스만 사용 중입니다. 각 worker 프로세스는 비차단 방식으로 여러 연결을 처리하여 컨텍스트 스위치의 수를 줄입니다.

각 worker 프로세스는 단일 스레드이며 독립적으로 실행되어 새 연결을 잡고 처리합니다. 프로세스는 공유 캐시 데이터, 세션 지속성 데이터 및 기타 공유 리소스에 대한 공유 메모리를 사용하여 통신할 수 있습니다.

4. NGINX Worker 프로세스 내부

NGINX 작업자 프로세스는 웹 클라이언트의 요청을 처리하기 위한 비차단 이벤트 기반 엔진입니다.

각 NGINX worker 프로세스는 NGINX 구성으로 초기화되며 master 프로세스에서 Listen 소켓 세트와 함께 제공됩니다.

NGINX worker 프로세스는 Listen 소켓(accept_mutex 및 kernel socket sharding)에서 이벤트를 기다리는 것으로 시작됩니다. 이벤트는 새로 들어오는 연결에 의해 시작됩니다. 이러한 연결은 상태 시스템에 할당됩니다. HTTP 상태 시스템이 가장 일반적으로 사용되지만 NGINX는 stream(원시 TCP) 트래픽 및 여러 메일 프로토콜(SMTP, IMAP 및 POP3)에 대한 상태 시스템도 구현합니다.

들어오는 클라이언트 요청을 처리하기 위해 NGINX는 HTTP 헤더를 읽고, 구성된 경우 제한을 적용하고, 필요에 따라 내부 리디렉션 및 하위 요청을 만들고, 백엔드 서비스로 전달하고, 필터를 적용하고, 작업을 기록합니다.

상태 시스템은 기본적으로 NGINX에 요청 처리 방법을 알려주는 명령 집합입니다. NGINX와 동일한 기능을 수행하는 대부분의 웹 서버는 유사한 상태 시스템을 사용합니다. 차이점은 구현에 있습니다.

5. 상태 머신 스케줄링

체스의 규칙과 같은 상태 기계를 생각하십시오. 각 HTTP 트랜잭션은 체스 게임입니다. 체스판의 한쪽에는 매우 빠르게 결정을 내릴 수 있는 그랜드마스터인 웹 서버가 있습니다. 다른 쪽에는 상대적으로 느린 네트워크를 통해 사이트나 애플리케이션에 액세스하는 웹 브라우저인 원격 클라이언트가 있습니다.

그러나 게임의 규칙은 매우 복잡할 수 있습니다. 예를 들어, 웹 서버는 다른 당사자와 통신(Upstream 애플리케이션으로 프록시)하거나 인증 서버와 통신해야 할 수 있습니다. 웹 서버의 third-party 모듈은 게임 규칙을 확장할 수도 있습니다.

5-1. 블로킹 상태 머신

운영체제가 CPU 코어에서 실행하도록 예약할 수 있는 자체 포함된 명령 집합으로 프로세스 또는 스레드에 대한 설명을 상기하십시오. 대부분의 웹 서버와 웹 애플리케이션은 연결당 프로세스 또는 연결당 스레드 모델을 사용하여 체스 게임을 합니다. 각 프로세스 또는 스레드에는 하나의 게임을 끝까지 플레이하기 위한 지침이 포함되어 있습니다. 프로세스가 서버에 의해 실행되는 동안 대부분의 시간은 ‘차단(block)’되어 클라이언트가 다음 이동을 완료할 때까지 기다립니다.

대부분의 웹 애플리케이션 플랫폼은 차단 I/O를 사용합니다. 즉, 각 작업자(스레드 또는 프로세스)는 한 번에 하나의 활성 연결만 처리할 수 있습니다.
  1. 웹 서버 프로세스는 청취 소켓에서 새 연결(클라이언트가 시작한 새 게임)을 청취합니다.
  2. 새 게임을 받으면 해당 게임을 플레이하고 각 이동 후 차단하여 클라이언트의 응답을 기다립니다.
  3. 게임이 완료되면 웹 서버 프로세스는 클라이언트가 새 게임을 시작하려는지 확인하기 위해 기다릴 수 있습니다(이는 keepalive 연결에 해당함). 연결이 닫히면(클라이언트가 사라지거나 시간 초과가 발생하면) 웹 서버 프로세스는 새 게임을 수신하기 위해 돌아갑니다.

기억해야 할 중요한 점은 모든 활성 HTTP 연결(모든 체스 게임)에는 전용 프로세스 또는 스레드(그랜드마스터)가 필요하다는 것입니다. 이 아키텍처는 third-party 모듈(‘새로운 규칙’)로 간단하고 쉽게 확장할 수 있습니다. 그러나 엄청난 불균형이 있습니다. 파일 설명자와 소량의 메모리로 표시되는 다소 가벼운 HTTP 연결은 매우 무거운 운영체제 개체인 별도의 스레드 또는 프로세스에 매핑됩니다. 프로그래밍의 편리함은 있지만 엄청난 낭비입니다.

5-2. NGINX는 진정한 그랜드마스터입니다.

한 명의 체스 그랜드마스터가 동시에 수십 명의 상대와 플레이하는 Simultaneous exhibition 게임에 대해 들어 보셨습니까?

Kiril Georgiev

Kiril Georgiev는 불가리아 소피아에서 동시에 360명을 연기했습니다.

그의 최종 성적은 284승 70무 6패였다.

이것이 바로 NGINX worker 프로세스가 “체스”를 하는 방식입니다. 각 worker (CPU 코어당 일반적으로 한 명의 worker가 있음을 기억하십시오)는 수백(실제로는 수십만)의 게임을 동시에 플레이할 수 있는 그랜드마스터입니다.

NGINX는 비차단 I/O와 함께 이벤트 기반 아키텍처를 사용하므로 수십만 개의 동시 연결을 처리할 수 있습니다.
  1. Worker는 청취 및 연결 소켓에서 이벤트를 기다립니다.
  2. 소켓에서 이벤트가 발생하고 Worker 가 이를 처리합니다.
    • 청취 소켓의 이벤트는 클라이언트가 새로운 체스 게임을 시작했음을 의미합니다. worker 는 새 연결 소켓을 만듭니다.
    • 연결 소켓의 이벤트는 클라이언트가 새로운 이동을 했음을 의미합니다. worker 는 신속하게 응답합니다.

Worker 는 “상대방”(클라이언트)이 응답하기를 기다리면서 네트워크 트래픽을 차단하지 않습니다. 이동이 완료되면 worker 는 즉시 이동이 처리되기를 기다리는 다른 게임으로 이동하거나 새로운 플레이어를 환영합니다.

5-3. 차단 다중 프로세스 아키텍처보다 빠른 이유는 무엇입니까?

NGINX는 worker 프로세스당 수십만 개의 연결을 지원하도록 매우 잘 확장됩니다. 각각의 새 연결은 다른 파일 설명자를 만들고 worker 프로세스에서 소량의 추가 메모리를 사용합니다. 연결당 추가 오버헤드가 거의 없습니다. NGINX 프로세스는 CPU에 고정된 상태로 유지될 수 있습니다. 컨텍스트 전환은 상대적으로 드물며 수행할 작업이 없을 때 발생합니다.

블로킹 프로세스별 연결 접근 방식에서 각 연결에는 많은 양의 추가 리소스와 오버헤드가 필요하며 컨텍스트 전환(한 프로세스에서 다른 프로세스로 전환)이 매우 자주 발생합니다.

더 자세한 설명은 NGINX의 기업 개발 부사장 겸 공동 설립자인 Andrew Alexeev가 작성한 NGINX 아키텍처에 대한 기사를 확인하십시오.

적절한 시스템 튜닝을 통해 NGINX는 worker 프로세스당 수십만 개의 동시 HTTP 연결을 처리하도록 확장할 수 있으며 비트를 놓치지 않고 트래픽 급증(새로운 게임의 유입)을 흡수할 수 있습니다.

6. 구성 업데이트 및 NGINX 업그레이드

적은 수의 worker 프로세스가 있는 NGINX의 프로세스 아키텍처는 구성 및 심지어 NGINX 바이너리 자체를 매우 효율적으로 업데이트합니다.

NGINX는 다운타임(요청 처리 중단) 없이 구성을 다시 로드합니다.

NGINX 구성 업데이트는 매우 간단하고 가볍고 안정적인 작업입니다. 일반적으로 디스크의 구성을 확인하고 마스터 프로세스에 SIGHUP 신호를 보내는 nginx -s reload 명령을 실행하는 것을 의미합니다.

마스터 프로세스가 SIGHUP을 수신하면 다음 두 가지 작업을 수행합니다.

  1. 구성을 다시 로드하고 새로운 worker 프로세스 집합을 포크합니다. 이러한 새 worker 프로세스는 즉시 연결 수락 및 트래픽 처리를 시작합니다(새 구성 설정 사용).
  2. 이전 worker 프로세스에 정상적으로 종료하라는 신호를 보냅니다. worker 프로세스가 새 연결 수락을 중지합니다. 각각의 현재 HTTP 요청이 완료되는 즉시 worker 프로세스는 연결을 완전히 종료합니다(즉, 남아 있는 keepalive가 없음). 모든 연결이 닫히면 worker 프로세스가 종료됩니다.

이 다시 로드 프로세스로 인해 CPU 및 메모리 사용량이 약간 급증할 수 있지만 일반적으로 활성 연결의 리소스 로드와 비교할 때 감지할 수 없습니다. 초당 여러 번 구성을 다시 로드할 수 있습니다(그리고 많은 NGINX 사용자가 정확히 그렇게 합니다). 매우 드물게 연결이 닫히기를 기다리는 여러 세대의 NGINX worker 프로세스가 있을 때 문제가 발생하지만 이러한 문제도 신속하게 해결됩니다.

NGINX의 바이너리 업그레이드 프로세스는 고가용성의 성배를 달성합니다. 연결 끊김, 다운타임 또는 서비스 중단 없이 소프트웨어를 즉시 업그레이드할 수 있습니다.

NGINX는 중단 시간(요청 처리 중단) 없이 바이너리를 다시 로드합니다.

바이너리 업그레이드 프로세스는 구성의 단계적 재로드와 접근 방식이 유사합니다. 새로운 NGINX 마스터 프로세스는 원래 마스터 프로세스와 병렬로 실행되며 청취 소켓을 공유합니다. 두 프로세스가 모두 활성 상태이며 각각의 worker 프로세스가 트래픽을 처리합니다. 그런 다음 이전 마스터와 해당 worker에게 정상적으로 종료하라는 신호를 보낼 수 있습니다.

전체 프로세스는 NGINX 제어에 자세히 설명되어 있습니다.

7. 결론

Inside NGINX 인포그래픽은 NGINX가 어떻게 작동하는지에 대한 높은 수준의 개요를 제공하지만, 이 간단한 설명 뒤에는 NGINX가 다양한 하드웨어에서 가능한 최고의 성능을 제공하는 동시에 보안과 안정성을 유지하도록 하는 10년 이상의 혁신과 최적화가 있습니다. 최신 웹 애플리케이션에는 필요합니다.

NGINX에 대해 궁금한 내용이 있으시면 NGXINSTORE에 문의하십시오.

사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.