概要
この記事では、Azure App Service で実行されている Windows Node.js アプリケーション (iisnode を使用) のベスト プラクティスとトラブルシューティングの手順について説明します。
Warnung
運用サイトでトラブルシューティング手順を使用する場合は、注意が必要です。 非運用セットアップ (ステージング領域など) でアプリのトラブルシューティングを行います。 問題を解決したら、ステージング領域を運用環境とスワップします。
IISNODE の構成
このスキーマ ファイルは、iisnode 用に構成できるすべての設定を示しています。 一部の設定は、アプリケーションに役立ちます。
アプリケーションごとのノードプロセス数
この設定は、IIS アプリケーションごとに起動するノード プロセスの数を制御します。 既定値は 1 です。
0に値を変更することで、仮想マシンの vCPU 数と同じ数の node.exe プロセスを起動できます。 推奨される値は、使用可能なすべての vCPU を使用できるように、多くのアプリケーションに対して 0 です。
Node.js イベント ループは 1 つのスレッドで実行されますが、アプリでワーカー スレッド、クラスター モジュール、ネイティブ アドオン、または並列で動作するその他のライブラリを使用する場合、1 つの Node.js プロセスで複数の CPU コアを使用できます。 CPU にバインドされたワークロードの場合は、複数の Node.js プロセス (VM vCPU 数まで) を実行することでスループットが高くなる可能性があるため、IIS はプロセス間で要求を分散できます。
nodeProcessCommandLine
この設定は、node.exe のパスを制御します。 この値を、使用している node.exe のバージョンを指定するように設定します。
プロセスごとの最大同時リクエスト数 (maxConcurrentRequestsPerProcess)
この設定は、iisnode が各 node.exeに送信する同時要求の最大数を制御します。 Azure App Service では、既定値は無制限です。 アプリケーションが受信する要求の数と、アプリケーションが各要求を処理する速度に応じて値を構成します。
maxNamedPipeConnectionRetry
この設定は、iisnode が node.exe に要求を送信する際に経由する名前付きパイプで接続の作成を再試行する最大回数を制御します。 この設定と namedPipeConnectionRetryDelay を併用することで、iisnode 内の各要求の合計タイムアウトを決定します。 Azure App Service では、既定値は 200 です。 合計タイムアウト(秒)= (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000
namedPipeConnectionRetryDelay
この設定は、iisnode が名前付きパイプ経由で node.exe に要求を送信する場合に、各再試行間で待機する時間 (ミリ秒) を制御します。 既定値は 250 ミリ秒です。 合計タイムアウト(秒)= (maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000
既定では、Azure App Service での iisnode の合計タイムアウトは、200 * 250 ms = 50 秒です。
logDirectory
この設定は、iisnode が stdout と stderr をログするディレクトリを制御します。 既定値は、メイン スクリプト ディレクトリ (メインの server.js が存在するディレクトリ) に対して相対的な iisnode です。
debuggerExtensionDll
この設定は、node アプリケーションのデバッグ時に iisnode が node-inspector のどのバージョンを使用するかを制御します。 現在、この設定に有効な値は、iisnode-inspector-0.7.3.dll と iisnode-inspector.dll の 2 つだけです。 既定値は、iisnode-inspector-0.7.3.dll です。 iisnode-inspector-0.7.3.dll バージョンでは node-inspector-0.7.3 と Web ソケットを使用します。 このバージョンを使用するには、Azure WebApp で Web ソケットを有効にする必要があります。
flushResponse
IIS の既定の動作として、応答データは、フラッシュするまでか、応答が終了するまでのどちらか早い方のタイミングで、最大 4 MB がバッファーに格納されます。 iisnode には、この動作をオーバーライドする構成設定があります。iisnode が node.exe から応答エンティティを受け取ったらすぐにその本体のフラグメントをフラッシュするには、web.config で iisnode/@flushResponse 属性を true に設定する必要があります。
<configuration>
<system.webServer>
<!-- ... -->
<iisnode flushResponse="true" />
</system.webServer>
</configuration>
応答エンティティ本体のすべてのフラグメントのフラッシュを有効にすると、パフォーマンスのオーバーヘッドが生じ、システムのスループットが約5%減少します (v0.1.13 時点)。 最善は、この設定のスコープを応答のストリーミングが必要なエンドポイントのみにすることです (たとえば web.config で<location> 要素を使用します)。
さらに、ストリーミング アプリケーションの場合は、iisnode ハンドラーの responseBufferLimit も 0 に設定する必要があります。
<handlers>
<add name="iisnode" path="app.js" verb="\*" modules="iisnode" responseBufferLimit="0"/>
</handlers>
watchedFiles
変更を監視するファイルのセミコロン区切りのリスト。 ファイルを変更すると、アプリケーションのリサイクルが発生します。 各エントリは、オプションのディレクトリ名と必要なファイル名で構成されます。両方とも、メイン アプリケーション エントリ ポイントがあるディレクトリを基準とします。 ファイル名の部分でのみワイルドカードを使用します。 既定値は *.js;iisnode.yml です。
リサイクル信号有効
既定値は false です。 有効にすると、Node.js アプリケーションは名前付きパイプ (環境変数 IISNODE_CONTROL_PIPE) に接続し、"リサイクル" メッセージを送信できます。 このメッセージにより、 w3wp プロセスが正常にリサイクルされます。
アイドルページアウト時間期間
既定値は 0 で、この機能が無効であることを意味します。 0 より大きい値に設定すると、すべての子プロセスiisnodeidlePageOutTimePeriodミリ秒ごとにページアウトされます。 ページアウトの意味については、ドキュメントを参照してください。 大量のメモリを消費するため、たまにメモリをディスクにページアウトして RAM の一部を解放する必要があるアプリケーションには、この設定が役立ちます。
Warnung
運用アプリケーションで以下の構成設定を有効にする場合は、注意が必要です。 ライブ運用アプリケーションでは有効にしないでください。
デバッグヘッダー有効化
既定値は false です。 true に設定すると、 iisnode は送信するすべての HTTP 応答に HTTP 応答ヘッダー iisnode-debug を追加します。
iisnode-debug ヘッダー値は URL です。 URL フラグメントを調べることで、個々の診断情報を取得できます。 視覚化を表示するには、ブラウザーで URL を開きます。
ロギング有効化
この設定では、iisnodeによるstdoutとstderrのログ記録を制御します。
iisnode は、Node.jsから stdout と stderr をキャプチャし、それを処理して起動し、 logDirectory 設定で指定されたディレクトリに書き込みます。 この設定を有効にすると、アプリケーションはファイル システムにログを書き込みます。 アプリケーションによって実行されるログの量によっては、この設定がパフォーマンスに影響する可能性があります。
devErrorsEnabled
既定値は false です。 true に設定すると、 iisnode ブラウザーに HTTP 状態コードと Win32 エラー コードが表示されます。 Win32 コードは、特定の種類の問題をデバッグするのに役立ちます。
debuggingEnabled (実稼働の運用サイトでは有効にしないでください)
この設定は、デバッグ機能を制御します。
iisnode は node-inspector と統合されます。 この設定を有効にすると、Node.js アプリケーションのデバッグが有効になります。
デバッグの詳細については、Windows での Node.js アプリケーションのデバッグに関するページを参照してください。
Node.js トラブルシューティングのシナリオと推奨事項
Node.js アプリケーションが過剰な送信呼び出しを行っている
多くのアプリケーションは、通常の操作の一環として送信接続を行います。 たとえば、要求が送信されると、Node.js アプリが REST API に連絡して、要求を処理するための情報を取得する場合があります。 HTTP または HTTPS 呼び出しを行うときは、キープアライブ エージェントを使用します。 これらの送信呼び出しを行うときは、 agentkeepalive モジュールをキープアライブ エージェントとして使用できます。
agentkeepalive モジュールは、Azure の Web WebApp VM でソケットが再利用されることを保証します。 送信要求のたびに新しいソケットを作成すると、アプリケーションにオーバーヘッドが追加されます。 アプリケーションが外部要求に対してソケットを再利用することにより、VMごとに割り当てられたmaxSocketsを超えないようにすることができます。 Azure App Service では、agentKeepAlive maxSockets 値を VM あたり合計 128 ソケット (node.exe の 4 つのインスタンス * インスタンスあたり 32 の maxSockets) に設定することが推奨されています。
agentKeepALive 構成の例:
let keepaliveAgent = new Agent({
maxSockets: 32,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 300000
});
Important
この例では、VM で 4 つの node.exe が実行されていることを前提としています。 VM で実行されている node.exe の数が異なる場合は、それに応じて maxSockets 設定を変更します。
Node.js アプリケーションが CPU を消費しすぎている
ポータルで、高い CPU 使用率に関する推奨事項を Azure App Service から受け取る場合があります。 特定のメトリックを監視するためにモニターをセットアップすることもできます。 Azure ポータル ダッシュボードで CPU 使用率を確認する場合は、CPU の MAX 値をチェックして、ピーク値を見逃さないようにします。 アプリケーションによる CPU の消費量が明らかに多すぎるが、理由を説明できない場合は、node アプリケーションをプロファイリングして調べることができます。
V8-Profiler を使用した Azure App Service の node アプリケーションのプロファイリング
たとえば、次のように「Hello World」アプリをプロファイリングしようとした場合です。
const http = require('http');
function WriteConsoleLog() {
for(let i=0;i<99999;++i) {
console.log('hello world');
}
}
function HandleRequest() {
WriteConsoleLog();
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
HandleRequest();
res.end('Hello world!');
}).listen(process.env.PORT);
アプリのデバッグ コンソール サイトに移動するには、 開発ツール>[Advanced Tools]、[ Go] の順に選択します。 Kudu で、CMD または PowerShell のデバッグ コンソールを選択します。
site/wwwroot ディレクトリに移動します。 次の図のようなコマンド プロンプトが表示されます。
コマンド npm install v8-profiler を実行します。
このコマンドは、node_modules ディレクトリとその依存関係すべてに v8-profiler をインストールします。
このコマンドは、すべての依存関係と共に、node_modules ディレクトリの下にv8-profilerをインストールします。
const http = require('http');
const profiler = require('v8-profiler');
const fs = require('fs');
function WriteConsoleLog() {
for(let i=0;i<99999;++i) {
console.log('hello world');
}
}
function HandleRequest() {
profiler.startProfiling('HandleRequest');
WriteConsoleLog();
fs.writeFileSync('profile.cpuprofile', JSON.stringify(profiler.stopProfiling('HandleRequest')));
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
HandleRequest();
res.end('Hello world!');
}).listen(process.env.PORT);
上記のコードは WriteConsoleLog 関数をプロファイルし、プロファイル出力をサイト wwwroot の下の 'profile.cpuprofile' ファイルに書き込みます。 アプリケーションに要求を送信します。 サイトの wwwroot 配下に 'profile.cpuprofile' ファイルが作成されているのを確認できます。
このファイルをダウンロードし、Chrome の F12 ツールで開きます。 Chrome で F12 キーを押し、[ プロファイル ] タブを選択します。[ 読み込み ] ボタンを選択します。 ダウンロードした profile.cpuprofile ファイルを選択します。 読み込んだプロファイルを選択します。
WriteConsoleLog関数によって時間の 95% が消費されたことがわかります。 出力には、問題の原因となる正確な行番号とソース ファイルも表示されます。
Node.js アプリケーションがメモリを消費しすぎている
アプリケーションのメモリ消費が多すぎる場合は、Azure App Service から、高いメモリ消費量に関する通知がポータルに表示されます。 特定のメトリックを監視するようにモニターを設定できます。 Azure ポータルダッシュボードでメモリ使用量を確認するときは、ピーク値を見逃さないように、メモリの最大値を確認します。
Node.js のリーク検出とヒープ比較
node-memwatch を使用して、メモリ リークを特定します。
v8 プロファイラーと同様に memwatch をインストールし、コードを編集してヒープをキャプチャおよび差分化し、アプリケーションのメモリ リークを識別します。
Node.js プロセスが予期せず停止する
node.exe がランダムにシャットダウンされる理由がいくつかあります。
- アプリケーションで未処理の例外がスローされます – スローされた例外の詳細については、
d:\home\LogFiles\Application\logging-errors.txtファイルを確認してください。 このファイルには、アプリケーションのデバッグと修正に役立つスタック トレースが含まれています。 - アプリケーションが消費するメモリが多すぎるため、開始後の他のプロセスに影響します。 VM メモリの合計が 100%に近い場合、プロセス マネージャーによって node.exe プロセスが強制終了される可能性があります。 プロセス マネージャーは、他のプロセスが何らかの作業を行う機会を得るために、いくつかのプロセスを強制終了します。 この問題を修正するには、アプリケーションをプロファイルしてメモリ リークを解決します。 アプリケーションが大量のメモリを必要とする場合は、大規模な VM にスケール アップします (VM が利用できる RAM が増えます)。
Node.js アプリケーションが起動しない
アプリケーションが起動時に 500 エラーを返す場合は、次のような原因が考えられます。
- Node.exe が正しい場所にありません。
nodeProcessCommandLine設定を確認します。 - メイン スクリプト ファイルが正しい場所に存在しません。
web.config確認し、handlers セクションのメイン スクリプト ファイルの名前がメイン スクリプト ファイルと一致することを確認します。 -
web.config構成が正しくありません。設定の名前と値を確認してください。 - アプリケーションの起動に時間がかかりすぎます。 アプリケーションに
(maxNamedPipeConnectionRetry * namedPipeConnectionRetryDelay) / 1000秒より長い時間がかかる場合、iisnodeは 500 エラーを返します。iisnodeがタイムアウトして 500 エラーが返されないように、これらの設定の値をアプリケーションの開始時刻に合わせて増やします。
Node.js アプリケーションがクラッシュしました
アプリケーションで未処理の例外が発生しています – スローされた例外の詳細については、d:\home\LogFiles\Application\logging-errors.txt ファイルを確認してください。 このファイルには、アプリケーションの診断と修正に役立つスタック トレースが含まれています。
Node.js アプリケーションの起動に時間がかかりすぎる (コールド スタート)
アプリケーションの起動時間が長くなる一般的な原因は、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 では名前付きパイプを使用して同時に 5000 個の要求を受け入れることができます。 Azure App Service で実行されるほとんどの node アプリケーションでは、この値で十分です。
NODE_PENDING_PIPE_INSTANCES には大きい値が設定されているため、Azure App Service に 503.1003 が表示されることはありません。
関連コンテンツ
Azure App Service での Node.js アプリケーションの詳細については、以下のリンクを参照してください。
- Get started with Node.js web apps in Azure App Service (Azure App Service で Node.js Web アプリの使用を開始する)
- Azure App Service で Node.js Web アプリをデバッグする方法
- Azure アプリケーションでの Node.js モジュールの使用
- Azure App Service Web Apps:Node.js
- Node.js デベロッパー センター
- スーパーシークレット Kudu デバッグ コンソールを探索する
サードパーティのお問い合わせ窓口に関する免責事項
マイクロソフトは、このトピックに関する追加情報を見つける手助けとして、サードパーティの連絡先情報を提供します。 この連絡先情報は予告なしに変更されることがあります。 マイクロソフトは、第三者の連絡先情報の正確性を保証しません。