블로그 | NGINX

Node.js 애플리케이션을 위한 5가지 성능 팁

NGINX-F5-수평-검정-유형-RGB의 일부
플로이드 스미스 썸네일
플로이드 스미스
2015년 11월 16일 게시
#nginx가 노드 서버 앞에 없다면 아마도 잘못된 작업을 하고 있을 것입니다.
– 트위터의 Bryan Hughes

Node.js는 세계에서 가장 인기 있는 프로그래밍 언어인 JavaScript로 서버 애플리케이션을 만드는 데 사용되는 최고의 도구입니다. 웹 서버와 애플리케이션 서버의 기능을 모두 제공하는 Node.js는 이제 모든 종류의 마이크로서비스 기반 개발과 제공을 위한 핵심 도구로 간주됩니다. ( Node.js와 NGINX에 관한 무료 Forrester 보고서를 다운로드하세요.)

Node.js는 백엔드 애플리케이션 개발을 위해 Java나 .NET을 대체하거나 보완할 수 있습니다.

Node.js는 단일 스레드 방식으로 비차단 I/O를 사용하므로 확장이 가능하고 수만 개의 동시 작업을 지원할 수 있습니다. NGINX와 이러한 아키텍처적 특성을 공유하며 10,000개 이상의 동시 연결을 지원하는 C10K 문제를 해결합니다. NGINX는 이 문제를 해결하기 위해 발명 되었습니다. Node.js는 높은 성능과 개발자 생산성으로 잘 알려져 있습니다.

그러면 무슨 문제가 생길 수 있을까요?

Node.js에는 Node.js 기반 시스템의 성능이 저하되거나 심지어 작동이 중단될 수 있는 몇 가지 약점과 취약성이 있습니다. Node.js 기반 웹 애플리케이션의 트래픽이 급격히 증가하면 문제가 더 자주 발생합니다.

또한, Node.js는 웹 페이지의 핵심적이고 다양한 콘텐츠를 생성하는 애플리케이션 로직을 만들고 실행하는 데 유용한 도구입니다. 하지만 정적 콘텐츠(예: 이미지 및 JavaScript 파일)를 제공하거나 여러 서버에 걸쳐 부하를 분산하는 데는 그다지 적합하지 않습니다.

Node.js를 최대한 활용하려면 정적 콘텐츠를 캐시하고, 여러 애플리케이션 서버 간에 프록시 및 로드 밸런싱을 수행하고, 클라이언트, Node.js, 헬퍼(Socket.IO를 실행하는 서버 등) 간의 포트 경합을 관리해야 합니다. NGINX는 이러한 모든 목적에 사용할 수 있으므로 Node.js 성능 튜닝을 위한 훌륭한 도구입니다.

Node.js 애플리케이션 성능을 개선하려면 다음 팁을 활용하세요.

  1. 역방향 프록시 서버 구현
  2. 정적 파일 캐시
  3. 여러 서버에 걸쳐 트래픽 로드 밸런싱
  4. 프록시 WebSocket 연결
  5. SSL/TLS 및 HTTP/2 구현

메모: Node.js 애플리케이션 성능을 빠르게 개선하려면 Node.js 구성을 수정하여 최신 멀티코어 서버를 활용해야 합니다. Node.js 설명서를 확인하여 Node.js가 웹 서버의 CPU 수와 동일한 수의 별도의 자식 프로세스를 생성하는 방법을 알아보세요. 그러면 각 프로세스가 마법처럼 단 하나의 CPU에만 집중되어 성능이 크게 향상됩니다.

팁 1 – 역방향 프록시 서버 구현

NGINX, Inc.에서는 고성능 사이트의 핵심으로 사용되는 애플리케이션 서버가 인터넷 트래픽에 직접 노출되는 것을 볼 때마다 항상 약간씩 충격을 받습니다. 여기에는 WordPress 기반 사이트는 물론 Node.js 사이트도 많이 포함됩니다.

Node.js는 대부분 애플리케이션 서버보다 확장성을 염두에 두고 설계되었으며, 웹 서버 측은 많은 양의 인터넷 트래픽을 상당히 잘 처리할 수 있습니다. 하지만 웹 서비스는 Node.js의 존재 이유가 아닙니다. 실제로 Node.js는 이런 목적으로 만들어지지 않았습니다.

트래픽이 많은 사이트가 있는 경우 애플리케이션 성능을 높이기 위한 첫 번째 단계는 Node.js 서버 앞에 역방향 프록시 서버를 배치하는 것입니다. 이렇게 하면 Node.js 서버가 인터넷 트래픽에 직접 노출되는 것을 방지하고 여러 애플리케이션 서버를 사용하고, 서버 간 부하를 분산하고, 콘텐츠를 캐싱하는 데 있어 매우 유연하게 작업할 수 있습니다.

기존 서버 설정 앞에 NGINX를 역방향 프록시 서버 로 배치한 다음 추가 용도로 사용하는 것은 NGINX의 핵심 사용 사례로, 전 세계 수천만 개의 웹사이트 에 구현되어 있습니다.

NGINX를 Node.js 역방향 프록시 서버로 사용하는 데는 다음과 같은 특정 이점이 있습니다.

  • 권한 처리 및 포트 할당 간소화
  • 정적 이미지를 보다 효율적으로 제공하기( 다음 팁 참조)
  • Node.js 충돌을 성공적으로 관리하기
  • DoS 공격 완화

메모 : 이 튜토리얼에서는 Ubuntu 14.04 또는 CentOS 환경에서 NGINX를 역방향 프록시 서버로 사용하는 방법을 설명하며, Node.js 앞에 NGINX를 배치하는 모든 사람에게 유용한 개요입니다.

팁 2 – 정적 파일 캐시

Node.js 기반 사이트 사용이 늘어나면 서버에 부담이 생기기 시작합니다. 이 시점에서 하려는 일은 두 가지입니다.

  1. Node.js 서버를 최대한 활용하세요
  2. 애플리케이션 서버를 쉽게 추가하고 서버 간 부하 분산을 수행할 수 있습니다.

사실 이건 하기 쉽습니다. 이전 팁 에서 설명한 대로 NGINX를 역방향 프록시 서버로 구현하는 것으로 시작합니다. 이를 통해 캐싱, 부하 분산(여러 개의 Node.js 서버가 있는 경우) 등을 쉽게 구현할 수 있습니다.

애플리케이션 컨테이너 플랫폼인 Modulus의 웹사이트에는 NGINX로 Node.js 애플리케이션 성능을 극대화하는 방법에 대한 유용한 기사가 있습니다. Node.js가 모든 작업을 스스로 수행하면서 저자의 사이트는 초당 평균 약 900개의 요청을 처리할 수 있었습니다. NGINX를 역방향 프록시 서버로 사용하여 정적 콘텐츠를 제공한 결과, 같은 사이트는 초당 1600개 이상의 요청을 처리했습니다. 이는 성능이 거의 2배 향상되었음을 의미합니다.

성능이 두 배로 향상되면 사이트 디자인을 검토하고(가능하면 개선하고), 애플리케이션 코드를 최적화하고, 추가 애플리케이션 서버를 배포하는 등 추가적인 성장을 수용하기 위한 추가 조치를 취할 시간을 확보할 수 있습니다.

다음은 Modulus에서 실행되는 웹사이트에 적용되는 구성 코드입니다.

서버 { listen 80;
서버 이름 static-test-47242.onmodulus.net;

루트 /mnt/app;
인덱스 index.html index.htm;

위치 /static/ {
try_files $uri $uri/ =404;
}

위치 /api/ {
proxy_pass http://node-test-45750.onmodulus.net;
}
}

NGINX, Inc.의 Patrick Nommensen이 쓴 이 자세한 문서에서는 Node.js 애플리케이션인 Ghost 오픈 소스 블로깅 플랫폼에서 실행되는 개인 블로그에서 정적 콘텐츠를 캐시하는 방법을 설명합니다. 일부 세부 사항은 Ghost에만 국한되지만 대부분의 코드를 다른 Node.js 애플리케이션에 재사용할 수 있습니다.

예를 들어, NGINX 위치 블록에서 일부 콘텐츠를 캐시에서 제외하려고 할 수 있습니다. 예를 들어, 블로깅 플랫폼의 관리 인터페이스는 일반적으로 캐시하고 싶지 않습니다. Ghost 관리 인터페이스의 캐싱을 비활성화(또는 제외)하는 구성 코드는 다음과 같습니다.

위치 ~ ^/(?:ghost|signout) { proxy_set_header X-Real-IP $remote_addr;
proxy_set_header 호스트 $http_host;
proxy_pass http://ghost_upstream;
add_header 캐시 제어 "캐시 없음, 비공개, 저장 없음,
반드시 재검증해야 함, 최대 오래된 것=0, 사후 검사=0, 사전 검사=0";
}

정적 콘텐츠 제공에 대한 일반 정보는 NGINX Plus 관리자 가이드를 참조하세요. 관리자 가이드에는 구성 지침, 파일 찾기 시도가 성공하거나 실패했을 때 대응하는 여러 옵션, 더욱 빠른 성능을 달성하기 위한 최적화 방법이 포함되어 있습니다.

NGINX 서버에서 정적 파일을 캐싱하면 Node.js 애플리케이션 서버의 작업 부하가 크게 줄어들어 훨씬 더 높은 성능을 달성할 수 있습니다.

팁 3 – Node.js 로드 밸런서 구현

Node.js 애플리케이션의 높은 성능(거의 무한한 성능)을 구현하는 진짜 핵심은 여러 애플리케이션 서버를 실행하고 모든 서버에서 부하를 분산하는 것입니다.

Node.js 로드 밸런싱은 특히 까다로울 수 있습니다. Node.js는 웹 브라우저에서 실행되는 JavaScript 코드와 Node.js 애플리케이션 서버에서 실행되는 JavaScript 코드 간에 JSON 객체를 데이터 교환 매체로 사용하여 높은 수준의 상호작용을 가능하게 하기 때문입니다. 이는 주어진 클라이언트 세션이 특정 애플리케이션 서버에서 지속적으로 실행된다는 것을 의미하며, 여러 애플리케이션 서버로는 세션 지속성을 달성하기가 본질적으로 어렵습니다.

인터넷과 웹의 가장 큰 장점 중 하나는 높은 수준의 무국적성으로, 요청된 파일에 액세스할 수 있는 모든 서버에서 클라이언트 요청을 처리할 수 있다는 것입니다. Node.js는 무상태성을 뒤집고, 동일한 서버가 모든 특정 클라이언트의 요청에 일관되게 응답하는 상태 저장 환경에서 가장 잘 작동합니다.

이런 요구 사항은 NGINX Open Source보다는 NGINX Plus 를 통해 가장 잘 충족할 수 있습니다. NGINX의 두 버전은 매우 유사하지만, 가장 큰 차이점 중 하나는 다양한 부하 분산 알고리즘을 지원한다는 것입니다.

NGINX는 상태 비저장 부하 분산 방법을 지원합니다.

  • 라운드 로빈 – 새로운 요청은 목록의 다음 서버로 전송됩니다.
  • 최소 연결 – 새로운 요청은 활성 연결이 가장 적은 서버로 전송됩니다.
  • IP 해시 – 새로운 요청은 클라이언트의 IP 주소 해시에 할당된 서버로 전송됩니다.

이러한 방법 중 IP 해시만이 주어진 클라이언트의 요청을 동일한 서버로 안정적으로 전송하며, 이는 Node.js 애플리케이션에 도움이 됩니다. 그러나 IP 해시는 이 블로그 게시물 에서 설명한 대로 부하 분산 기술에 관한 내용으로, 한 서버가 다른 서버를 희생하고 불균형적인 수의 요청을 수신하게 되는 결과를 쉽게 초래할 수 있습니다. 이 방법은 서버 리소스 전체에 걸쳐 요청이 최적이 아닌 수준으로 할당될 가능성을 희생하고 상태 저장 기능을 지원합니다.

NGINX와 달리 NGINX Plus는 세션 지속성을 지원합니다. 세션 지속성을 사용하면 동일한 서버가 주어진 클라이언트의 모든 요청을 안정적으로 수신합니다. 클라이언트와 서버 간의 상태 기반 통신을 제공하는 Node.js와 고급 로드 밸런싱 기능을 제공하는 NGINX Plus의 장점이 모두 극대화됩니다.

따라서 NGINX 또는 NGINX Plus를 사용하여 여러 Node.js 서버에서 부하 분산을 지원할 수 있습니다. 그러나 NGINX Plus를 사용하면 최대 부하 분산 성능과 Node.js에 친화적인 상태 저장 기능을 모두 얻을 수 있습니다. NGINX Plus에 내장된 애플리케이션 상태 점검모니터링 기능 도 여기에서 유용합니다.

NGINX Plus는 세션 드레이닝 도 지원합니다. 세션 드레이닝을 사용하면 애플리케이션 서버가 서비스를 중단하라는 요청 이후에도 현재 세션을 정상적으로 완료할 수 있습니다.

팁 4 – 프록시 WebSocket 연결

모든 버전의 HTTP는 클라이언트가 서버에 파일을 요청하는 "풀" 통신을 위해 설계되었습니다. WebSocket은 클라이언트가 요청하지 않은 파일을 서버가 사전에 보낼 수 있는 "푸시" 및 "푸시/풀" 통신을 가능하게 하는 도구입니다.

WebSocket 프로토콜을 사용하면 데이터 전송량을 줄이고 대기 시간을 최소화하는 동시에 클라이언트와 서버 간의 보다 강력한 상호작용을 지원하기가 더 쉬워집니다. 필요한 경우, 클라이언트와 서버가 모두 필요에 따라 요청을 시작하고 수신하는 풀 듀플렉스 연결을 구현할 수 있습니다.

WebSocket 프로토콜은 강력한 JavaScript 인터페이스를 갖추고 있어 애플리케이션 서버로 Node.js에 자연스럽게 적합하며, 거래량이 적당한 웹 애플리케이션의 경우 웹 서버로도 적합합니다. 거래량이 늘어나면 클라이언트와 Node.js 웹 서버 사이에 NGINX를 삽입하고, NGINX 또는 NGINX Plus를 사용하여 정적 파일을 캐시 하고 여러 애플리케이션 서버 간의 부하를 분산 하는 것이 합리적입니다.

Node.js는 종종 Socket.IO와 함께 사용됩니다. Socket.IO는 Node.js 애플리케이션과 함께 사용하기에 꽤 인기를 얻고 있는 WebSocket API입니다. 이로 인해 포트 80(HTTP의 경우) 또는 포트 443(HTTPS의 경우)이 매우 혼잡해질 수 있으며, 이를 해결하려면 Socket.IO 서버로 프록시를 연결해야 합니다. 위에서 설명한 대로 NGINX를 프록시 서버로 사용하고, 정적 파일 캐싱, 부하 분산 등의 추가 기능도 얻을 수 있습니다.

다음은 포트 5000에서 수신하는 server.js 노드 애플리케이션 파일에 대한 코드입니다. 프록시 서버(웹 서버가 아님) 역할을 하며 요청을 적절한 포트로 라우팅합니다.

var io = require('socket.io').listen(5000); 
io.sockets.on('connection', function (socket) {
socket.on('닉네임 설정', function (name) {
socket.set('nickname', name, function () {
socket.emit('ready');
});
});

socket.on('msg', function () {
socket.get('nickname', function (err, name) {
console.log('채팅 메시지 ', name);
});
});
});

index.html 파일에서 다음 코드를 추가하여 서버 애플리케이션에 연결하고 애플리케이션과 사용자 브라우저 사이에 WebSocket을 인스턴스화합니다.

<script src="/socket.io/socket.io.js"></script><script>// <![CDATA[
var socket = io(); // 여기에 초기화 코드를 넣으세요.
// ]]>
</script>

NGINX 구성을 포함한 전체 지침은 Node.js와 Socket.IO와 함께 NGINX와 NGINX Plus를 사용하는 방법에 대한 블로그 게시물을 참조하세요. 이런 종류의 웹 애플리케이션의 잠재적인 아키텍처 및 인프라 문제에 대해 더 자세히 알아보려면 실시간 웹 애플리케이션 과 WebSocket에 대한 블로그 게시물을 참조하세요.

팁 5 – SSL/TLS 및 HTTP/2 구현

점점 더 많은 사이트가 SSL/TLS를 사용하여 사이트에서의 모든 사용자 상호작용을 보안하고 있습니다. 이러한 이동을 할지 여부와 시기는 귀하의 결정이지만, 귀하가 이동을 할 경우 NGINX는 두 가지 방법으로 전환을 지원합니다.

  1. NGINX를 역방향 프록시로 설정하면 NGINX에서 클라이언트에 대한 SSL/TLS 연결을 종료할 수 있습니다. Node.js 서버는 NGINX 역방향 프록시 서버를 통해 암호화되지 않은 요청과 콘텐츠를 송수신합니다.
  2. 초기 징후에 따르면, HTTP 프로토콜의 새로운 버전인 HTTP/2를 사용하면 SSL/TLS를 사용함으로써 발생하는 성능 저하를 대부분 또는 완전히 상쇄할 수 있을 것으로 보입니다. NGINX는 HTTP/2를 지원하며 SSL/TLS와 함께 HTTP/2를 종료할 수 있어 Node.js 애플리케이션 서버를 변경할 필요가 없습니다.

구현 단계에는 Node.js 구성 파일에서 URL을 업데이트하고, NGINX 구성에서 보안 연결을 설정하고 최적화하고, 필요한 경우 SPDY 또는 HTTP/2를 사용하는 것이 포함됩니다. HTTP/2 지원을 추가하면 HTTP/2를 지원하는 브라우저 버전은 새로운 프로토콜을 사용하여 애플리케이션과 통신합니다. 이전 브라우저 버전은 계속 HTTP/1.x를 사용합니다.

다음 구성 코드는 여기 에서 설명한 대로 SPDY를 사용하는 Ghost 블로그를 위한 것입니다. 여기에는 OCSP 스테이플링과 같은 고급 기능이 포함되어 있습니다. OCSP 스테이플링 옵션을 포함하여 SSL 종료를 위해 NGINX를 사용하는 것과 관련된 고려 사항은 여기를 참조하세요. 동일한 주제에 대한 일반적인 개요는 여기를 참조하세요.

지금 또는 2016년 초에 SPDY 지원이 중단되면 Node.js 애플리케이션을 구성하고 SPDY에서 HTTP/2로 업그레이드하기 위해 사소한 변경만 하면 됩니다.

서버 { server_name domain.com;
listen 443 ssl spdy;
spdy_headers_comp 6;
spdy_keepalive_timeout 300;
keepalive_timeout 300;
ssl_certificate_key /etc/nginx/ssl/domain.key;
ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_session_cache 공유:SSL:10m; 
ssl_session_timeout 24h; 
ssl_buffer_size 1400; 
ssl_stapling 켜짐;
ssl_stapling_verify 켜짐;
ssl_trusted_certificate /etc/nginx/ssl/trust.crt;
리졸버 8.8.8.8 8.8.4.4 유효=300초;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
add_header X-Cache $upstream_cache_status;

location / {
proxy_cache STATIC;
proxy_cache_valid 200 30m;
proxy_cache_valid 404 1m;
proxy_pass http://ghost_upstream;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_hide_header X-powered-by;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header 호스트 $http_host;
만료 10m;
}

위치 /content/images {
별칭 /path/to/ghost/content/images;
액세스 로그 끄기;
만료 최대;
}

위치 /assets {
별칭 /path/to/ghost/themes/uno-master/assets;
액세스 로그 끄기;
만료 최대;
}

위치 /public {
별칭 /path/to/ghost/built/public;
액세스 로그 끄기;
만료 최대;
}

위치 /ghost/scripts {
별칭 /path/to/ghost/core/built/scripts;
액세스 로그 끄기;
만료 최대;
}

location ~ ^/(?:ghost|signout) { 
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header 호스트 $http_host;
proxy_pass http://ghost_upstream;
add_header 캐시 제어 "캐시 없음, 비공개, 저장 없음,
반드시 재검증해야 함, 최대 부실=0, 사후 검사=0, 사전 검사=0";
proxy_set_header X-Forwarded-Proto https;
}
}

결론

이 블로그 게시물에서는 Node.js 애플리케이션에서 구현할 수 있는 가장 중요한 성능 개선 사항 중 일부를 설명합니다. 이 튜토리얼은 Node.js와 함께 NGINX를 애플리케이션 믹스에 추가하는 데 중점을 두고 있습니다. 즉, NGINX를 역방향 프록시 서버로 사용하여 정적 파일을 캐시하고, 부하를 분산하고, WebSocket 연결을 프록시하고, SSL/TLS 및 HTTP/2 프로토콜을 종료합니다.

NGINX와 Node.js를 결합하면 새로운 마이크로서비스 친화적인 애플리케이션을 만들거나 Java나 Microsoft .NET을 사용하는 기존 SOA 기반 애플리케이션에 유연성과 기능을 추가할 수 있는 방법으로 널리 인정받고 있습니다 . 이 게시물은 Node.js 애플리케이션을 최적화하는 데 도움이 되며, 원하는 경우 Node.js와 NGINX 간의 파트너십을 실현하는 데도 도움이 됩니다.


"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."