コンテナー内で Node.js をデバッグする
Node.js プロジェクトに Docker ファイルを追加すると、コンテナー内でそのアプリケーションをデバッグできるようにタスクと起動構成が追加されます。ただし、Node.js を取り巻くエコシステムが大きいため、これらのタスクではすべてのアプリケーション フレームワークまたはライブラリに対応できず、一部のアプリケーションでは追加の構成が必要になります。
コンテナーのエントリ ポイントを構成する
Container Tools 拡張機能は、package.json
のプロパティを介して、コンテナーのエントリ ポイント、つまりコンテナー内でデバッグ モードでアプリケーションを起動するためのコマンド ラインを推論します。拡張機能は、まず scripts
オブジェクトで start
スクリプトを探します。見つかった場合、そしてそれが node
または nodejs
コマンドで始まる場合、それを使用してデバッグ モードでアプリケーションを起動するためのコマンド ラインを構築します。見つからない場合、または認識された node
コマンドでない場合、package.json
の main
プロパティが使用されます。どちらも見つからないか認識されない場合、コンテナーを起動するために使用される docker-run
タスクの dockerRun.command
プロパティを明示的に設定する必要があります。
一部の Node.js アプリケーション フレームワークには、アプリケーションを管理するための CLI が含まれており、start
スクリプトでアプリケーションを起動するために使用され、基礎となる node
コマンドを隠蔽します。これらの場合、Container Tools 拡張機能は開始コマンドを推論できないため、開始コマンドを明示的に構成する必要があります。
例: Nest.js アプリケーションのエントリ ポイントを構成する
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"command": "nest start --debug 0.0.0.0:9229"
},
"node": {
"enableDebugging": true
}
}
]
}
例: Meteor アプリケーションのエントリ ポイントを構成する
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"command": "node --inspect=0.0.0.0:9229 main.js"
},
"node": {
"enableDebugging": true
}
}
]
}
ブラウザーをアプリケーションのエントリ ページに自動的に起動する
Container Tools 拡張機能は、デバッガーでアプリケーションが起動した後、ブラウザーをアプリケーションのエントリ ポイントに自動的に起動できます。この機能はデフォルトで有効になっており、launch.json
のデバッグ構成の dockerServerReadyAction
オブジェクトを介して構成されます。
この機能は、アプリケーションのいくつかの側面によって異なります。
- アプリケーションは、デバッグ コンソールにログを出力する必要があります。
- アプリケーションは、「サーバー準備完了」メッセージをログに記録する必要があります。
- アプリケーションは、ブラウザーで表示可能なページを提供する必要があります。
デフォルト設定は Express.js ベースのアプリケーションでは機能するかもしれませんが、他の Node.js フレームワークでは、これらの側面の1つ以上を明示的に構成する必要がある場合があります。
アプリケーションのログがデバッグ コンソールに書き込まれるようにする
この機能は、アプリケーションがログをアタッチされたデバッガーのデバッグ コンソールに書き込むことに依存します。ただし、すべてのロギング フレームワークがデバッグ コンソールに書き込むわけではありません。コンソールベースのロガーを使用するように構成されている場合でも (一部の「コンソール」ロガーは実際にはコンソールをバイパスして stdout
に直接書き込みます)。
解決策はロギング フレームワークによって異なりますが、通常は実際にコンソールに書き込むロガーを作成/追加する必要があります。
例: Express アプリケーションがデバッグ コンソールに書き込むように構成する
デフォルトでは、Express.js は debug ロギング モジュールを使用します。これはコンソールをバイパスできます。これは、ログ関数をコンソールの debug()
メソッドに明示的にバインドすることで解決できます。
var app = require('../app');
var debug = require('debug')('my-express-app:server');
var http = require('http');
// Force logging to the debug console.
debug.log = console.debug.bind(console);
また、debug
ロガーは、docker-run
タスクで設定できる DEBUG
環境変数によって有効になった場合にのみログを書き込むことに注意してください。(この環境変数は、Docker ファイルがアプリケーションに追加されると、デフォルトで *
に設定されます。)
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"env": {
"DEBUG": "*"
}
},
"node": {
"enableDebugging": true
}
}
]
}
アプリケーションが「準備完了」になるタイミングを構成する
拡張機能は、Express.js がデフォルトで行うように、アプリケーションがデバッグ コンソールに Listening on port <number>
の形式のメッセージを書き込むと、HTTP 接続を受信する「準備ができた」と判断します。アプリケーションが異なるメッセージをログに記録する場合は、デバッグ起動構成の dockerServerReadyAction オブジェクトの pattern
プロパティを、そのメッセージに一致する JavaScript 正規表現に設定する必要があります。正規表現には、アプリケーションがリッスンしているポートに対応するキャプチャ グループを含める必要があります。
たとえば、アプリケーションが次のメッセージをログに記録するとします。
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Application has started on ' + bind);
}
デバッグ起動構成 (launch.json
内) の対応する pattern
は次のとおりです。
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"pattern": "Application has started on port (\\d+)"
}
}
]
}
ポート番号の
(\\d+)
キャプチャ グループと、\d
文字クラスのバックスラッシュの JSON エスケープ文字としての\
の使用に注意してください。
アプリケーションのエントリ ページを構成する
デフォルトでは、Container Tools 拡張機能はブラウザーの「メイン」ページを開きます (アプリケーションによってどのように決定されるかによります)。ブラウザーを特定のページに開く必要がある場合は、デバッグ起動構成の dockerServerReadyAction オブジェクトの uriFormat
プロパティを Node.js 形式の文字列に設定する必要があります。これには、ポートを置換する場所を示す1つの文字列トークンが含まれます。
メイン ページの代わりに about.html
ページを開くためのデバッグ起動構成 (launch.json
内) の対応する uriFormat
は次のとおりです。
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"uriFormat": "https://:%s/about.html"
}
}
]
}
コンテナーのソース ファイルをローカル ワークスペースにマップする
デフォルトでは、Container Tools 拡張機能は、実行中のコンテナー内のアプリケーションのソース ファイルが /usr/src/app
フォルダーにあると仮定し、デバッガーはこれらのファイルをオープンされたワークスペースのルートにマッピングして、コンテナーから Visual Studio Code にブレークポイントを変換します。
アプリケーションのソース ファイルが別の場所にある場合 (たとえば、Node.js フレームワークが異なると異なる規則があるため)、コンテナー内または開かれたワークスペース内のどちらかに関わらず、デバッグ起動構成の node オブジェクトの localRoot
プロパティと remoteRoot
プロパティのいずれかまたは両方を、それぞれワークスペースとコンテナー内のルート ソースの場所に設定する必要があります。
たとえば、アプリケーションが代わりに /usr/my-custom-location
に存在する場合、対応する remoteRoot
プロパティは次のようになります。
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"node": {
"remoteRoot": "/usr/my-custom-location"
}
}
]
}
トラブルシューティング
node_modules がないためにコンテナー イメージのビルドまたは起動に失敗する
Dockerfile は、多くの場合、イメージのビルド時間、イメージ サイズ、またはその両方を最適化するように配置されています。ただし、すべての Node.js アプリケーション フレームワークがすべての一般的な Node.js Dockerfile 最適化をサポートしているわけではありません。特に、一部のフレームワークでは、node_modules
フォルダーがアプリケーションのルート フォルダーの直下のサブフォルダーである必要がありますが、Container Tools 拡張機能は、node_modules
フォルダーが親または祖先レベルに存在する Dockerfile をスキャフォールドします (これは通常、Node.js で許可されています)。
解決策は、Dockerfile
からその最適化を削除することです。
FROM node:lts-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
# Remove the `&& mv node_modules ../` from the RUN command:
# RUN npm install --production --silent && mv node_modules ../
RUN npm install --production --silent
COPY . .
EXPOSE 3000
RUN chown -R node /usr/src/app
USER node
CMD ["npm", "start"]