如何调试Docker里运行的node应用

Authors
  • avatar
    Name
    小明&小艺
    Twitter

大多数时候,你可以在本地机器上运行你的应用,并且仅使用容器来隔离数据库和消息队列,但有些 bug 只有在应用本身也被容器化时才会显现。在这些情况下,知道如何附加调试器到服务上是非常有帮助的。

根据 Node.js 开发者调查报告显示,一半的 Node.js 用户使用 Docker 进行开发。虽然容器化总体上是一个非常强大的工具——在 RisingStack,我们总是通过在 docker-compose.yaml 中启动所需的基础设施来开始新项目——如果你不知道如何做到这一点,要接触到被封装的 Node 进程可能会很棘手。

如果你需要 Docker、Kubernetes、微服务或 Node.js 方面的指导,欢迎通过info@risingstack.com联系我们,或者通过我们的网站联系!

所有在本文中使用的代码片段和设置都可以在其专用的 GitHub 仓库中找到。

如何使用 Node 检查器

如果你主要使用 printf,也就是所谓的洞穴人调试,找到正确的值和正确的时间可能会非常困难。

如果你每次添加 console.log 时都不得不重新构建你的容器镜像,事情会变得更糟。如果能够构建一次镜像,然后在运行时跳入其中,检查变量,可能会容易得多。为了更好地理解我们接下来要做的事情,我强烈建议你先熟悉 node inspect 命令。

要运行你的 Node 应用在调试模式下,简单地在 node 后面添加 inspect,像这样:

当你在 inspect 模式下运行代码时,它总是在第一行停止,等待你与它交互。对于那些使用 gdb 调试代码的人来说,这个界面可能很有吸引力。然而,如果你习惯于使用 GUI 与调试器交互,你可能想打开 Chrome 浏览器,导航到 chrome://inspect。

你应该看到类似这样的东西:

在远程目标下,点击 inspect,你将被呈现 Chrome 开发者工具调试器。

现在你可以随意使用调试器了。是时候把我们的应用打包进容器了。

在 Docker 容器中调试 Node.js

首先,我们需要创建一个 Dockerfile,

和一个 docker-compose.yaml

现在如果你运行 docker-compose up,你将能够在 http://localhost:3000 上访问你的服务。

下一步是将调试端口暴露给外部世界。首先,让我们创建一个 debug-compose.yaml。

如你所见,我们打开了 9229 端口,这是 Node.js 应用的调试端口。我们还覆盖了在 Dockerfile 中指定的命令。--inspect-brk=0.0.0.0 参数做了两件事:

--inspect 告诉 Node 我们希望以调试模式运行我们的应用。

通过添加-brk,我们还确保应用在第一行停止,这样我们就有足够的时间打开检查器

添加=0.0.0.0 将调试器对任何 IP 的连接开放。

默认情况下,检查器绑定到 127.0.0.1,这是有意义的,因为我们通常不希望允许全世界的人附加调试器到我们的应用。然而,容器是与我们的主机不同的主机,具有不同的 IP,所以我们无法到达它。只要我们本地这样做是可以的;然而,我们不希望在像这样运行的实时服务器上运行它。

因此,请确保这是与你的 docker-compose.yaml 不同的文件。

通过更多的工作,你可以将调试端口从你的暂存集群暴露到你的 IP——但在这种情况下,只对你的 IP——并在那里调试问题。

还要注意,端口转发规则被包含在"-s"中。如果你省略了引号,规则可能不起作用,这使得很难弄清楚为什么你无法将调试器附加到你的进程。

话虽如此,你应该能够在开发工具中检查你的应用。

使用 Visual Studio Code 调试

使用检查器对于单个文件问题很好,但它可能在发现项目中的所有文件时遇到问题。在这些情况下,最好附加你的 IDE 提供的调试器。让我们看看如何在 Visual Studio Code 中做到这一点。

首先,导航到调试标签

然后点击齿轮图标

从弹出列表中,选择 docker(确保你安装了 Docker 扩展)

它应该在项目的.vscode 文件夹中生成一个 launch.json,看起来像这样:

它几乎可以了,尽管在我们的情况下,我们应用的根是容器文件系统的根,所以我们需要更新它。当你完成后,对象应该看起来像这样:

现在,如果你按下键盘上的 F5,你将被提示你习惯了的 VSCode 调试器。再次按下 F5,让服务器开始监听。如果你在某个地方放置了一个断点,并通过 http://localhost:3000 调用服务器,你应该看到这样

为什么不使用 ndb?

尽管 ndb 非常适合调试,但你目前无法将其附加到正在运行的进程,这几乎破坏了我们的情况。

你也可以在容器内启动你的调试进程,并从外部附加你的调试器,但你也需要修改你的 Dockerfile 才能这样做,而且你不会真正获得任何东西,因为无论如何你都需要附加你的 Chrome、VSCode 或另一个调试器。关注这个问题以获取更新。

关于 Node.js 调试的最后思考

看到像 Kubernetes、AWS ECS、Docker Swarm 等容器技术越来越普及,很明显容器将会留下来。

如果你需要 Docker、Kubernetes、微服务或 Node.js 方面的指导,欢迎通过info@risingstack.com联系我们,或者通过我们的网站联系!

你可以在开发时在本地机器上运行相同的镜像,最终将其部署到集群上,这绝对是一件好事,因为你可以将应用与配置捆绑在一起并一起部署。然而,当应用被打包时,发现只有在这种情况下才会出现的 bug 可能会很困难,当你依赖 printf 调试时,所以即使你到目前为止还没有使用过它,也绝对是个好主意,成为调试器的朋友,学习如何将它们附加到你的容器中运行的进程。

祝你调试愉快!

这篇文章的灵感来自于我们遇到了一个只在容器中出现的 bug,感谢@fazekasda 的帮助!