概要

Node.jsの記事 Good practices for high-performance and scalable Node.js applications [Part 1/3] を翻訳する。
例のごとくgoogle翻訳 & 意訳


Good practices for high-performance and scalable Node.js applications [Part 1/3]

この3回の記事では、Node.js Webバックエンドアプリケーションの開発に関するいくつかの優れた方法について説明します。

このシリーズはNodeについてのチュートリアルではありませんが、読んだことはNode.jsの基本を既に熟知している開発者を対象としており、アーキテクチャの改善に関するヒントを探しています。

主な焦点は、より少ないリソースで最高の結果を得るために、効率とパフォーマンスです。

Webアプリケーションのスループットを向上させる1つの方法は、Webアプリケーションの規模を拡大し、複数のインスタンス間で着信接続のバランシングを複数回インスタンス化することです。この最初の記事では、Node.jsアプリケーションを複数のコアまたは複数のマシンで水平にスケーリングする方法について説明します。

スケールアップするときは、状態から認証まで、アプリケーションのさまざまな側面に注意する必要があります。したがって、2番目の記事では、Node.jsアプリケーションを拡大する際に考慮する必要がある事項について説明します。

優先度キューの採用、cronプロセスのような定期的なジョブの管理など、N個のプロセス/マシンまでスケールアップするときにN回実行することは意図されないものを扱うことのできるグッドプラクティスを3つ目の記事で扱います。

Chapter 1 - Node.jsアプリケーションの水平スケーリング(Horizontally scaling a Node.js application)

水平スケーリングは、アプリケーションインスタンスを複製して、より多くの着信接続を管理することです。このアクションは、単一のマルチコアマシンで実行することも、異なるマシン間で実行することもできます。

垂直スケーリングは単一のマシン性能を向上させることであり、コード側では特別な作業は必要ありません。

同じマシン上の複数のプロセス(Multiple processes on same machine)

アプリケーションのスループットを向上させる一般的な方法の1つは、マシンのコアごとに1つのプロセスを生成することです。このようにして、すでに効率的なNode.jsの要求の「並行性」管理を掛け合わせて並列化することができます。(see “event driven, non-blocking I/O”)

コアの数よりも多くのプロセスを生成することはおそらく賢明ではありません。なぜなら、低いレベルでは、OSはこれらのプロセス間のCPU時間のバランスをとる可能性が高いからです。

単一のマシンでスケーリングを行うためのさまざまな戦略がありますが、共通の概念は、同じポート上で複数のプロセスを実行し、すべてのプロセス/コアにわたって着信接続を分散するために使用される内部ロード・バランシングです。

以下で説明する戦略は、標準Node.jsクラスタモードと自動高レベルPM2クラスタ機能です。

ネイティブクラスタモード(Native cluster mode)

ネイティブのNode.jsクラスターモジュールは、単一のマシン上でノードアプリケーションをスケールする基本的な方法です。https://Node.js.org/api/cluster.html
プロセスの1つのインスタンス(「マスター」と呼ばれます)は、アプリケーションを実行するコアプロセスごとに1つずつ、他の子プロセス(「ワーカー」と呼ばれます)を生成する役割を果たします。着信接続は、同じポート上でサービスを公開するすべてのワーカーにラウンドロビン戦略に従って配布されます。

このアプローチの主な欠点は、手作業でプロセスの数を簡単に変更することなく、通常は古典的なif-elseブロックを使用して、マスタとワーカープロセスの違いをコード内で手動で管理する必要があることです。

次の例は、公式ドキュメントから抜粋したものです。

const cluster = require(‘cluster’);  
const http = require(‘http’);  
const numCPUs = require(‘os’).cpus().length;  

if (cluster.isMaster) {  

 console.log(`Master ${process.pid} is running`);  

 // Fork workers.  
 for (let i = 0; i < numCPUs; i++) {  
  cluster.fork();  
 }  

 cluster.on(‘exit’, (worker, code, signal) => {  
  console.log(`worker ${worker.process.pid} died`);  
 });  

} else {  

 // Workers can share any TCP connection  
 // In this case it is an HTTP server  
 http.createServer((req, res) => {  
  res.writeHead(200);  
  res.end(‘hello world\n’);  
 }).listen(8000);  

 console.log(`Worker ${process.pid} started`);  

}  

PM2クラスタモード(PM2 Cluster mode)

PM2をプロセスマネージャーとして使用している場合(私がお勧めします)、クラスタモジュールを気にせずにすべてのコアでプロセスを拡張できるマジッククラスタ機能があります。 PM2デーモンは "マスタ"プロセスの役割をカバーし、アプリケーションのN個のプロセスをラウンドロビンバランシングを使用してワーカーとして生成します。

このようにすれば、シングルコアでの使用と同じようにアプリケーションを記述するだけで済みます(次の記事で説明する注意事項があります).PM2はマルチコアの部分を気にします。

アプリケーションをクラスタモードで起動すると、「pm2スケール」を使用してオンザフライでインスタンスの数を調整し、「0秒のダウンタイム」リロードを実行できます。リロードは、プロセスが常に再開され、オンラインで少なくとも1つのプロセス。

プロセス管理者として、PM2は、実稼働環境でノードを実行する際に考慮する必要がある他の多くの便利な機能と同様に、プロセスが再起動した場合にも処理を再開します。

さらに拡張する必要がある場合は、おそらくさらに多くのマシンを展開する必要があります。

ネットワーク負荷分散を備えた複数のマシン(Multiple machines with network load balancing)

複数のマシン間でのスケーリングの主な概念は、複数のコアでのスケーリングと似ています。複数のマシン、それぞれが1つ以上のプロセスを実行するマシン、および各マシンにトラフィックをリダイレクトするバランサがあります。

リクエストが特定のノードに送信されると、前のパラグラフで説明した内部バランサはトラフィックを特定のプロセスに送信します。

ネットワークバランサは、さまざまな方法で展開できます。AWSを使用してインフラストラクチャをプロビジョニングする場合は、ELB(Elastic Load Balancer)などの管理されたロードバランサを使用することをお勧めします。これは自動スケーリングなどの便利な機能をサポートしているためです。

しかし、あなたが昔ながらの方法でやりたければ、マシンを配備し、NGINXを使ってバランサを自分でセットアップすることができます。アップストリームを指すリバースプロキシの設定は、このジョブでは非常に簡単です。設定の例を以下に示します。

http {  

 upstream myapp1 {  
   server srv1.example.com;  
   server srv2.example.com;  
   server srv3.example.com;  
 }  

 server {  
   listen 80;  
   location / {  
    proxy_pass http://myapp1;  
   }  
 }  

}  

このようにして、ロードバランサは、外界に公開されたアプリケーションの唯一のエントリポイントになります。インフラストラクチャの単一障害点であることを心配する場合は、同じサーバーを指す複数のロードバランサを展開できます。

バランサ間でトラフィックを分散させるために(各自が独自のIPアドレスを使用します)あなたのメインドメインに複数のDNS "A"レコードを追加できるので、DNSリゾルバはバランサ間でトラフィックを分散し、毎回異なるIPアドレスに解決します。

Next steps

ここでは、インフラストラクチャから可能な限り最高のパフォーマンスを得るためにNode.jsアプリケーションをさまざまなレベルでどのようにスケールするかを見てきましたが、シングルノードからマルチノードおよびマルチバランサに移行する際には注意が必要です。マルチプロセス環境でアプリケーションを使用するには、準備が整っていなければなりません。そうしないと、いくつかの問題や望ましくない動作が発生します。次の記事では、アプリケーションを大規模にするための準備について説明します。ここで見つけることができます。