如何使用Docker构建Node.js应用程序
介绍
Docker 平台允许开发人员将应用程序打包和运行为 容器。 容器是在共享操作系统上运行的隔离进程,为虚拟机提供了更轻量级的替代方案。 尽管容器并不新鲜,但它们提供的好处——包括进程隔离和环境标准化——随着越来越多的开发人员使用分布式应用程序架构而变得越来越重要。
使用 Docker 构建和扩展应用程序时,起点通常是为您的应用程序创建一个映像,然后您可以在容器中运行该映像。 该映像包括您的应用程序代码、库、配置文件、环境变量和运行时。 使用映像可确保容器中的环境是标准化的,并且仅包含构建和运行应用程序所必需的内容。
在本教程中,您将为使用 Express 框架和 Bootstrap 的静态网站创建应用程序映像。 然后,您将使用该映像构建一个容器并将其推送到 Docker Hub 以供将来使用。 最后,您将从 Docker Hub 存储库中提取存储的映像并构建另一个容器,演示如何重新创建和扩展应用程序。
先决条件
要遵循本教程,您将需要:
- 一台 Ubuntu 18.04 服务器,按照 Initial Server Setup guide 进行设置。
- 按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2 安装在您的服务器上的 Docker。
- 安装了 Node.js 和 npm,遵循 这些关于使用由 NodeSource 管理的 PPA 进行安装的说明。
- 一个 Docker Hub 帐户。 有关如何设置的概述,请参阅 这篇介绍 关于 Docker Hub 入门。
第 1 步 — 安装您的应用程序依赖项
要创建映像,您首先需要制作应用程序文件,然后您可以将其复制到容器中。 这些文件将包括您的应用程序的静态内容、代码和依赖项。
首先,在非 root 用户的主目录中为您的项目创建一个目录。 我们将调用我们的 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
const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
const port = 8080;
require 函数加载 express 模块,然后我们用它来创建 app 和 router 对象。 router 对象将执行应用程序的路由功能,并且当我们定义 HTTP 方法路由时,我们会将它们添加到该对象以定义我们的应用程序将如何处理请求。
该文件的这一部分还设置了几个常量,path 和 port:
path:定义基目录,即当前项目目录中的views子目录。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 页面,而对 /sharks 路由的 GET 请求应返回 [X199X ]。
最后,挂载 router 中间件和应用程序的静态资产,并告诉应用程序监听端口 8080:
~/node_project/app.js
...
app.use(express.static(path));
app.use('/', router);
app.listen(port, function () {
console.log('Example app listening on port 8080!')
})
完成的 app.js 文件将如下所示:
~/node_project/app.js
const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
const port = 8080;
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(port, 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>
此处的顶级 navbar 允许用户在 Home 和 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>
...
此外,我们在 jumbotron 的按钮中创建了指向鲨鱼信息页面的链接:
~/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 类来指示当前页面。
完成后保存并关闭文件。
最后,通过首先在 views 目录中创建一个 css 文件夹来创建您在 index.html 和 sharks.html 中链接到的自定义 CSS 样式表:
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 image,因为在撰写本文时,这是 推荐的 Node.js 的 LTS 版本。 alpine 图像源自 Alpine Linux 项目,将帮助我们减小图像大小。 有关 alpine 映像是否适合您的项目的更多信息,请参阅 Docker Hub 节点映像页面的 Image Variants 部分下的完整讨论[ X210X]。
添加以下 FROM 指令来设置应用程序的基础镜像:
~/node_project/Dockerfile
FROM node:10-alpine
此图像包括 Node.js 和 npm。 每个 Dockerfile 必须以 FROM 指令开头。
默认情况下,Docker 节点映像包含一个非 root node 用户,您可以使用该用户来避免以 root 身份运行应用程序容器。 建议的安全做法是避免以 root 身份运行容器,并 将容器 内的功能限制为仅运行其进程所需的功能。 因此,我们将使用 node 用户的主目录作为我们应用程序的工作目录,并将它们设置为容器内的用户。 有关使用 Docker 节点映像时的最佳实践的更多信息,请参阅此 最佳实践指南 。
为了微调容器中应用程序代码的权限,让我们在 /home/node 和 app 目录中创建 node_modules 子目录。 创建这些目录将确保它们具有我们想要的权限,这在我们使用 npm install 在容器中创建本地节点模块时非常重要。 除了创建这些目录之外,我们还将它们的所有权设置给我们的 node 用户:
~/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 ./
在运行 npm install 或复制应用程序代码之前添加这条 COPY 指令可以让我们利用 Docker 的缓存机制。 在构建的每个阶段,Docker 都会检查它是否为该特定指令缓存了一个层。 如果我们更改 package.json,该层将被重建,但如果我们不这样做,该指令将允许 Docker 使用现有的镜像层并跳过重新安装我们的节点模块。
为确保所有应用程序文件为非root node用户所有,包括node_modules目录的内容,运行前切换用户为node npm install:
~/node_project/Dockerfile
... USER node
复制项目依赖并切换用户后,我们可以运行npm install:
~/node_project/Dockerfile
... RUN npm install
接下来,将具有适当权限的应用程序代码复制到容器上的应用程序目录:
~/node_project/Dockerfile
... COPY --chown=node:node . .
这将确保应用程序文件归非 root 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 ./ USER node RUN npm install COPY --chown=node: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 标志与 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
您将看到以下输出:
OutputREPOSITORY 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
您将看到以下输出:
OutputCONTAINER 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
出现提示时,输入您的 Docker Hub 帐户密码。 以这种方式登录将使用您的 Docker Hub 凭据在用户的主目录中创建一个 ~/.docker/config.json 文件。
您现在可以使用之前创建的标签 your_dockerhub_username/nodejs-image-demo 将应用程序映像推送到 Docker Hub:
docker push your_dockerhub_username/nodejs-image-demo
让我们通过销毁我们当前的应用程序容器和镜像并使用存储库中的镜像重建它们来测试镜像注册表的实用性。
首先,列出您正在运行的容器:
docker ps
您将看到以下输出:
OutputCONTAINER 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 图像和构建中的其他图像:
OutputREPOSITORY 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
您将看到您的应用程序图像:
OutputREPOSITORY 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
OutputCONTAINER 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 等工具创建多容器设置的更多信息,可以查看以下指南:
- 如何在 Ubuntu 18.04 上安装 Docker Compose。
- 如何在 Ubuntu 18.04 上使用 Docker 机器配置和管理远程 Docker 主机。
有关使用容器数据的一般提示,请参阅:
如果您对其他与 Docker 相关的主题感兴趣,请参阅我们完整的 Docker 教程库。