这篇博客文章显示了如何调试运行在Docker容器中的简单Node.js应用程序。 本教程的布局方式允许您在构建自己的Node.js应用程序时将其用作参考,并且适合那些事先接触过JavaScript编程和Docker的读者。
先决条件
1. Docker 。 有关安装Docker的详细信息,请参阅“ 安装Docker”页面。
2. Node.js 10或更高版本。 要检查您的计算机上是否安装了Node.js,请启动终端窗口并键入以下命令:
node -v
如果已经安装了Node.js,您将看到类似以下内容:
v10.15.3
如果未安装Node.js,则可以从“ 下载”页面下载安装程序。
3. Microsoft Visual Studio 。 有关如何安装Visual Studio的详细信息,请参阅“ 安装Visual Studio”页面。
初始化您的Todo应用程序
在本教程的范围内,我们将创建一个准任务列表,允许用户添加和删除任务。 应用程序中将存在一个小错误,我们将使用Visual Studio Code调试代码并解决问题。 您将在本教程中获得的知识将帮助您调试自己的应用程序。 让我们开始吧。
1.启动一个新的终端窗口,移至您的项目目录,然后执行以下命令:
mkdir MyTodoApp &&cd MyTodoApp
2.通过以下方式初始化项目:
npm init -y
这将输出如下内容:
Wrote to /Users/ProspectOne/Documents/MyTodoApp/package.json:
{"name" : "MyTodoApp" ,
"version" : "1.0.0" ,
"description" : "" ,
"main" : "index.js" ,
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1"
},
"keywords" : [],
"author" : "" ,
"license" : "ISC"
}
创建准系统Todo应用程序
我们将使用Express来构建待办应用程序, Express是Node.js的快速,不受限制的极简主义Web框架。 Express旨在使开发网站变得更加容易,它是最受欢迎的Node.js Web框架之一。
1.通过输入以下命令来安装express
和其他一些先决条件:
npm install express body-parser cookie-session ejs --save
> ejs@3.0.1 postinstall /Users/ProspectOne/Documents/test /MyTodoApp/node_modules/ejs
> node ./postinstall.js
Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN MyTodoApp@1.0.0 No description
npm WARN MyTodoApp@1.0.0 No repository field.
+ ejs@3.0.1
+ body-parser@1.19.0
+ express@4.17.1
+ cookie-session@1.3.3
added 55 packages from 39 contributors and audited 166 packages in 6.533s
found 0 vulnerabilities
2.创建一个名为app.js
的文件,其内容如下:
const express = require('express' )
const app = express()
const bodyParser = require( 'body-parser' )
const session = require( 'cookie-session' )
const urlencodedParser = bodyParser.urlencoded({ extended: false })
const port = 3000
app.use(session({ secret: process.env.SECRET }))
.use( function (req, res, next) {
next()
})
.get ( '/todo' , function (req, res) {
res.render( 'todo.ejs' , { todolist: req.session.todolist })
})
.post ( '/todo/add/' , urlencodedParser, function (req, res) {
if (req.body.newtodo != '' ) {
req.session.todolist.push(req.body.newtodo)
}
res.redirect( '/todo' )
})
.get ( '/todo/delete/:id' , function (req, res) {
if (req.params.id != '' ) {
req.session.todolist.splice(req.params.id, 1)
}
res.redirect( '/todo' )
})
.use ( function (req, res, next) {
res.redirect( '/todo' )
})
.listen(port, () => console.log(`MyTodo app is listening on port ${port} !`))
请注意,上面的代码片段是openclassroom.com网站上的代码的衍生产品,并且解释此代码的工作方式不在本教程的讨论范围之内。 如果细节不清楚,建议您在完成本教程后访问其站点,以进一步学习。
3.创建一个名为./views/todo.ejs
的文件,并将以下内容粘贴到其中:
<!DOCTYPE html>
<html>
<head>
<title>My todolist</title>
<style>
a {text-decoration: none; color: black;}
</style>
</head>
<body>
<h1>My todolist</h1>
<ul>
<% todolist.forEach(function (todo, index) { %>
<li><a href= "/todo/delete/<%= index %>" >✘</a> <%= todo %></li>
<% }); %>
</ul>
<form action= "/todo/add/" method= "post" >
<p>
<label for = "newtodo" >What should I do ?</label>
<input type = "text" name= "newtodo" id= "newtodo" autofocus />
<input type = "submit" />
</p>
</form>
</body>
</html>
4.此时,您的目录结构应类似于以下内容:
tree -L 2 -I node_modules
.
├── app.js
├── package-lock.json
├── package.json
└── views
└── todo.ejs
1 directory, 4 files
5.现在,您可以通过输入以下内容来启动Web服务器:
SECRET=bestkeptsecret; node app.js
这会将以下消息打印到控制台:
MyTodo app is listening on port 3000!
创建一个Docker镜像
既然您已经编写了Todo应用程序,现在该为它添加创建Docker映像了。 每个Docker容器均基于Docker映像,该映像包含使用Docker部署和运行应用程序所需的所有信息。 要运行Docker容器,您可以:
- 下载现有的Docker映像
- 创建自己的图像
在本教程中,您将创建自己的图像。 请注意,Docker映像通常由多层组成,并且每一层基本上都是只读文件系统。 这种工作方式是,Docker为在Dockerfile中找到的每条指令创建一个层,并将其放置在先前层的顶部。 放置经常更改的应用程序代码靠近文件底部被认为是一种好习惯。
1.创建一个名为Dockerfile
的文件,并将以下代码片段粘贴到其中:
FROM node:10
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node" , "app.js" ]
让我们仔细看一下这个文件:
- FROM :设置基本图像。 您稍后添加的所有内容都将基于该图像。 在此示例中,我们使用的是Node.js版本10。
- WORKDIR :此命令设置将用于COPY,RUN和CMD命令的工作目录。
- RUN :这行代码在Docker容器内运行
npm install
命令。 - COPY :将文件从构建上下文复制到Docker映像中
- EXPOSE :指定在容器内部运行的进程正在侦听3000端口。 当您将端口从主机转发到容器时,这将在本教程的后面部分很有用。
- CMD : 仅在容器启动后,此行在Docker容器中运行
node app.js
2.为避免将大文件发送到构建上下文并加快该过程,可以使用.dockerignore
文件。 这不过是一个纯文本文件,其中包含应从构建中排除的文件和目录的名称。 您可以认为它类似于.gitignore
文件。 创建一个名为.dockerignore
的文件,其内容如下:
node_modules
npm-debug.log
3.现在,您可以通过输入docker build
命令,然后依次输入以下内容来docker build
Docker映像:
- -t参数指定图像的名称
- 上下文路径,该路径应指向您要从Dockerfile引用的文件集
docker build -t prospectone/my-todo-list .
Sending build context to Docker daemon 24.58kB
Step 1/7 : FROM node:10
---> c5d0d6dc0b5b
Step 2/7 : WORKDIR /usr/src/app
---> Using cache
---> 508b797a892e
Step 3/7 : COPY package*.json ./
---> 0b821f725c19
Step 4/7 : RUN npm install
---> Runningin d692a6278d2b
> ejs@3.0.1 postinstall /usr/src/app/node_modules/ejs
> node ./postinstall.js
Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)
npm WARN MyTodoApp@1.0.0 No description
npm WARN MyTodoApp@1.0.0 No repository field.
added 55 packages from 39 contributors and audited 166 packages in 2.564s
found 0 vulnerabilities
Removing intermediate container d692a6278d2b
---> 067de030e269
Step 5/7 : COPY . .
---> 3141ccb6e094
Step 6/7 : EXPOSE 3000
---> Running in eb824e38d8c6
Removing intermediate container eb824e38d8c6
---> b09d55adc1c4
Step 7/7 : CMD [ "node" , "app.js" ]
---> Running in 7e77e0cbfa75
Removing intermediate container 7e77e0cbfa75
---> c0a2db4c7a65
Successfully built c0a2db4c7a65
Successfully tagged prospectone/my-todo-list:latest
如上所述, docker build
命令的工作方式是为Dockerfile中的每个命令添加一个新层。 然后,一旦命令成功执行,Docker就会删除中间容器。
4.现在,您已经构建了映像,让我们通过输入docker run
命令并向其传递以下参数来docker run
它:
-
-p
,将主机(3001)上的端口转发到容器(3000),并由:
分隔:
-
-e
创建一个称为SECRET
的环境变量并将其值设置为bestkeptsecret
-
-d
指定容器应在后台运行 - 图像的名称(
prospectone/my-awesome-app
)
docker run -p 3001:3000 -e SECRET=bestkeptsecret -d prospectone/my-todo-list
db16ed662e8a3e0a93f226ab873199713936bd687a4546d2fce93e678d131243
5.您可以验证您的Docker容器正在运行:
docker ps
输出应类似于:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a6eb166191c7 prospectone/my-todo-list"docker-entrypoint.s…" 4 seconds ago Up 3 seconds 0.0.0.0:3001->3000/tcp happy_hawking
6.要检查日志,请输入docker logs
命令,然后输入容器的id
:
docker logs a6eb166191c7
MyTodo app is listening on port 3000!
7.现在您的应用程序已启动并正在运行,将浏览器指向http:// localhost:3001,然后让我们添加一个新的待办事项。 如下所示,应用程序错误出现在todo.ejs
文件的第15行:

在下一部分中,您将学习如何使用Visual Studio Code调试它。
8.但首先,使用以下方法停止容器:
dockerkill a6eb166191c7
a6eb166191c7
在Microsoft Visual Studio代码中启用调试
Visual Studio Code为运行在Docker容器中的Node.js应用程序提供调试支持。 请按照以下步骤启用此功能:
1.通过替换以下行来编辑您的Dockerfile:
CMD ["node" , "app.js" ]
与:
CMD ["npm" , "run" , "start-debug" ]
您的Dockerfile应该如下所示:
FROM node:10
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm" , "run" , "start-debug" ]
2.打开package.json
文件,并将以下行添加到scripts
对象:
"start-debug" : "node --inspect=0.0.0.0 app.js"
此行代码启动Node.js进程,并在端口9229
上侦听调试客户端。
这是package.json
文件的外观:
{"name" : "MyTodoApp" ,
"version" : "1.0.0" ,
"description" : "" ,
"main" : "index.js" ,
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1" ,
"start-debug" : "node --inspect=0.0.0.0 app.js"
},
"keywords" : [],
"author" : "" ,
"license" : "ISC" ,
"dependencies" : {
"body-parser" : "^1.19.0" ,
"cookie-session" : "^1.3.3" ,
"ejs" : "^3.0.1" ,
"express" : "^4.17.1"
}
}
3.每次更新Dockerfile时,都必须再次构建Docker映像:
docker build -t prospectone/my-todo-list .
Sending build context to Docker daemon 19.97kB
Step 1/7 : FROM node:10
---> c5d0d6dc0b5b
Step 2/7 : WORKDIR /usr/src/app
---> Using cache
---> 508b797a892e
Step 3/7 : COPY package*.json ./
---> c0eec534b176
Step 4/7 : RUN npm install
---> Runningin a155901cb957
npm WARN MyAwesomeApp@1.0.0 No description
npm WARN MyAwesomeApp@1.0.0 No repository field.
added 50 packages from 37 contributors and audited 126 packages in 11.504s
found 0 vulnerabilities
Removing intermediate container a155901cb957
---> 010473a35e41
Step 5/7 : COPY . .
---> 76dfa12d4db4
Step 6/7 : EXPOSE 3000
---> Running in b5a334c9a2ea
Removing intermediate container b5a334c9a2ea
---> b5a869ab5441
Step 7/7 : CMD [ "npm" , "run" , "start-debug" ]
---> Running in 1beb2ca9a391
Removing intermediate container 1beb2ca9a391
---> 157b7d4cb77b
Successfully built 157b7d4cb77b
Successfully tagged prospectone/my-todo-list:latest
请注意,步骤7已更新,这意味着Docker现在将执行npm run start-debug
命令。
4.要启用Visual Studio Code调试,还必须转发端口9229
。 通过输入以下内容来启动您的Docker容器:
docker run -p 3001:3000 -p 9229:9229 -e SECRET=bestkeptsecret22222 -d perfops/my-todo-list
0f5860bebdb5c70538bcdd10ddc901411b37ea0c7d92283310700085b1b8ddc5
5.您可以通过在容器id
之后输入docker logs
命令来检查日志:
docker logs 0f5860bebdb5c70538bcdd10ddc901411b37ea0c7d92283310700085b1b8ddc5
> My@1.0.0 start-debug /usr/src/app
> node --inspect=0.0.0.0 app.js
Debugger listening on ws://0.0.0.0:9229/59d4550c-fc0e-412e-870a-c02b4a6dcd0f
Forhelp , see: https://nodejs.org/en/docs/inspector
请注意,调试器现在正在侦听端口9229
。 接下来,将配置Visual Studio代码以调试应用程序。
使用Visual Studio代码调试应用程序
1.在Visual Studio Code中,打开MyTodoApp
目录。

2.调试配置存储在名为launch.json
的文件中。 要打开它,请按Command+Shift+P
,然后选择“ Debug: Open launch.json
。

3.用以下代码片段替换launch.json
文件的内容:
{"version" : "0.2.0" ,
"configurations" : [
{
"name" : "Docker: Attach to Node" ,
"type" : "node" ,
"request" : "attach" ,
"port" : 9229,
"address" : "localhost" ,
"localRoot" : " ${workspaceFolder} " ,
"remoteRoot" : "/usr/src/app" ,
"protocol" : "inspector" ,
"skipFiles" : [
" ${workspaceFolder} /node_modules/**/*.js" ,
"<node_internals>/**/*.js"
]
}
]
}
请注意,我们使用skipFiles
属性来避免单步执行node_modules
目录和Node.js的内置核心模块中的代码。
4.现在一切都已设置好,您可以开始调试应用程序了。 请记住, views.js
文件的第15行有一个错误,该错误基本上遍历了todolist
数组: todolist.forEach(function(todo, index)
。查看app.js
文件,您会看到todo.ejs在第14行渲染。让我们添加一个断点,以便我们可以检查todolist
变量的值:

5.输入Shift+Command+D
切换到“ Debug
视图。 然后,单击“ Debug and Run
按钮:

6.要检查req.session.todolist
变量的值,必须通过选择+
号然后键入变量的名称( req.session.todolist
)添加一个新的表达式以进行观察:

7.切换到浏览器窗口,然后重新加载http:// localhost:3001页面。

请注意底部的“ Waiting for localhost
消息。 这意味着我们的断点已暂停执行,我们可以检查req.session.todolist
变量的值。 返回到Visual Studio以获取详细信息:

因此, req.session.todolist
变量是undefined
。 您能想到如何解决此错误吗? 答案在下面,但是请先考虑一下再继续。
8. ejb
模板遍历应存储在当前会话中的todolist
数组。 但是我们忘了初始化这个数组,所以它是undefined
。 让我们通过在.use
函数中添加以下代码行来解决此问题:
if (typeof (req.session.todolist) == 'undefined' ) {
req.session.todolist = []
}
确保将此代码段粘贴在调用下一个函数的代码行上方。 您的.use函数应如下所示:
app.use(session({ secret: process.env.SECRET }))
.use(function (req, res, next) {
if (typeof (req.session.todolist) == 'undefined' ) {
req.session.todolist = []
}
next()
})
9.检索正在运行的容器的id
:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb9f175f7af3 prospectone/my-todo-list"docker-entrypoint.s…" 15 minutes ago Up 15 minutes 0.0.0.0:9229->9229/tcp, 0.0.0.0:3001->3000/tcp nervous_hopper
10.通过输入docker kill
命令和其id
停止容器:
dockerkill cb9f175f7af3
cb9f175f7af3
11.要应用更改,您必须再次运行docker build
命令:
docker build -t prospectone/my-todo-list .
Sending build context to Docker daemon 26.11kB
Step 1/7 : FROM node:10
---> c5d0d6dc0b5b
Step 2/7 : WORKDIR /usr/src/app
---> Using cache
---> 508b797a892e
Step 3/7 : COPY package*.json ./
---> Using cache
---> c5ac875da76b
Step 4/7 : RUN npm install
---> Using cache
---> 29e7b3bac403
Step 5/7 : COPY . .
---> b92f577afd57
Step 6/7 : EXPOSE 3000
---> Runningin 78606a3c2e03
Removing intermediate container 78606a3c2e03
---> 59c2ed552549
Step 7/7 : CMD [ "npm" , "run" , "start-debug" ]
---> Running in e0313973bb5a
Removing intermediate container e0313973bb5a
---> 70a675646c0d
Successfully built 70a675646c0d
Successfully tagged prospectone/my-todo-list:latest
12.现在,您可以使用以下命令运行容器:
docker run -p 3001:3000 -p 9229:9229 -e SECRET=bestkeptsecret222212 -d prospectone/my-todo-list
f75d4ef8b702df13749b10615f3945ea61b36571b0dc42b76f50b3c99e14f4c6
13.通过运行以下命令来检查日志:
docker logs 10f467dbb476
f75d4ef8b702df13749b10615f3945ea61b36571b0dc42b76f50b3c99e14f4c6
14.重新加载页面并添加新任务:

恭喜,您已经成功编写了一个基本的待办事项应用程序,并在Docker容器中运行了该应用程序,并使用Visual Studio Code对其进行了调试并修复了一个错误。 在下一篇博客文章中,我们将引导您完成对现有应用程序进行docker化的过程。
关于作者-Sudip是一位具有15年以上工作经验的解决方案架构师,并且是Javelynn的创始人。 他喜欢通过写作分享自己的知识,当他不这样做时,他一定是在钓鱼或下棋。
先前发布在 https://appfleet.com/上 。