Dockerコンテナ内Node.jsアプリケーションをデバッグする課題
Dockerコンテナ内で実行されているNode.jsアプリケーションのデバッグは、開発環境でよく直面する課題です。コンテナ化された環境では、従来のローカル開発時のデバッグ手法がそのまま使えないケースが多いため、効率的な開発に支障をきたすことがあります。
具体的な課題として以下のようなものがあります:
- コンテナ内で発生したエラーの詳細な原因追求が難しい
- 変数の中身やコールスタックをリアルタイムで確認できない
console.logによる原始的なデバッグに頼らざるを得ない- ブレークポイントを設置して実行フローを一時停止できない
特に複雑なバックエンド処理やAPI開発では、処理の途中経過を詳細に調査する必要があるケースが多いです。この記事では、ChromeDevToolsを使用してDockerコンテナ内のNode.jsアプリケーションをリモートでデバッグする方法を紹介します。
ChromeDevToolsとNode.jsのリモートデバッグの基本
Node.jsには、ビルトインでリモートデバッグを可能にする機能があります。--inspectまたは--inspect-brkフラグを使うことで、WebSocketプロトコルを通じて外部のデバッガからアプリケーションに接続できるようになります。
主なデバッグフラグ:
--inspect: デバッグポートを開き、アプリケーションを実行します--inspect-brk: デバッグポートを開き、最初の行で実行を一時停止します(初回接続時に便利)--inspect=[host:port]: 特定のホストとポートでデバッグポートを開きます
ChromeDevToolsは、この接続を使用してNode.jsアプリケーションとやり取りします。これにより以下のことが可能になります:
- ブレークポイントの設定と実行の一時停止
- 変数の値をリアルタイムで確認
- コールスタックのトレース
- メモリの使用状況の監視
- コンソールからコマンドの実行
Dockerコンテナ内でNode.jsアプリケーションをデバッグするためには、このデバッグポート(デフォルトは9229)をコンテナの外部に公開する必要があります。
// デバッグ可能なNode.jsアプリケーションの例
const express = require('express');
const app = express();
// ブレークポイントを設定できる場所
app.get('/', (req, res) => {
const message = 'Hello World'; // この変数の値を確認できます
console.log('Request received');
res.send(message);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});このコードをデバッグするには、node --inspect=0.0.0.0:9229 index.jsのように実行します。0.0.0.0を指定することで、あらゆるIPアドレスからの接続を許可します。これはDockerコンテナ内で実行する場合に重要な設定です。
Dockerfileとdocker-compose.ymlの設定例
Dockerコンテナ内でNode.jsアプリケーションをデバッグするためには、Dockerfileとdocker-compose.ymlファイルを適切に設定する必要があります。以下に実用的な例を示します。
Dockerfileの例:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# デバッグモードで実行するためのコマンド
# 本番環境では変更することを忘れずに
CMD ["node", "--inspect=0.0.0.0:9229", "index.js"]
# または、起動時に自動的に一時停止する場合
# CMD ["node", "--inspect-brk=0.0.0.0:9229", "index.js"]重要なポイントは、--inspect=0.0.0.0:9229フラグを使用していることです。0.0.0.0は、あらゆるインターフェースからの接続を許可するために必要です。
docker-compose.ymlの例:
version: '3'
services:
app:
build: .
ports:
- "3000:3000" # アプリケーションポート
- "9229:9229" # デバッグポート
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
# 以下はnpmスクリプトでデバッグを開始する場合の例
# command: npm run debugこの設定では、2つの重要なポートマッピングがあります:
3000:3000- アプリケーションのHTTPポート9229:9229- Node.jsデバッグポート
package.jsonのスクリプト例:
{
"scripts": {
"start": "node index.js",
"debug": "node --inspect=0.0.0.0:9229 index.js",
"debug-brk": "node --inspect-brk=0.0.0.0:9229 index.js"
}
}これにより、npm run debugコマンドでデバッグ可能なモードでアプリケーションを起動できます。または、最初の行で実行を一時停止する場合はnpm run debug-brkを使用します。
デバッグポートの公開と接続方法
デバッグポートを公開した後、ChromeDevToolsを使ってコンテナ内のNode.jsアプリケーションに接続する方法を説明します。
1. コンテナの起動
まず、設定したDockerコンテナを起動します:
# docker-compose.ymlがある場合
docker-compose up
# または個別のDockerコンテナを起動する場合
docker run -p 3000:3000 -p 9229:9229 your-node-appコンテナが起動すると、Node.jsアプリケーションがデバッグモードで実行され、以下のようなメッセージが表示されます:
Debugger listening on ws://0.0.0.0:9229/xxxxxxxxxxxx
For help, see: https://nodejs.org/en/docs/inspector2. Chrome DevToolsでの接続
ChromeブラウザでNode.jsアプリケーションにデバッグ接続する方法はいくつかあります:
方法1: Chrome拡張機能を使用
Node.js V8 Inspector Managerなどの拡張機能を使用すると、デバッグ可能なNode.jsプロセスにワンクリックで接続できます。
方法2: DevTools専用URL
Chromeのアドレスバーに以下のURLを入力:
chrome://inspect「Discover network targets」をクリックし、「Configure...」ボタンからリモートターゲットを追加します:
localhost:9229「Done」をクリックして、「Remote Target」セクションに表示されるNode.jsアプリケーションの「inspect」リンクをクリックします。
方法3: コマンドラインから直接開く
# macOSの場合
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --no-first-run --no-default-browser-check https://localhost:9229/json3. 接続トラブルシューティング
接続できない場合は、以下を確認してください:
- コンテナが正常に起動しているか
- デバッグポート(9229)が正しくマッピングされているか
- ファイアウォールでポートがブロックされていないか
- Node.jsアプリケーションが
--inspect=0.0.0.0:9229パラメータで起動されているか
以下のコマンドでポートがリッスンされているか確認できます:
# Dockerコンテナ内のプロセスを確認
docker exec -it <container_name> ps aux | grep node
# ポートが開いているか確認
docker exec -it <container_name> netstat -tulpn | grep 9229ポートが開いているにもかかわらず接続できない場合は、ネットワーク設定を確認するか、コンテナを再起動してみてください。
ブレークポイントの設定と変数調査のテクニック
ChromeDevToolsに接続できたら、効果的にデバッグを行うための方法を見ていきましょう。
ブレークポイントの設定方法
ソースパネルでコードを特定: ChromeDevToolsの「Sources」タブで、デバッグしたいファイルを選択します。
ブレークポイントを設定: 調査したい行の行番号をクリックしてブレークポイントを設定します。青いマーカーが表示されます。
条件付きブレークポイント: 行番号を右クリックして「Add conditional breakpoint」を選択すると、特定の条件が満たされた場合にのみ実行が停止します。
// 例: userIdが特定の値の場合のみ停止 userId === '12345'
変数とスコープの調査
実行が一時停止したら、以下の方法で変数を調査できます:
Scopesパネル: 現在のスコープで利用可能な変数とその値がすべて表示されます。
Watchパネル: 特定の変数やエクスプレッションの値を継続的に観察できます。「+」ボタンをクリックして監視したい式を追加します。
// 例: 配列の長さを監視 users.length // オブジェクトの特定のプロパティを監視 request.headers.authorizationコンソールでの評価: 一時停止中にコンソールパネルで変数や式を評価できます。
// 例: 現在のスコープにあるusersオブジェクトを調査 > users > users.filter(u => u.isActive)
実行フローの制御
デバッグ中にコードの実行フローを制御するためのボタンがあります:
- Resume (F8): 次のブレークポイントまで実行を再開します
- Step Over (F10): 現在の行を実行し、次の行で停止します(関数呼び出しにはステップインしません)
- Step Into (F11): 関数呼び出しにステップインします
- Step Out (Shift+F11): 現在の関数から抜けます
- Deactivate breakpoints: 一時的にすべてのブレークポイントを無効化します
高度なテクニック
Blackboxing: Node.jsのコアモジュールや依存ライブラリをブラックボックス化して、自分のコードのみにフォーカスします。
設定方法: DevToolsの設定(F1) > Blackboxing > パターンを追加(例:
/node_modules/)ログポイント: コードを修正せずに「console.log」を挿入したのと同じ効果が得られます。行番号を右クリックして「Add logpoint」を選択します。
// 例: リクエストの内容をログ出力 Request received: {req.url}非同期コードのデバッグ: 「Async」チェックボックスをオンにすると、Promiseや非同期関数の実行フローを追跡できます。
DOM変更のブレークポイント: フロントエンドのデバッグで特定のDOM要素が変更されたときに一時停止する機能です。
これらのテクニックを駆使することで、複雑なコードの挙動を効率的に調査できます。特にDockerコンテナ内で実行されているコードは直接アクセスしにくいため、リモートデバッグの能力を最大限に活用することが重要です。
実践的なトラブルシューティングと解決例
最後に、Dockerコンテナ内のNode.jsアプリケーションデバッグで遭遇する可能性のある具体的な問題と、その解決方法を紹介します。
ケース1: デバッグポートに接続できない
問題: chrome://inspectでターゲットが表示されない。
解決策:
コンテナの実行コマンドを確認:
docker ps -aコンテナ内のNode.jsプロセスを確認:
docker exec -it <container_name> ps aux | grep node正しいパラメータ(
--inspect=0.0.0.0:9229)で実行されているか確認します。ポートマッピングを確認:
docker port <container_name>9229ポートが正しくマッピングされているか確認します。
ネットワーク設定の修正:
# docker-compose.ymlの修正例 services: app: ports: - "127.0.0.1:9229:9229" # 特定のインターフェースにバインド
ケース2: TypeScriptやトランスパイルされたコードのデバッグ
問題: ブレークポイントが機能しない、またはソースマップが正しく読み込まれない。
解決策:
ソースマップを有効にする:
// tsconfig.jsonの設定 { "compilerOptions": { "sourceMap": true, "inlineSources": true } }Node.jsでソースマップを有効にする:
node --inspect=0.0.0.0:9229 --require source-map-support/register dist/index.jsプロジェクトにソースマップサポートを追加:
npm install --save-dev source-map-support
ケース3: nodemonとデバッグの連携
問題: ホットリロード中にデバッグ接続が切断される。
解決策:
package.jsonのスクリプトを修正:
{ "scripts": { "debug": "nodemon --inspect=0.0.0.0:9229 index.js" } }nodemonの設定ファイルを作成:
// nodemon.json { "restartable": "rs", "ignore": [".git", "node_modules/**/node_modules"], "verbose": true, "execMap": { "js": "node --inspect=0.0.0.0:9229" }, "events": { "restart": "osascript -e 'display notification \"App restarted due to changes\" with title \"nodemon\"'" }, "watch": ["src/"], "ext": "js,json" }
ケース4: メモリリークの診断
問題: コンテナがメモリを消費し続け、最終的にクラッシュする。
解決策:
Memory Profileを取得:
ChromeDevToolsの「Memory」タブで「Take heap snapshot」を使用。
コードでのヒープダンプの生成:
// index.jsに追加 const heapdump = require('heapdump'); // メモリ使用量が増加したタイミングでダンプを生成 process.on('SIGUSR2', () => { heapdump.writeSnapshot('/app/heapdump-' + Date.now() + '.heapsnapshot'); });コマンドでヒープダンプを生成:
docker exec -it <container_name> kill -USR2 1ヒープダンプファイルをローカルにコピー:
docker cp <container_name>:/app/heapdump-xxxx.heapsnapshot ./ChromeDevToolsの「Memory」タブで「Load」を使って分析。
ケース5: APIリクエストのデバッグ
問題: 特定のAPIエンドポイントで問題が発生し、リクエスト内容の調査が必要。
解決策:
リクエストハンドラーにブレークポイントを設定:
app.post('/api/users', (req, res) => { // ここにブレークポイントを設定 const userData = req.body; // ...処理... });Network条件付きブレークポイントの活用:
DevToolsの「Sources」タブで、「XHR/fetch breakpoints」にURLパターンを追加します。
リクエストモックツールの活用:
Postmanなどでコンテナに直接リクエストを送信し、デバッグブレークポイントをトリガーします。
これらの実践的なトラブルシューティング例を参考に、Dockerコンテナ内のNode.jsアプリケーションを効率的にデバッグしてください。適切なデバッグ環境を整えることで、開発効率が大幅に向上し、より堅牢なアプリケーションの開発が可能になります。
最短で課題解決する一冊
この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。
