コンテナー内での 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 プロパティを、ポートが置換される場所を示す1つの文字列トークンを持つ Node.js フォーマット文字列に設定する必要があります。
メインページの代わりに 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"]