在容器中偵錯 Node.js
將 Docker 檔案新增至 Node.js 專案時,會新增工作和啟動組態,以啟用在 Docker 容器中偵錯該應用程式。然而,由於 Node.js 周圍的龐大生態系統,這些工作無法容納每個應用程式架構或程式庫,這表示某些應用程式將需要額外組態。
設定 Docker 容器進入點
Docker 擴充功能會透過 package.json
的屬性,推斷 Docker 容器的進入點,也就是在 Docker 容器中以偵錯模式啟動應用程式的命令列。擴充功能會先在 scripts
物件中尋找 start
指令碼;如果找到且以 node
或 nodejs
命令開頭,則會使用該指令碼來建置以偵錯模式啟動應用程式的命令列。如果找不到或不是可辨識的 node
命令,則會使用 package.json
中的 main
屬性。如果兩者都找不到或無法辨識,則您需要明確設定用於啟動 Docker 容器的 docker-run
工作的 dockerRun.command
屬性。
某些 Node.js 應用程式架構包含用於管理應用程式的 CLI,並用於在 start
指令碼中啟動應用程式,這會模糊化底層的 node
命令。在這些情況下,Docker 擴充功能無法推斷啟動命令,您必須明確設定啟動命令。
範例:設定 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
}
}
]
}
自動啟動瀏覽器至應用程式的進入頁面
Docker 擴充功能可以在應用程式在偵錯工具中啟動後,自動啟動瀏覽器至應用程式的進入點。此功能預設為啟用,並透過 launch.json
中偵錯組態的 dockerServerReadyAction
物件進行設定。
此功能取決於應用程式的幾個方面
- 應用程式必須將記錄輸出至偵錯主控台。
- 應用程式必須記錄「伺服器已就緒」訊息。
- 應用程式必須提供可瀏覽的頁面。
雖然預設設定可能適用於以 Express.js 為基礎的應用程式,但其他 Node.js 架構可能需要明確設定其中一個或多個方面。
確保應用程式記錄寫入偵錯主控台
此功能取決於應用程式將其記錄寫入附加偵錯工具的偵錯主控台。然而,並非所有記錄架構都會寫入偵錯主控台,即使設定為使用以主控台為基礎的記錄器 (因為某些「主控台」記錄器實際上會繞過主控台並直接寫入 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
記錄器僅在透過 DEBUG
環境變數啟用時才會寫入記錄,該變數可以在 docker-run
工作中設定。(當 Docker 檔案新增至應用程式時,此環境變數預設會設定為 *
。)
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"env": {
"DEBUG": "*"
}
},
"node": {
"enableDebugging": true
}
}
]
}
設定應用程式何時「就緒」
當應用程式將 Listening on port <number>
格式的訊息寫入偵錯主控台時 (如 Express.js 預設所做的那樣),擴充功能會判斷應用程式「就緒」可以接收 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": "Docker Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"pattern": "Application has started on port (\\d+)"
}
}
]
}
請注意連接埠號碼的
(\\d+)
擷取群組,以及使用\
作為 JSON 逸出字元,用於\d
字元類別中的反斜線。
設定應用程式進入頁面
預設情況下,Docker 擴充功能會開啟瀏覽器的「主要」頁面 (無論應用程式如何判斷)。如果應該將瀏覽器開啟至特定頁面,則應將偵錯啟動組態的 dockerServerReadyAction 物件的 uriFormat
屬性設定為 Node.js 格式字串,其中一個字串權杖指示應替換連接埠的位置。
偵錯啟動組態 (`launch.json` 中) 中對應的 uriFormat
,用於開啟 about.html
頁面而非主要頁面,將會是
{
"configurations": [
{
"name": "Docker Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"uriFormat": "https://127.0.0.1:%s/about.html"
}
}
]
}
將 Docker 容器來源檔案對應至本機工作區
預設情況下,Docker 擴充功能會假設執行中 Docker 容器中的應用程式來源檔案位於 `/usr/src/app` 資料夾中,然後偵錯工具會將這些檔案對應回已開啟工作區的根目錄,以便將中斷點從容器轉譯回 Visual Studio Code。
如果應用程式來源檔案位於不同的位置 (例如,不同的 Node.js 架構有不同的慣例),無論是在 Docker 容器內或已開啟的工作區內,則應將偵錯啟動組態的 node 物件的 localRoot
和/或 remoteRoot
屬性設定為工作區和 Docker 容器內的根來源位置。
例如,如果應用程式改為位於 `/usr/my-custom-location`,則對應的 remoteRoot
屬性將會是
{
"configurations": [
{
"name": "Docker Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"node": {
"remoteRoot": "/usr/my-custom-location"
}
}
]
}
疑難排解
Docker 映像因遺失 node_modules 而無法建置或啟動
Dockerfile 的排列方式通常旨在最佳化映像建置時間、映像大小或兩者兼具。然而,並非每個 Node.js 應用程式架構都支援所有典型的 Node.js Dockerfile 最佳化。特別是,對於某些架構,node_modules
資料夾必須是應用程式根資料夾的直接子資料夾,而 Docker 擴充功能會建立 Dockerfile,其中 node_modules
資料夾存在於父層或祖先層級 (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"]