🚀 在 VS Code 中免費取得

在容器中偵錯 Node.js

將 Docker 檔案新增至 Node.js 專案時,會新增工作和啟動組態,以啟用在 Docker 容器中偵錯該應用程式。然而,由於 Node.js 周圍的龐大生態系統,這些工作無法容納每個應用程式架構或程式庫,這表示某些應用程式將需要額外組態。

設定 Docker 容器進入點

Docker 擴充功能會透過 package.json 的屬性,推斷 Docker 容器的進入點,也就是在 Docker 容器中以偵錯模式啟動應用程式的命令列。擴充功能會先在 scripts 物件中尋找 start 指令碼;如果找到且以 nodenodejs 命令開頭,則會使用該指令碼來建置以偵錯模式啟動應用程式的命令列。如果找不到或不是可辨識的 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"]