现已发布!阅读关于 11 月新增功能和修复的内容。

在容器中调试 Node.js

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

配置容器入口点

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

某些 Node.js 应用程序框架包含用于管理应用程序的 CLI,并在 start 脚本中使用这些 CLI 来启动应用程序,这会隐藏底层的 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 <number> 的消息时(如 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 转义字符。

配置应用程序入口页面

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

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

{
  "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 对象的 localRootremoteRoot 属性中的一个或两个,分别设置为工作区和容器内的根源位置。

例如,如果应用程序位于 /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"]
© . This site is unofficial and not affiliated with Microsoft.