次の方法で共有


Node.js のベスト プラクティスとトラブルシューティング

この記事では、iisnode を使用して Azure App Service で実行されるNode.js アプリケーションのベスト プラクティスとトラブルシューティングについて説明します。

警告

運用サイトの問題のトラブルシューティングを行う場合は、注意が必要です。 ステージング スロットなど、非運用環境のセットアップでアプリのトラブルシューティングを行うことをお勧めします。 問題が修正されたら、ステージング スロットを運用スロットとスワップします。

iisnode の構成

このスキーマ ファイルは、iisnode 用に構成できるすべての設定を示しています。 アプリケーションに役立つ設定には、次のようなものがあります。

アプリケーションごとのノードプロセス数

この設定は、IIS アプリケーションごとに起動されるノード プロセスの数を制御します。 既定値は 1 です。 値を 0 に変更することで、仮想マシンの vCPU 数と同じ数の node.exe プロセスを起動できます。 マシン上のすべての vCPU を使用できるように、ほとんどのアプリケーションで推奨値は 0 です。 node.exe はシングルスレッドなので、1 つの node.exe で最大 1 vCPU を使用します。 ノード アプリケーションから最大のパフォーマンスを得るには、すべての vCPU を使用します。

nodeProcessCommandLine

この設定は、node.exe のパスを制御します。 この値を設定すると、node.exe のバージョンを指定することができます。

プロセスごとの最大同時リクエスト数 (maxConcurrentRequestsPerProcess)

この設定は、iisnode が各 node.exe に送信する同時要求の最大数を制御します。 Azure App Service では、既定値は Infinite です。 アプリケーションが受け取る要求の数と、アプリケーションが各要求を処理する速度に応じて、この値を構成できます。

maxNamedPipeConnectionRetry

この設定は、iisnode が node.exe に要求を送信する際に経由する名前付きパイプで接続の作成を再試行する最大回数を制御します。 この設定と namedPipeConnectionRetryDelay を併用することで、iisnode 内の各要求の合計タイムアウトを決定します。 Azure App Service の既定値は 200 です。 Total Timeout in seconds = (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000

namedPipeConnectionRetryDelay

この設定は、iisnode が名前付きパイプ経由で要求を node.exe に送信するまでの各再試行の間に待機する時間 (ミリ秒) を制御します。 既定値は 250 ミリ秒です。 Total Timeout in seconds = (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000

既定では、App Service の iisnode の合計タイムアウトは 200 * 250 ms = 50 seconds

logDirectory

この設定は、iisnode が stdout と stderr をログするディレクトリを制御します。 既定値は iisnode です。これはメイン スクリプト ディレクトリ (メイン server.js が存在する場所) を基準としています。

debuggerExtensionDll

この設定では、iisnode がノード アプリケーションのデバッグ時に使用する node-inspector のバージョンを制御します。 現時点 では、iisnode-inspector-0.7.3.dlliisnode-inspector.dll は、この設定の唯一の 2 つの有効な値です。 既定値は iisnode-inspector-0.7.3.dllです。 iisnode-inspector-0.7.3.dll バージョンでは node-inspector-0.7.3 と Web ソケットを使用します。 このバージョンを使用するには、Azure Web アプリの Web ソケットを有効にします。

flushResponse

既定では、IIS は、フラッシュする前、または応答の最後のいずれか早い方まで、応答データを最大 4 MB までバッファーします。 iisnode には、この動作をオーバーライドする構成設定が用意されています。iisnode が応答エンティティ本体のフラグメントを node.exeから受信するとすぐにフラッシュするには、iisnode/@flushResponse属性を true に設定する必要があります。

<configuration>
    <system.webServer>
        <!-- ... -->
        <iisnode flushResponse="true" />
    </system.webServer>
</configuration>

応答エンティティ本体のすべてのフラグメントのフラッシュを有効にすると、パフォーマンス オーバーヘッドが増加し、システムのスループットが約 5% (v0.1.13 時点) 削減されます。 この設定の範囲は、web.config 内の <location> 要素を使用するなど、応答ストリーミングを必要とするエンドポイントのみに限定するのが最善です。

さらに、ストリーミング アプリケーションの場合は、iisnode ハンドラーの responseBufferLimit0 に設定する必要があります。

<handlers>
    <add name="iisnode" path="app.js" verb="\*" modules="iisnode" responseBufferLimit="0"/>
</handlers>

watchedFiles

変更を監視する対象となるファイルの一覧です (セミコロン区切り)。 ファイルを変更すると、アプリケーションのリサイクルが発生します。 各エントリは、オプションのディレクトリ名と必要なファイル名で構成されます。必須のファイル名は、メイン アプリケーション エントリ ポイントが配置されているディレクトリを基準とします。 ワイルドカードは、ファイル名部分のみに使用できます。 既定値は *.js;iisnode.yml です。

リサイクル信号有効

既定値は false です。 有効にした場合、ノード アプリケーションは名前付きパイプ (環境変数 IISNODE_CONTROL_PIPE) に接続し、 リサイクル メッセージを送信できます。 これにより、w3wp は適切にリサイクルされます。

idlePageOutTimePeriod

既定値は 0 です。これは、この機能が無効になっていることを意味します。 0 より大きい値に設定すると、iisnode はすべての子プロセスを idlePageOutTimePeriod ミリ秒ごとにページアウトします。 ページアウトの意味を理解するには、EmptyWorkingSet 関数を参照してください。 大量のメモリを消費するため、たまにメモリをディスクにページアウトして RAM の一部を解放する必要があるアプリケーションには、この設定が役立ちます。

警告

運用アプリケーションで以下の構成設定を有効にする場合は、注意が必要です。 稼働中の運用アプリケーションでは、これらの設定を有効にしないことをお勧めします。

デバッグヘッダー有効化

既定値は false です。 true に設定すると、iisnode は、iisnode-debugヘッダー値が URL であるすべての HTTP 応答に HTTP 応答ヘッダー iisnode-debugを追加します。 診断情報の各部分は URL フラグメントを調べることで取得できますが、ブラウザーで URL を開いて視覚化できます。

loggingEnabled

この設定は、iisnode による stdout と stderr のログを制御します。 iisnode は、起動したノード プロセスから stdout/stderr をキャプチャし、 logDirectory 設定で指定されたディレクトリに書き込みます。 これが有効になると、アプリケーションはファイル システムにログを書き込みます。アプリケーションによるログ記録の量によっては、パフォーマンスに影響が出る場合があります。

devErrorsEnabled

既定値は false です。 true に設定すると、iisnode はブラウザーに HTTP 状態コードと Win32 エラー コードを表示します。 Win32 コードは、特定の種類の問題のデバッグに役立ちます。

デバッグ有効

ライブ運用サイトでは有効にしないでください。

この設定は、デバッグ機能を制御します。 iisnode は node-inspector と統合されています。 この設定を有効にすることで、node アプリケーションのデバッグが有効になります。 この設定を有効にすると、iisnode は、ノード アプリケーションへの最初のデバッグ要求で debuggerVirtualDir ディレクトリに node-inspector ファイルを作成します。 http://yoursite/server.js/debug に要求を送信することによって、node-inspector を読み込むことができます。 debuggerPathSegment設定を使用して、デバッグ URL セグメントを制御できます。 既定では、debuggerPathSegment='debug'。 たとえば、 debuggerPathSegment を GUID に設定して、他のユーザーが検出するのが難しくなることがあります。

シナリオと推奨事項/トラブルシューティング

node アプリケーションによる発信呼び出しが多すぎる

多くのアプリケーションでは、通常の操作の一環として送信接続を行いたいと考えています。 たとえば、要求が送信されると、ノード アプリは他の場所の REST API に接続し、要求を処理するための情報を取得したいと考えています。 HTTP または HTTPS 呼び出しを行うときは、キープアライブ エージェントを使用する必要があります。 これらの発信呼び出しを行うときに、キープ アライブ エージェントとして agentkeepalive モジュールを使用できます。

agentkeepalive モジュールを使用すると、ソケットが Azure Web アプリ VM で再利用されます。 送信要求のたびに新しいソケットを作成すると、アプリケーションにオーバーヘッドが追加されます。 アプリケーションでソケットを再利用することで、アプリケーションが VM ごとに割り当てられている maxSockets を超えないことが保証されます。 Azure App Service の推奨事項は、agentKeepAlive maxSockets 値を合計の (four instances of node.exe * 32 maxSockets/instance) 128 sockets per VMに設定することです。

agentKeepALive 構成の例:

let keepaliveAgent = new Agent({
    maxSockets: 32,
    maxFreeSockets: 10,
    timeout: 60000,
    freeSocketTimeout: 300000
});

重要

この例では、VM で 4 つの node.exe インスタンスが実行されていることを前提としています。 数値が異なる場合は、それに応じて maxSocket の設定を変更する必要があります。

ノード アプリケーションが CPU を消費しすぎている

ポータルで、高い CPU 使用率に関する推奨事項を Azure App Service から受け取る場合があります。 特定のメトリックを監視するためにモニターをセットアップすることもできます。 Azure portal ダッシュボードで CPU 使用率を確認するときは、CPU の MAX 値を確認して、ピーク値を見逃さないようにします。 アプリケーションの CPU 消費が多すぎて理由を説明できない場合は、ノード アプリケーションをプロファイリングして調べることができます。

ノード アプリケーションで消費されるメモリが多すぎる

アプリケーションのメモリ消費が多すぎる場合は、Azure App Service から、高いメモリ消費量に関する通知がポータルに表示されます。 特定のメトリックを監視するようにモニターを設定できます。 Azure portal ダッシュボードでメモリ使用量を確認するときは、ピーク値を見逃さないように、メモリの MAX 値を確認してください。

Node.js のリーク検出とヒープ比較

node-memwatch を使うと、メモリ リークの特定に役立ちます。 v8-profiler と同じように memwatch をインストールし、ヒープのキャプチャと比較を行い、アプリケーションのメモリ リークを特定するようにコードを編集することができます。

node.exe インスタンスがランダムに強制終了される

node.exe がランダムにシャットダウンされる理由がいくつかあります。

  • アプリケーションが未処理のエクセプションをスローしています。 d:\home\LogFiles\Application\logging-errors.txt ファイル内のスローされた例外の詳細を確認してください。 このファイルには、アプリケーションのデバッグと修正に役立つスタック トレースが含まれています。
  • アプリケーションが消費するメモリが多すぎるため、開始後の他のプロセスに影響します。 VM メモリの合計が 100%に近い場合、node.exe インスタンスはプロセス マネージャーによって強制終了される可能性があります。 プロセス マネージャは、他のプロセスに作業を行う機会を与えるために、いくつかのプロセスを強制終了します。 この問題を修正するには、アプリケーションをプロファイルしてメモリ リークを解決します。 アプリケーションが大量のメモリを必要とする場合は、大規模な VM にスケール アップします (VM が利用できる RAM が増えます)。

ノード アプリケーションが起動しない

アプリケーションの起動時に 500 エラーが返される場合は、いくつかの理由が考えられます。

  • Node.exe が正しい場所にありません。 nodeProcessCommandLine設定を確認します。
  • メイン スクリプト ファイルが正しい場所にありません。 web.config 確認し、handlers セクションのメイン スクリプト ファイルの名前がメイン スクリプト ファイルと一致することを確認します。
  • Web.config 構成が正しくありません。 設定の名前と値を確認します。
  • コールド スタート: アプリケーションの起動に時間がかかりすぎます。 アプリケーションに (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1,000 secondsより長い時間がかかる場合、iisnode は 500 エラーを返します。 iisnode がタイムアウトして 500 エラーを返すことを防ぐために、アプリケーションの開始時間と一致するようにこれらの設定の値を大きくしてください。

node アプリケーションがクラッシュした

アプリケーションが未処理の例外をスローしています。 d:\home\LogFiles\Application\logging-errors.txt を確認して、スローされた例外の詳細を確認してください。 このファイルには、アプリケーションの診断と修正に役立つスタック トレースが含まれています。

ノード アプリケーションの起動に時間がかかりすぎる (コールド スタート)

アプリケーションの起動時間が長くなる一般的な原因は、node_modules 内に多数のファイルがあることです。 アプリケーションは起動時にこれらのファイルのほとんどを読み込もうとします。 既定では、ファイルは Azure App Service 上のネットワーク共有に存在するため、多くのファイルの読み込みには時間がかかります。 このプロセスを高速化するための解決策は、次のようにいくつかあります。

  • アプリケーション起動時にすべてのモジュールを読み込まずに、node_modules の遅延読み込みを試します。 遅延読み込みモジュールの場合、モジュール コードの最初の実行前に関数内でモジュールが実際に必要な場合は、require('module') の呼び出しを行う必要があります。
  • Azure App Service には、ローカル キャッシュと呼ばれる機能が用意されています。 この機能は、コンテンツをネットワーク共有から VM 上のローカル ディスクにコピーします。 ファイルはローカルにあるため、node_modules の読み込み時間は短縮されます。

iisnode HTTP 状態とサブステータス

cnodeconstants ソース ファイルには、エラーが原因で iisnode から返される可能性のあるすべての状態または副状態の組み合わせが一覧表示されます。

アプリケーションの FREB を有効にして、Win32 エラー コードを表示します。 パフォーマンス上の理由から、非運用サイトでのみ FREB を有効にしてください。

HTTP の状態 HTTP サブステータス 考えられる理由
500 1000 iisnode への要求のディスパッチに問題が発生しました。 node.exe が開始されたかどうかを確認します。 起動時に Node.exe がクラッシュした可能性があります。 web.config 構成でエラーを確認します。
500 1001 - Win32Error 0x2: アプリが URL に応答していません。 URL 書き換えルールか、express アプリで正しいルートが定義されているかどうかを確認してください。
- Win32Error 0x6d: 名前付きパイプがビジーです。 パイプがビジー状態であるため、Node.exe は要求を受け入れていません。 高 CPU 使用率を確認してください。
- その他のエラー: node.exe がクラッシュしたかどうかを確認します。
500 1002 Node.exe クラッシュしました。 d:\home\LogFiles\logging-errors.txt をチェックしてスタックトレースを確認します。
500 1003 パイプ構成の問題。 名前付きパイプの構成が正しくありません。
500 1004 ~ 1018 要求の送信中、または node.exe との間の応答の処理中に、エラーが発生しました。 node.exe がクラッシュしたかどうかを確認してください。 d:\home\LogFiles\logging-errors.txt を確認してスタック トレースを確認します。
503 1000 メモリ不足のため、名前付きパイプ接続をこれ以上割り当てられません。 アプリで大量のメモリが使用されている理由を確認してください。 設定値 maxConcurrentRequestsPerProcess 確認してください。 この値が無制限になっておらず、要求が多数ある場合は、エラーを防ぐために、この値を大きくします。
503 1001 アプリケーションがリサイクルされているため、要求を node.exe にディスパッチできませんでした。 アプリケーションがリサイクルされた後は、要求を通常どおりに処理する必要があります。
503 1002 実際の理由で Win32 エラー コードを確認します。 要求を node.exeにディスパッチできませんでした。
503 1003 名前付きパイプがビジー状態です。 node.exe が過剰な CPU を消費しているかどうかを確認します。

Node.exe には、 NODE_PENDING_PIPE_INSTANCESと呼ばれる設定があります。 Azure App Service では、この値は 5000 に設定されています。つまり、node.exe は名前付きパイプで一度に 5,000 件の要求を受け入れることが可能です。 Azure App Service で実行されるほとんどの node アプリケーションでは、この値で十分です。 NODE_PENDING_PIPE_INSTANCESの値が高いため、Azure App Service に 503.1003 は表示されません。