如何使用 Docker 构建 Node.js 应用程序
介绍
Docker平台允许开发人员将应用程序打包并以容器的形式运行。容器是在共享操作系统上运行的独立进程,为虚拟机提供了更轻量级的替代方案。虽然容器并非新技术,但它们的优势(包括进程隔离和环境标准化)随着越来越多的开发人员使用分布式应用程序架构而变得越来越重要。
使用 Docker 构建和扩展应用程序时,通常首先要为应用程序创建一个镜像,然后将其在容器中运行。该镜像包含应用程序代码、库、配置文件、环境变量和运行时。使用镜像可确保容器中的环境标准化,并且仅包含构建和运行应用程序所需的内容。
在本教程中,您将为使用Express框架和Bootstrap 的静态网站创建一个应用程序镜像。然后,您将使用该镜像构建一个容器并将其推送到Docker Hub以供将来使用。最后,您将从 Docker Hub 存储库中提取存储的镜像并构建另一个容器,以演示如何重新创建和扩展您的应用程序。
先决条件
要遵循本教程,您需要:
- 一台 Ubuntu 18.04 服务器,按照初始服务器设置指南进行设置。
- 按照如何在 Ubuntu 18.04 上安装和使用 Docker的步骤 1 和 2 在您的服务器上安装 Docker 。
- 安装了 Node.js 和 npm,按照这些说明使用 NodeSource 管理的 PPA 进行安装。
- 一个 Docker Hub 帐户。有关如何设置此帐户的概述,请参阅Docker Hub 入门介绍。
第1步 - 安装应用程序依赖项
要创建镜像,您首先需要创建应用程序文件,然后将其复制到容器中。这些文件将包含应用程序的静态内容、代码和依赖项。
首先,在非root用户的主目录中为你的项目创建一个目录。我们将它命名为 ours node_project
,但你也可以用其他名称替换它:
mkdir node_project
导航到此目录:
cd node_project
这将是项目的根目录。
接下来,创建一个package.json
包含项目依赖项和其他标识信息的文件。使用以下代码nano
或您喜欢的编辑器打开该文件:
nano package.json
添加以下项目信息,包括项目名称、作者、许可证、入口点和依赖项。请务必将作者信息替换为您自己的姓名和联系方式:
~/node_project/package.json
{
"name": "nodejs-image-demo",
"version": "1.0.0",
"description": "nodejs image demo",
"author": "Sammy the Shark <sammy@example.com>",
"license": "MIT",
"main": "app.js",
"keywords": [
"nodejs",
"bootstrap",
"express"
],
"dependencies": {
"express": "^4.16.4"
}
}
此文件包含项目名称、作者以及共享时所遵循的许可证。Npm建议您使用简短且具有描述性的名称,并避免在npm 注册表中出现重复。我们在许可证字段中列出了MIT 许可证,允许免费使用和分发应用程序代码。
此外,该文件还规定:
"main"
:应用程序的入口点app.js
。接下来您将创建此文件。"dependencies"
:项目依赖项 — 在本例中为 Express 4.16.4 或更高版本。
虽然此文件未列出存储库,但您可以按照以下指南添加存储库package.json
。如果您要对应用程序进行版本控制,这是一个很好的补充。
完成更改后,保存并关闭文件。
要安装项目的依赖项,请运行以下命令:
npm install
package.json
这将安装您在项目目录中的文件中列出的软件包。
我们现在可以继续构建应用程序文件。
第2步 - 创建应用程序文件
我们将创建一个网站,为用户提供关于鲨鱼的信息。我们的应用程序将包含一个主入口,app.js
以及一个views
包含项目静态资源的目录。落地页,index.html
将为用户提供一些初步信息,并提供一个指向包含更详细鲨鱼信息的页面的链接,即sharks.html
。在views
目录中,我们将创建落地页和sharks.html
。
首先,app.js
在主项目目录中打开定义项目的路由:
nano app.js
该文件的第一部分将创建 Express 应用程序和路由器对象,并将基目录、端口和主机定义为变量:
~/node_project/app.js
var express = require("express");
var app = express();
var router = express.Router();
var path = __dirname + '/views/';
const PORT = 8080;
const HOST = '0.0.0.0';
该require
函数加载express
模块,然后我们使用它来创建app
和router
对象。该router
对象将执行应用程序的路由功能,当我们定义 HTTP 方法路由时,我们会将它们添加到此对象中,以定义应用程序如何处理请求。
该文件的这一部分还设置了一些变量path
,,,PORT
和HOST
:
path
:定义基目录,它将是views
当前项目目录中的子目录。HOST
:定义应用程序将绑定并监听的地址。将此设置为0.0.0.0
所有 IPv4 地址与 Docker 的默认暴露容器行为相对应,0.0.0.0
除非另有说明。PORT
:告诉应用程序监听并绑定到端口8080
。
接下来,使用对象设置应用程序的路由router
:
~/node_project/app.js
...
router.use(function (req,res,next) {
console.log("/" + req.method);
next();
});
router.get("/",function(req,res){
res.sendFile(path + "index.html");
});
router.get("/sharks",function(req,res){
res.sendFile(path + "sharks.html");
});
该router.use
函数加载一个中间件函数,该函数将记录路由器的请求并将其传递给应用程序的路由。这些中间件在后续函数中定义,这些函数指定对基础项目 URL 的 GET 请求应返回页面index.html
,而对路由的 GET 请求/sharks
应返回sharks.html
。
最后,挂载router
中间件和应用程序的静态资产并告诉应用程序监听端口8080
:
~/node_project/app.js
...
app.use(express.static(path));
app.use("/", router);
app.listen(8080, function () {
console.log('Example app listening on port 8080!')
})
完成的app.js
文件将如下所示:
~/node_project/app.js
var express = require("express");
var app = express();
var router = express.Router();
var path = __dirname + '/views/';
const PORT = 8080;
const HOST = '0.0.0.0';
router.use(function (req,res,next) {
console.log("/" + req.method);
next();
});
router.get("/",function(req,res){
res.sendFile(path + "index.html");
});
router.get("/sharks",function(req,res){
res.sendFile(path + "sharks.html");
});
app.use(express.static(path));
app.use("/", router);
app.listen(8080, function () {
console.log('Example app listening on port 8080!')
})
完成后保存并关闭文件。
接下来,让我们向应用程序添加一些静态内容。首先创建views
目录:
mkdir views
打开登陆页面文件index.html
:
nano views/index.html
将以下代码添加到文件中,这将导入 Boostrap 并创建一个jumbotron组件,其中包含指向更详细信息页面的链接sharks.html
:
~/node_project/views/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>About Sharks</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="css/styles.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
</head>
<body>
<nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
<div class="container">
<button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
</button> <a class="navbar-brand" href="#">Everything Sharks</a>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav mr-auto">
<li class="active nav-item"><a href="/" class="nav-link">Home</a>
</li>
<li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="jumbotron">
<div class="container">
<h1>Want to Learn About Sharks?</h1>
<p>Are you ready to learn about sharks?</p>
<br>
<p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-lg-6">
<h3>Not all sharks are alike</h3>
<p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.
</p>
</div>
<div class="col-lg-6">
<h3>Sharks are ancient</h3>
<p>There is evidence to suggest that sharks lived up to 400 million years ago.
</p>
</div>
</div>
</div>
</body>
</html>
此处的顶级导航栏允许用户在主页和Sharks页面之间切换。在navbar-nav
子组件中,我们使用 Bootstrap 的active
类来向用户指示当前页面。我们还指定了指向静态页面的路由,这些路由与我们在 中定义的路由相匹配app.js
:
~/node_project/views/index.html
...
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav mr-auto">
<li class="active nav-item"><a href="/" class="nav-link">Home</a>
</li>
<li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
</li>
</ul>
</div>
...
此外,我们在大屏幕按钮中创建了指向鲨鱼信息页面的链接:
~/node_project/views/index.html
...
<div class="jumbotron">
<div class="container">
<h1>Want to Learn About Sharks?</h1>
<p>Are you ready to learn about sharks?</p>
<br>
<p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
</p>
</div>
</div>
...
标题中还有一个指向自定义样式表的链接:
~/node_project/views/index.html
...
<link href="css/styles.css" rel="stylesheet">
...
我们将在此步骤结束时创建此样式表。
完成后保存并关闭文件。
有了应用程序登陆页面,我们就可以创建鲨鱼信息页面,sharks.html
它将为感兴趣的用户提供更多有关鲨鱼的信息。
打开文件:
nano views/sharks.html
添加以下代码,导入 Bootstrap 和自定义样式表并向用户提供有关某些鲨鱼的详细信息:
~/node_project/views/sharks.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>About Sharks</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="css/styles.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
</head>
<nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
<div class="container">
<button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
</button> <a class="navbar-brand" href="/">Everything Sharks</a>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav mr-auto">
<li class="nav-item"><a href="/" class="nav-link">Home</a>
</li>
<li class="active nav-item"><a href="/sharks" class="nav-link">Sharks</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="jumbotron text-center">
<h1>Shark Info</h1>
</div>
<div class="container">
<div class="row">
<div class="col-lg-6">
<p>
<div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
</p>
</div>
<div class="col-lg-6">
<p>
<div class="caption">Other sharks are known to be friendly and welcoming!</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
</p>
</div>
</div>
</div>
</html>
请注意,在此文件中,我们再次使用该类active
来指示当前页面。
完成后保存并关闭文件。
最后,创建您链接到的自定义 CSS 样式表index.html
,sharks.html
首先css
在views
目录中创建一个文件夹:
mkdir views/css
打开样式表:
nano views/css/styles.css
添加以下代码,它将为我们的页面设置所需的颜色和字体:
~/node_project/views/css/styles.css
.navbar {
margin-bottom: 0;
}
body {
background: #020A1B;
color: #ffffff;
font-family: 'Merriweather', sans-serif;
}
h1,
h2 {
font-weight: bold;
}
p {
font-size: 16px;
color: #ffffff;
}
.jumbotron {
background: #0048CD;
color: white;
text-align: center;
}
.jumbotron p {
color: white;
font-size: 26px;
}
.btn-primary {
color: #fff;
text-color: #000000;
border-color: white;
margin-bottom: 5px;
}
img,
video,
audio {
margin-top: 20px;
max-width: 80%;
}
div.caption: {
float: left;
clear: both;
}
除了设置字体和颜色外,此文件还通过指定max-width
80% 来限制图像的大小。这将防止它们在页面上占用超出我们预期的空间。
完成后保存并关闭文件。
应用程序文件到位并且项目依赖项安装完成后,您就可以启动该应用程序了。
如果您遵循了先决条件中的初始服务器设置教程,那么您将拥有一个仅允许 SSH 流量的活动防火墙。要允许流量到端口8080
运行:
sudo ufw allow 8080
要启动应用程序,请确保您位于项目的根目录中:
cd ~/node_project
使用以下命令启动应用程序node app.js
:
node app.js
在浏览器中访问http://your_server_ip:8080
。您将看到以下登录页面:
点击“获取鲨鱼信息”按钮。您将看到以下信息页面:
现在,您的应用程序已启动并运行。准备就绪后,请键入 退出服务器CTRL+C
。现在,我们可以继续创建 Dockerfile,以便根据需要重新创建和扩展此应用程序。
第 3 步 — 编写 Dockerfile
Dockerfile 指定应用程序容器执行时将包含哪些内容。使用 Dockerfile 可以定义容器环境,并避免依赖项或运行时版本不一致。
遵循这些关于构建优化容器的指导原则,我们将通过最小化图像层的数量并将图像的功能限制为单一目的(重新创建我们的应用程序文件和静态内容),使我们的图像尽可能高效。
在项目的根目录中,创建 Dockerfile:
nano Dockerfile
Docker 镜像是使用一系列相互依赖的分层镜像创建的。我们的第一步是添加应用程序的基础镜像,它将构成应用程序构建的起点。
我们来使用node:10-alpine
镜像,因为在撰写本文时,这是Node.js 的推荐 LTS 版本。该alpine
镜像源自Alpine Linux项目,有助于我们减小镜像大小。有关该alpine
镜像是否适合您的项目的更多信息,请参阅Docker Hub Node 镜像页面中“镜像变体”部分的完整讨论。
添加以下FROM
指令来设置应用程序的基础镜像:
~/node_project/Dockerfile
FROM node:10-alpine
此镜像包含 Node.js 和 npm。每个 Dockerfile 必须以一条FROM
指令开头。
默认情况下,Docker Node 镜像包含一个非 root节点用户,您可以使用该用户避免以root 身份运行应用程序容器。建议的安全做法是避免以root 身份运行容器,并将容器内的功能限制为仅运行其进程所需的功能。因此,我们将使用节点用户的主目录作为应用程序的工作目录,并将其设置为容器内的用户。有关使用 Docker Node 镜像的最佳实践的更多信息,请参阅此最佳实践指南。
为了微调容器中应用程序代码的权限,让我们在 目录中创建子目录node_modules
。创建这些目录将确保它们具有我们所需的权限,这在我们用 容器创建本地 Node 模块时非常重要。除了创建这些目录之外,我们还将它们的所有权设置为我们的Node用户:/home/node
app
npm install
~/node_project/Dockerfile
...
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
有关合并指令实用性的更多信息RUN
,请参阅有关如何管理容器层的讨论。
接下来,将应用程序的工作目录设置为/home/node/app
:
~/node_project/Dockerfile
...
WORKDIR /home/node/app
如果WORKDIR
未设置,Docker 将默认创建一个,因此最好明确设置它。
接下来,复制package.json
和package-lock.json
(适用于 npm 5+)文件:
~/node_project/Dockerfile
...
COPY package*.json ./
在运行或复制应用程序代码之前添加此COPY
指令npm install
,可以让我们利用 Docker 的缓存机制。在构建的每个阶段,Docker 都会检查是否有针对该特定指令的缓存层。如果我们更改了package.json
,则将重建该层;但如果没有更改,此指令将允许 Docker 使用现有的镜像层,并跳过重新安装节点模块的步骤。
复制项目依赖项后,我们可以运行npm install
:
~/node_project/Dockerfile
...
RUN npm install
将您的应用程序代码复制到容器上的工作应用程序目录:
~/node_project/Dockerfile
...
COPY . .
为了确保应用程序文件归非根节点用户所有,请将权限从应用程序目录复制到容器上的目录:
~/node_project/Dockerfile
...
COPY --chown=node:node . .
将用户设置为节点:
~/node_project/Dockerfile
...
USER node
8080
在容器上公开端口并启动应用程序:
~/node_project/Dockerfile
...
EXPOSE 8080
CMD [ "node", "app.js" ]
EXPOSE
不会公开端口,而是作为一种记录容器中哪些端口将在运行时公开的方式。CMD
运行命令来启动应用程序——在本例中为node app.js
。请注意,每个 Dockerfile 中只能包含一条CMD
指令。如果包含多条指令,则只有最后一条指令会生效。
Dockerfile 可以做很多事情。完整的操作说明列表,请参阅 Docker 的Dockerfile 参考文档。
完整的 Dockerfile 如下所示:
~/node_project/Dockerfile
FROM node:10-alpine
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package*.json ./
RUN npm install
COPY . .
COPY --chown=node:node . .
USER node
EXPOSE 8080
CMD [ "node", "app.js" ]
编辑完成后保存并关闭文件。
在构建应用程序镜像之前,让我们添加一个.dockerignore
文件。它的工作方式与.gitignore
文件类似,.dockerignore
指定项目目录中的哪些文件和目录不应复制到容器中。
打开.dockerignore
文件:
nano .dockerignore
在文件内,添加本地节点模块、npm 日志、Dockerfile 和.dockerignore
文件:
~/node_project/.dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
如果您正在使用Git,那么您还需要添加.git
目录和.gitignore
文件。
完成后保存并关闭文件。
现在,您可以使用该docker build
命令构建应用程序镜像了。使用-t
with 标志docker build
可以给镜像添加一个易记的名称。因为我们要将镜像推送到 Docker Hub,所以需要在标签中包含 Docker Hub 用户名。我们将镜像标记为nodejs-image-demo
,但您可以随意将其替换为您自己的名称。记住,也请将其替换your_dockerhub_username
为您自己的 Docker Hub 用户名:
docker build -t your_dockerhub_username/nodejs-image-demo .
指定.
构建上下文是当前目录。
构建镜像需要一两分钟。完成后,请检查镜像:
docker images
您将看到以下输出:
Output
REPOSITORY TAG IMAGE ID CREATED SIZE
your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 73MB
node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB
现在可以使用 来创建包含此镜像的容器docker run
。我们将在此命令中包含三个标志:
-p
:这会发布容器上的端口并将其映射到主机上的端口。我们将使用80
主机上的端口,但如果该端口上运行着其他进程,您可以根据需要修改它。有关其工作原理的更多信息,请参阅 Docker 文档中关于端口绑定的讨论。-d
:这将在后台运行容器。--name
:这使我们能够赋予容器一个令人难忘的名字。
运行以下命令来构建容器:
docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo
一旦你的容器启动并运行,你就可以使用以下命令检查正在运行的容器列表docker ps
:
docker ps
您将看到以下输出:
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo
容器运行后,您现在可以通过浏览器访问您的应用程序http://your_server_ip
。您将再次看到您的应用程序登录页面:
现在您已经为应用程序创建了映像,您可以将其推送到 Docker Hub 以供将来使用。
第4步 - 使用存储库处理图像
通过将应用程序镜像推送到 Docker Hub 等镜像仓库,您可以在构建和扩展容器时使用它。我们将演示如何将应用程序镜像推送到仓库,然后使用该镜像重新创建容器。
推送镜像的第一步是登录您在先决条件中创建的 Docker Hub 帐户:
docker login -u your_dockerhub_username -p your_dockerhub_password
以这种方式登录将~/.docker/config.json
使用您的 Docker Hub 凭据在用户主目录中创建一个文件。
现在,您可以使用之前创建的标签将应用程序映像推送到 Docker Hub your_dockerhub_username/nodejs-image-demo
:
docker push your_dockerhub_username/nodejs-image-demo
让我们通过销毁当前的应用程序容器和图像并使用存储库中的图像重建它们来测试图像注册表的实用性。
首先,列出正在运行的容器:
docker ps
您将看到以下输出:
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo
使用CONTAINER ID
输出中列出的内容,停止正在运行的应用程序容器。请务必将下面突出显示的 ID 替换为你自己的CONTAINER ID
:
docker stop e50ad27074a7
使用标志列出您的所有图像-a
:
docker images -a
您将看到以下输出,其中包含您的图像名称,your_dockerhub_username/nodejs-image-demo
以及该node
图像和您构建的其他图像:
Output
REPOSITORY TAG IMAGE ID CREATED SIZE
your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 73MB
<none> <none> 2e3267d9ac02 4 minutes ago 72.9MB
<none> <none> 8352b41730b9 4 minutes ago 73MB
<none> <none> 5d58b92823cb 4 minutes ago 73MB
<none> <none> 3f1e35d7062a 4 minutes ago 73MB
<none> <none> 02176311e4d0 4 minutes ago 73MB
<none> <none> 8e84b33edcda 4 minutes ago 70.7MB
<none> <none> 6a5ed70f86f2 4 minutes ago 70.7MB
<none> <none> 776b2637d3c1 4 minutes ago 70.7MB
node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB
使用以下命令删除已停止的容器和所有图像,包括未使用或悬空的图像:
docker system prune -a
在输出提示时输入y
,确认是否要删除已停止的容器和镜像。请注意,这也会删除你的构建缓存。
现在,您已删除运行应用程序镜像的容器以及镜像本身。有关删除 Docker 容器、镜像和卷的更多信息,请参阅如何删除 Docker 镜像、容器和卷。
删除所有镜像和容器后,您现在可以从 Docker Hub 中提取应用程序镜像:
docker pull your_dockerhub_username/nodejs-image-demo
再次列出您的图像:
docker images
您将看到您的应用程序图像:
Output
REPOSITORY TAG IMAGE ID CREATED SIZE
your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 73MB
您现在可以使用步骤 3 中的命令重建容器:
docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo
列出正在运行的容器:
docker ps
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "node app.js" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo
http://your_server_ip
再次访问以查看您正在运行的应用程序。
结论
在本教程中,您使用 Express 和 Bootstrap 创建了一个静态 Web 应用程序,并为其创建了一个 Docker 镜像。您使用此镜像创建了一个容器,并将该镜像推送到 Docker Hub。之后,您可以销毁镜像和容器,并使用 Docker Hub 仓库重新创建它们。
如果您有兴趣了解有关如何使用 Docker Compose 和 Docker Machine 等工具创建多容器设置的更多信息,可以查看以下指南:
有关使用容器数据的一般提示,请参阅:
如果您对其他与 Docker 相关的主题感兴趣,请参阅我们完整的Docker 教程库。
本作品采用Creative Commons Attribution-NonCommercial-ShareAlike 4.0 国际许可协议进行许可
文章来源:https://dev.to/digitalocean/how-to-build-a-nodejs-application-with-docker-29h9