新知识点!一文告诉你如何调试运行在Docker容器中的远程Node.js应用程序

云栖号:https://yqh.aliyun.com
第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策!

新知识点!一文告诉你如何调试运行在Docker容器中的远程Node.js应用程序

你知道吗?

如果你想要调试已运行在远程计算机上Docker容器内的Node.js应用程序,并且希望无需修改命令参数(启用调试模式),其实并不需要向全世界开放远程Node.js调试器代理端口来实现。

或许你原本不知道这真的可以实现,也不知道该如何实现。不过当你看完本文,就会Get 到一些新奇的知识点,话不多说,往下看吧!

TdodoMVC演示应用程序

作者将使用TodoMVC Node.js应用程序的一个Github分支(由Gleb Bahmutov创建)作为本文的演示应用程序,可以随意克隆并使用此代码库:https://github.com/alexei-led/todomvc-express。

下面是添加的用于TodoMVC应用程序的Dockerfile,它允许在Docker容器中运行TodoMVC应用程序。

FROM alpine:3.5
# install node
RUN apk add --no-cache nodejs-current tini
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Build time argument to set NODE_ENV (‘production’’ bydefault)
ARG NODE_ENV
ENV NODE_ENV ${NODE_ENV:-production}
# install npm packages: clean obsolete files
COPY package.json /usr/src/app/
RUN npm config set depth 0 && 
npm install && 
npm cache clean && 
rm -rf /tmp/*
# copy source files
COPY . /usr/src/app
EXPOSE 3000
# Set tini as entrypoint
ENTRYPOINT [“/sbin/tini”, “--“]
CMD [ “npm”, “start” ]
# add VCS labels for code sync and nice reports
ARG VCS_REF=”local”
LABEL org.label-schema.vcs-ref=$VCS_REF 
org.label-schema.vcs-url="https://github.com/alexei-led/todomvc-express.git"

在Docker容器中构建和运行TodoMVC

要为TodoMVC应用程序构建新的Docker映像,请运行docker build命令。

$ # build Docker image; set VCS_REF tocurrent HEAD commit (short)
$ docker build -t local/todomvc --build-argVCS_REF=`git rev-parse ---short HEAD` .
$ # run TodoMVC in a Docker container
$ docker run -d -p 3000:3000 --name todomvclocal/todomvc node src/start.js

新知识点!一文告诉你如何调试运行在Docker容器中的远程Node.js应用程序

计划

最终目标:作者希望能够将Node.js调试器添加到AWS云中的远程主机上Docker容器中已经启动并运行的Node.js应用程序上,而无需修改应用程序、容器、容器配置或使用其他调试标志重新启动它。

想象一下,该应用程序正在运行,但是现在发生了一些问题。作者想使用调试器连接到该应用程序并开始查看问题。

因此,作者需要一个计划一个逐步的流程,这将有助于实现最终目标。下面让我们来探索操作流程。

在服务器(AWS EC2 VM)机器上,有一个运行在Docker容器中的Node.js应用程序。在客户端(笔记本电脑)上,使用一个IDE(Visual Studio Code),Node.js应用程序代码(git pull /clone)和一个Node.js调试器。下面是计划清单:

  • 将已经运行的应用程序设置为调试模式
  • 公开一个新的Node.js调试器代理端口,以安全的方式启用远程调试
  • 同步客户端-服务器代码:两者都应在git树中的相同的提交上
  • 将本地Node.js调试器添加到远程服务器上的Node.js调试器代理端口,并以安全的方式进行
  • 如果一切正常,应该能够执行常规的调试任务,例如设置断点、检查变量和暂停执行等。

步骤1:将已经运行的Node.js应用程序设置为调试模式

通过使用debug命令行标志启动Node或使用SIGUSR1通知现有的Node进程,可以启用和访问V8调试器。(Node API文档)

因此,为了打开Node调试器代理,只需要将SIGUSR1信号发送到TodoMVC应用程序的Node.js进程。注意它运行在Docker容器中。我们可以使用什么命令将过程信号发送到在Docker容器中运行的应用程序呢?

作者选择docker killcommand,该命令实际上并没有杀死在Docker容器中运行的PID 1进程,而是向其发送Unix信号(默认情况下,它会发送SIGKILL)。

1)将TodoMVC设置为调试模式

下面要做的就是将SIGUSR1发送到在todomvcDocker容器中运行的TodoMVC应用程序。有两种方法可以做到这一点:

  • 使用docker kill --signal命令将SIGUSR1发送到在Docker容器中运行的PID 1进程,如果它是正确的(正确完成信号转发)初始化应用程序(如tini),那么它将起作用;
  • 或者在已经运行的Docker容器中执行kill -s SIGUSR1,将SIGUSR1信号发送到主Node.js进程。
$ # send SIGUSR1 with docker kill (if usingproper init process)
$ docker kill --signal SIGUSR1 todomvc
$ # OR run kill command for node processinside todomvc container
$ docker exec -it todomvc sh -c ‘kill -sSIGUSR1 $(pidof -s node)’

让我们确认Node应用程序已设置为调试模式。

$ docker logs todomvcTodoMVC server listening athttp://:::3000
emitting 2 todos server has new 2 todos
GET / 200 31.439 ms — 3241
GET /app.css 304 4.907 ms
— —
Starting debugger agent.
Debugger listening on 127.0.0.1:5858

如上所示,Node.js调试器代理已启动,但它只能接受来自本地主机的连接,请参见最后输出行:Debugger listening 127.0.0.1:5858

步骤2:公开节点调试端口

为了将远程Node.js调试器添加到以调试模式运行的Node应用程序中,需要以下操作:

  • 允许从任何(或特定)IP(或IP范围)连接到调试器代理
  • 公开Docker容器外部的Node.js调试器代理的端口

当应用程序已经在Docker容器中运行并且Node.js调试器代理准备与运行在同一台机器上的Node.js调试器进行通信而无法从Node.js调试器代理端口访问时,该如何在Docker容器之外访问呢?

新知识点!一文告诉你如何调试运行在Docker容器中的远程Node.js应用程序

可以使用暴露的调试器端口启动每个Node.js Docker容器并允许来自任何IP的连接(使用特殊的--debug-port和--debug Node.js标志),但是我们并不是在寻找很简单的方法。

从安全的角度来看,这不是一个好主意(允许不受保护地访问Node.js调试器)。另外,如果我们使用调试标志重新启动一个已经在运行的应用程序,那么我们将失去当前的执行上下文,并且可能无法重现要调试的问题。那么我们需要一个更好的解决方案

很不幸,Docker不允许为已经运行的Docker容器公开其他端口。因此,我们需要以某种方式连接到正在运行的容器网络,并为Node.js调试器代理公开一个新端口。

同样,当Node.js进程已经启动时,也无法告诉Node.js调试器代理接受来自不同IP地址的连接。

借助名为socat(SOcketCAT)的小型Linux实用程序,可以解决上述两个问题。就像netcat一样,但考虑到安全性(例如它支持chrooting),并且可以在各种协议并通过文件、管道、设备、TCP套接字、Unix套接字、OCKS4的客户端、代理CONNECT或SSL等工作。

socat手册页上描述如下:

socat是基于命令行的实用程序,可建立两个双向字节流并在它们之间传输数据。因为可以从大量不同类型的数据接收器和源(请参阅地址类型)中构造流,并且由于可以将大量地址选项应用于流,所以socat可以用于许多不同的目的。

这个socat正是我需要的。因此,计划如下:我将在板载socat实用程序的情况下运行一个新的Docker容器,并为TodoMVC容器配置Node.js调试器端口映射

socat.Dockerfile如下:

FROM alpine:3.5
RUN apk add --no-cache socat
CMD socat -h

1)构建socat Docker容器

$ docker build -t local/socat — <socat.Dockerfile

2)允许从任何IP连接到Node debugger代理

需要在与todomvc容器相同的网络名称空间中运行“ sidecar” socat容器,并定义端口映射

$ # define local port forwarding
$ docker run -d --name socat-nip--network=container:todomvc 
local/socat socat TCP-LISTEN:4848,forkTCP:127.0.0.1:5858

现在,到达4848端口的所有流量都将被路由到监听127.0.0.1:5858的Node.js调试器代理。 4848端口可以接受来自任何IP的流量。还可以使用IP范围来限制到socat侦听端口的连接,添加range = 选项。

3)公开Docker容器的Node.js调试器端口

在大多数情况下,我们都想调试在远程计算机(例如,AWS EC2实例)上运行的应用程序。我们也不想将不受保护的Node.js调试器代理端口公开给全世界。一种可行且可行的解决方案是使用SSH隧道访问此端口。

$ # Open SSH Tunnel to gain access to serversport 5858.
$ # Set `SSH_KEY_FILE` to ssh key location oradd it to ssh-agent
$ #
$ # open an ssh tunnel, send it to the bg, andwait 20 seconds
$ # for connections, once all connections areclosed
$ # after 20 seconds then close the tunnel
$ ssh -i ${SSH_KEY_FILE} -f -oExitOnForwardFailure=yes 
-L 5858:127.0.0.1:5858ec2_user@some.ec2.host.com sleep 20

现在,所有到localhost:5858的流量都将通过SSH隧道传输到远程Docker主机,并经过一些socat转发到运行在todomvc容器中的Node.js调试器代理上。

新知识点!一文告诉你如何调试运行在Docker容器中的远程Node.js应用程序

步骤3:同步相同的代码提交

为了能够调试远程应用程序,你需要确保在IDE中使用的代码与在远程服务器上运行的代码相同。作者也将尝试使这一步骤自动化,还记得在TodoMVC Dockerfile中使用过的LABEL命令吗?这些标签可帮助我们识别git存储库并提交应用程序Docker映像:

  • org.label-schema.vcs-ref——包含用于HEAD提交的短SHA
  • org.label-schema.vcs-url——包含一个应用程序git存储库URL(可在git clone/pull中使用)

作者使用的是(标签架构约定)[http://label-schema.org/rc1/],因为它很有用,你也可以选择其他任何约定。

这种方法可以为每个正确标记的Docker映像标识应用程序代码存储库以及创建该存储库的提交。

$ # get git repository url form Docker image
$ GIT_URL=$(docker inspect local/todomvc
| jq -r‘.[].ContainerConfig.Labels.”org.label-schema.vcs-url”’)
$ # get git commit from Docker image
$ GIT_COMMIT=$(docker inspect local/todomvc
| jq -r ‘.[].ContainerConfig.Labels.”org.label-schema.vcs-ref”’)
$
$ # clone git repository, if needed
$ git clone $GIT_URL
$ # set HEAD to same commit as server
$ git checkout $GIT_COMMIT

现在,本地开发环境和远程应用程序都在同一个git commit上了,我们可以开始调试代码了。

步骤4:将本地Node.js调试器添加到调试器代理端口

需要配置IDE才能开始调试,作者使用的是Visual Studio Code,这里需要添加一个新的Launch配置。

此启动配置指定远程调试器服务器和要添加的端口以及应用程序源文件的远程位置,这些位置应与本地文件同步(请参见上一步)。

{ // For more information about Node.js debugattributes, visit: https://go.microsoft.com/fwlink/?linkid=830387
“version”: “0.2.0”,
“configurations”: [ {
“type”: “node”,
“request”: “attach”,
“name”: “Debug Remote Docker”,
“address”: “127.0.0.1”,
“port”: 5858, “localRoot”:
“${workspaceRoot}/”,
“remoteRoot”: “/usr/src/app/” }
] }

总结

通过以上步骤可以实现计划的目标:可以将Node.js调试器添加到已经在远程计算机上的Docker容器中运行的Node.js应用程序。找到合适的解决方案需要很长时间,但是在找到合适的解决方案之后,该过程看起来并不复杂。

弄清楚之后,一旦在环境中遇到新问题,就可以轻松地将Node.js调试器添加到正在运行的应用程序,并开始研究该问题,这样非常方便。

作者录制了一部短片,只是为了证明所演示的步骤与本文中所描述的所有步骤一样都很顺利。

希望这篇文章对你有用,欢迎评论区和我们讨论!

第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策!

原文发布时间:2020-02-26
本文作者:Alexei Ledenev
本文来自:“CSDN云计算公众号”,了解相关信息可以关注“CSDN云计算”