尝试以扩展 VS Code 中的代理模式!

在容器中调试 Node.js

当向 Node.js 项目添加 Docker 文件时,将添加任务和启动配置,以允许在容器中调试该应用程序。但是,由于 Node.js 周围的生态系统庞大,这些任务无法适应所有应用程序框架或库,这意味着某些应用程序将需要额外的配置。

配置容器入口点

容器工具扩展通过 `package.json` 的属性推断容器的入口点,即在容器中以调试模式启动应用程序的命令行。扩展首先在 `scripts` 对象中查找 `start` 脚本;如果找到,并且它以 `node` 或 `nodejs` 命令开头,它将使用该命令来构建在调试模式下启动应用程序的命令行。如果未找到,或者如果不是可识别的 `node` 命令,则使用 `package.json` 中的 `main` 属性。如果两者都未找到或未识别,则需要显式设置用于启动容器的 `docker-run` 任务的 `dockerRun.command` 属性。

某些 Node.js 应用程序框架包含用于管理应用程序的 CLI,并用于在 `start` 脚本中启动应用程序,这会模糊底层的 `node` 命令。在这种情况下,容器工具扩展无法推断启动命令,您必须显式配置启动命令。

示例:为 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
      }
    }
  ]
}

自动将浏览器启动到应用程序的入口页面

容器工具扩展可以在应用程序在调试器中启动后自动将浏览器启动到应用程序的入口点。此功能默认启用,并通过 `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 ` 的消息时(Express.js 默认如此),扩展会确定应用程序已“就绪”接收 HTTP 连接。如果应用程序记录了不同的消息,则应将调试启动配置(在 `launch.json` 中)的 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 转义字符的使用。

配置应用程序入口页面

默认情况下,容器工具扩展将打开浏览器的“主”页面(无论应用程序如何确定)。如果浏览器应打开到特定页面,则调试启动配置的 dockerServerReadyAction 对象的 `uriFormat` 属性应设置为 Node.js 格式字符串,其中包含一个字符串令牌,指示应替换端口的位置。

调试启动配置(在 `launch.json` 中)中用于打开 `about.html` 页面而不是主页的相应 `uriFormat` 将是

{
  "configurations": [
    {
      "name": "Containers: Node.js Launch",
      "type": "docker",
      "request": "launch",
      "preLaunchTask": "docker-run: debug",
      "platform": "node",
      "dockerServerReadyAction": {
        "uriFormat": "https://:%s/about.html"
      }
    }
  ]
}

将容器源文件映射到本地工作区

默认情况下,容器工具扩展假定正在运行的容器中的应用程序源文件位于 `/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 而无法构建或启动

Dockerfiles 通常以优化镜像构建时间、镜像大小或两者的方式进行安排。但是,并非所有 Node.js 应用程序框架都支持所有典型的 Node.js Dockerfile 优化。特别是,对于某些框架,`node_modules` 文件夹必须是应用程序根文件夹的直接子文件夹,而容器工具扩展会搭建一个 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"]