返回博客

如何在 Ubuntu 20.04 上使用 Docker 部署 Node.js (Express.js) 应用

如何在 Ubuntu 20.04 上使用 Docker 部署 Node.js (Express.js) 应用

介绍

Docker 是一个容器平台,它是一个轻量级、虚拟化、可移植、软件定义的标准化环境。它允许软件与运行在物理宿主机上的其他软件隔离运行。Docker 是软件开发中持续开发和集成方面的一个决定性组件。它提供了一个 虚拟机的轻量级替代方案,并允许开发人员享受分布式应用架构。为了深入了解 Docker 生态系统的概览,请查看这篇文章.

使用 Docker 构建应用程序的过程始于开发人员为其应用程序创建镜像。然后,该镜像将被部署在容器中。镜像包含应用程序的定义组件,例如应用程序代码、库、配置文件、环境变量和运行时环境。镜像标准化了容器内部的环境,从而赋予了容器化可移植性的特征。 Node.js 是一个开源、跨平台的后端 JavaScript 运行时环境,可以在浏览器之外执行 JavaScript 代码。它构建在 Chrome 的 V8 JavaScript 引擎. Express.js 是一个运行在 Node.js 之上的极简后端 JavaScript 框架。

在本教程中,我们将为运行在 Express 框架上的网站创建一个镜像。我们将使用 Bootstrap(一个前端库)来让前端看起来更好看。创建镜像后,我们将构建一个容器并将其推送到 Docker Hub。Docker Hub 允许开发人员托管容器化应用程序,以便轻松部署到任何 Docker 环境。一旦您的容器托管在 Docker Hub 上,我们将拉取它并构建另一个实际用于运行我们网站的镜像。

前提条件

这是一个动手实践教程。您应该创建一个能够让您跟着操作的环境。

步骤 1:配置应用程序依赖项

在创建镜像之前,您需要创建应用程序源代码。应用程序源代码包括将被复制到容器中的代码、静态内容和依赖项。首先在非 root 用户的家目录中为您的项目创建一个目录。我们将其命名为 node_express,但您也可以自由使用您喜欢的目录名称:

接下来,进入该目录:

这将是您的应用程序根目录。一个 node.js 应用程序需要根目录中包含一个 package.json 文件。Npm 使用此文件来确定您的应用程序需要哪些依赖项。输入以下命令来创建此文件:

之后,将以下代码片段添加到文件中。您可以根据需要更新名称、作者、描述和入口点文件:

如您所见,此文件指定了项目名称、版本、作者以及共享应用程序代码所依据的许可协议。建议为您的项目使用简短且具有描述性的名称,以避免在 npm 注册表 中出现重复。我们为该项目指定了 ISC 许可协议,它允许免费复制、修改或分发应用程序代码。

最重要的是,您应该注意文件中的以下指令:

  • main”:此指令指定了应用程序的入口点,我们将其设置为 index.js。我们稍后将创建此文件。
  • dependencies”:此指令指定了当我们运行 npm 命令时将从 npm 注册表拉取的应用程序依赖项,在我们的案例中,我们需要 Express 4.17.1 及以上版本。

现在您可以通过按 Ctrl + O 保存文件。然后,通过按 Ctrl + X 关闭文件。接下来,我们将通过运行以下命令来安装依赖项:

该命令会安装 package.json 文件中指定的应用程序依赖项,并将其放入 node_modules 目录中。这些目录在您首次运行该命令时已自动创建。安装好应用程序依赖项后,您现在可以开始添加应用程序代码了。

第 2 步:添加您的应用程序代码文件

我们将创建一个基础的食谱网站,这要感谢 allrecipes。该应用程序的主要入口点是 index.js 文件。我们将添加一个 views 目录,用于存放项目的各种页面和静态资源。该网站将有一个引导页,其中包含介绍性信息和一些食谱的链接。

我们的引导页代码将放在 home.html 文件中。首先,通过输入以下命令创建 index.js 文件:

添加以下代码,该代码导入并创建了一个 Express 应用程序。它还指定了 Router 对象、基础目录以及提供此应用程序服务的端口:

require 是一个加载模块的 JavaScript 函数。在这种情况下,我们正在加载 express 模块。然后,我们将使用导入的模块来创建 express 和 router 对象。随着教程的深入,router 对象将通过响应添加到该对象中的 HTTP 方法调用来执行应用程序的路由功能。

我们还设置了 pathport。path 常量定义了代码的基础目录。在我们的案例中,它是项目根目录下的 views 子目录。 port 指定了 express 应用程序应该监听的端口,在我们的示例中,我们将其设置为 8090.

一旦我们有了这些常量,我们就可以使用 router 对象为应用程序指定一些路由。将以下代码添加到 index.js 文件中以指定路由:

您可以 添加中间件 到路由,通过使用 router.use 函数。在这种情况下,我们添加了一个函数,在将路由请求传递给应用程序路由之前对其进行记录。对应用程序根目录的 GET 请求将返回 home.html 页面。然后,我们为三个食谱添加了页面,这些页面也将通过对特定食谱页面的 GET 请求来检索。

最后,添加以下代码以挂载 router 中间件和应用程序静态资源。此外,告诉 express 应用程序监听端口 8090:

您完整的 index.js 文件应该像这样:

您现在可以保存并关闭该文件。下一步是将静态网页添加到 views 目录。首先输入以下命令来创建该目录:

输入以下命令以打开 home.html 落地页文件:

将以下代码添加到文件中。该代码导入了 Bootstrap,并向网站访问者提供一些关于该网站主题的信息:

除了导入 Bootstrap 之外,该页面还添加了一个基本的 导航菜单 ,以帮助我们在页面之间跳转并返回到主页。我们还添加了一行来导入我们的自定义 CSS 文件:

稍后我们将使用此文件为应用程序添加自定义样式。现在,让我们为食谱创建三个页面。我们首先从创建 千层面 页面开始。使用以下命令用 nano 编辑器打开该文件:

在打开的文件中,添加以下代码。此文件将导入 Bootstrap、custom.css 文件,指定一个导航菜单并提供一些千层面食谱信息:

让我们按照相同的步骤为 牛油果酱 食谱页面创建一个文件。运行以下命令,使用 nano 打开该文件:

然后将此代码添加到文件中:

最后,让我们创建 banana_bread.html 文件,输入以下命令:

然后,将以下 HTML 代码添加到文件中:

现在,我们已经创建了所有页面。如果你还记得,我们还要添加 css/custom.css 文件。输入以下命令来创建该目录:

然后使用以下命令在 nano 编辑器中创建并打开该文件:

你可以根据需要添加更多 CSS 代码来美化你的网站。为简便起见,让我们将以下代码片段添加到文件中:

完成后保存并关闭文件。

由于我们现在已经安装了应用程序源代码和项目依赖项,因此可以启动该应用程序。

我们已将应用设置为监听端口 8090,运行以下命令以指示防火墙允许流量通过该端口。如果你指定了其他端口,请替换命令中的端口号:

现在,你可以启动应用程序。但首先,请运行以下命令确保你处于项目根目录中:

使用 node index.js 启动应用程序。如果你指定了不同的入口点,请将其替换为你的入口点:

如果你在浏览器中导航到 http://your_public_server_ip:8090,你将看到定义的食谱(Recipes)着陆页:

Recipes

 

你可以在导航中看到指向各种食谱的链接。让我们点击一些。下面是 Lasagna 食谱页面:

Node.js app install on Ubuntu 1

这里是 Guacamole 食谱页面:

Guacamole

至此,你已经创建了应用程序并测试了其是否按预期工作。你可以通过按 Ctrl + C 退出服务器,然后继续创建 Dockerfile。Dockerfile 有助于实现可扩展性,因为它可以在需要时重新创建应用程序实例。

第 3 步:创建 Dockerfile

Docker 在构建镜像时会读取 Dockerfile 中指定的指令。它指定了应用程序的运行环境。因此,它有助于开发人员避免依赖项不一致或运行版本更改的问题。输入以下命令来创建 Dockerfile:

Docker 镜像是使用相互构建的多个镜像层创建的。首先,您需要添加一个基础镜像,作为应用程序的起点。

由于该应用程序需要在 node.js 环境中运行,我们将首先添加 node:10-alpine image ,用于 node.js。目前,在编写本教程时,这是 recommended LTS version of Node.js。我们选择这个特定镜像是因为它源自 Alpine Linux project。因此,它将有助于将我们的镜像大小保持在最低限度。在 Docker Hub Node images page 下有几个镜像变体,您可以根据需要进行选择。

添加以下代码,使用 FROM 指令来设置应用程序的基础镜像:

该镜像包含 Node.js 和 npm。每个 Dockerfile 必须以 FROM 指令开始。Docker node 镜像默认带有一个非 root 的 node 用户,您可以使用它来运行您的应用程序容器,而不是以 root 身份运行。Docker security recommends 不要以 root 身份运行容器,并将权限限制为仅运行其资源所需的权限。

在这种情况下,我们将使用 node 用户的主目录作为应用程序的工作目录,并作为容器内的用户。您可以查看此 Docker Node image best practices 指南以获取更多信息。

我们将在 node_modules 子目录,连同 app 目录一起创建在 /home/node 内部,以帮助简化应用程序代码的权限。创建这些目录可以确保在我们在容器内部本地运行 npm install 命令时,它们具有正确的权限。创建目录后,您必须将它们的所有权设置为 node 用户。我们将在 Dockerfile 中通过添加以下行来实现这一点:

然后,您将通过添加以下行来设置工作目录:

始终设置 WORKDIR 是一个好主意,这样 Docker 就不必默认创建它。

添加以下行以复制 package.jsonpackage-lock.json 文件:

建议在运行 COPY 指令之前添加 npm install 或复制应用程序源代码。这使您能够利用 Docker 的缓存机制。在构建过程中,Docker 会检查是否为每个指令缓存了一个层。这意味着如果您没有更改 package.json 文件,那么 Docker 将使用现有的镜像层,并避免重新安装 node modules,从而加快构建过程。

在运行 npm install 之前,添加以下行以将用户切换为 node,以确保所有应用程序文件和 node_modules 目录都归非 root 的 node 用户所有:

我们的容器现在已准备好运行 npm install 命令。将以下行添加到 Dockerfile 中:

一旦安装了 node_modules,添加以下行,这将指示 Docker 将应用程序代码复制到容器上的应用程序目录中,并具有正确的权限和所有权,即非 root 的 node 用户:

最后一步是公开容器上的端口 8090,正如我们在入口 index.js 文件中所定义的那样:

EXPOSE 设置容器上的哪些端口将在运行时打开。 CMD 运行启动应用程序的命令,在本例中为 node index.js。

Dockerfile 中只能有一个 CMD 命令,因为只有最后一个才会生效。请查看Dockerfile 参考文档,以获取您可以使用 Dockerfile 执行的操作列表。

您完整的 Dockerfile 应该像这样:

您现在可以保存并关闭该文件。

接下来您要做的就是添加 .dockerignore 文件。就像 .gitignore 文件一样,.dockerignore 指定了项目目录中哪些文件和目录不应该被复制到容器中。

使用 nano 编辑器打开文件:

在文件内添加以下行:

如果使用的是 git 仓库,那么您还应该添加 .git 目录和 .gitignore 文件。保存并关闭文件。

如果一切顺利,现在是时候使用 docker build 命令来构建应用程序镜像了。您可以将 –t 标志添加到 docker build 命令中,以便为镜像标记一个易于记忆的名称,而不是 docker 默认设置的随机字符串。我们还将把镜像推送到 Docker Hub,因此最好在标签中包含您的 Docker Hub 用户名。

我们将使用 nodejs-express-image 作为标签名称。您可以自由选择自己喜欢的标签名称。以下是构建镜像的命令:

请记住将 your_dockerhub_username 替换为您实际的 Docker Hub 用户名。末尾的 .(点)指定构建上下文为当前目录。

构建过程需要一到两分钟。完成后,输入命令以检查您的镜像:

您应该会看到类似以下的内容:

sudo docker images

请记住,我们已将 your_dockerhub_username 替换为实际的用户名。

确认镜像已构建后,您现在可以使用 docker run 创建一个带有该镜像的容器。将包含以下标志:

  • -p:发布容器上的端口并将其映射到主机系统上的端口。出于演示目的,我们将在主机系统上使用端口 80。但是,如果您在该端口上运行了另一个进程,请根据需要进行修改。了解更多关于 Docker 文档中的端口绑定.
  • -d:用于后台模式。允许容器在后台继续运行。
  • --name:您可以使用它来设置一个易于记忆的名称,而不是让 Docker 分配一个随机字符串。

构建容器的命令如下。请适当替换您的 Docker Hub 用户名:

等待容器构建并开始运行。您可以使用此命令检查所有正在运行的容器:

您应该会看到类似于以下内容的输出:

Node.js app install on Ubuntu 3

如输出所示,容器现在正在运行。如果您在浏览器中访问服务器的公共 IP 地址(不带端口),则可以在浏览器中查看它。您的首页将会加载:

awesome recipe

 

您已成功使用 Docker 部署了 Node Express 静态网站。让我们看看如何将此镜像推送到 Docker Hub,以便将来使用和扩展。

步骤 4:使用 Docker 镜像仓库

您可以将镜像推送到像 Docker Hub 这样的镜像仓库中,并保存它们以备将来使用、与其他开发人员共享或允许扩展您的容器。我们可以将创建的镜像推送到 Docker Hub,并使用它来重新创建容器。

使用以下命令登录您的 Docker Hub 帐户。将其替换为您实际的 Docker Hub 用户名:

在提示时输入您的密码。登录后,将在您的用户主目录下创建一个 ~/.docker/config.json 文件,其中包含您的 Docker Hub 凭据。

设置完成后,输入以下命令将镜像推送到 Docker Hub,并指定您之前构建镜像时设置的标签:

此命令将 docker 镜像推送到您的 Docker Hub 帐户。如果您访问您的帐户,您可以看到您最近推送的镜像:

Docker Hub

我们可以通过销毁当前的应用程序容器并使用仓库中的镜像重新构建它,来测试镜像仓库的实用性。

输入以下命令列出您当前的容器:

您应该会看到类似于以下的输出:

Docker Hub

注意输出中列出的 CONTAINER ID,复制它,并使用以下命令停止您的容器,将 ID 替换为您的 ID:

输入以下命令列出您系统中所有可用的 docker 镜像:

输出将显示您的镜像名称、node.js 镜像以及构建过程中的其他镜像。

输入以下命令删除镜像,包括未使用的或悬空的镜像:

输入 y 以确认。这将删除已停止的容器和镜像。如果您列出它们,您将在输出中看到一个空列表:

output

现在,您已经删除了运行应用程序的容器以及镜像本身。通过阅读我们的教程,了解更多关于删除 Docker 容器、镜像和数据卷.

我们现在可以通过首先使用以下命令从 Docker Hub 拉取镜像来重新创建整个过程。请相应地替换您的 Docker Hub 用户名:

再次使用以下命令列出您的 Docker 镜像:

您应该在输出中看到该镜像:

sudo docker

您现在可以使用来自 步骤 3 的命令重新构建您的容器。当然,请在适当的地方替换您的 Docker Hub 用户名:

列出您的容器以确认它已被重新构建:

您应该会看到类似的输出:

在浏览器中,导航到您服务器的公网 IP 地址,您应该会看到您的应用程序正在运行。

结论

如果您已经按照教程进行到这一步,那么您现在已经拥有了一个使用 Express 和 Bootstrap 制作并使用 Docker 部署的静态网站。您使用静态网站文件构建了 Docker 镜像,并使用该镜像创建了容器。然后,您将镜像推送到 Docker 镜像仓库 Docker Hub,使其可用于将来使用或扩展。为了测试镜像仓库的使用,您销毁了镜像和容器,从仓库中拉取了镜像,并重新构建了容器。

本教程解释了如何部署 Node.js 应用程序。如果您想了解如何使用不同的 Web 开发技术栈,我们有一篇关于在 Nginx 上使用 Docker Compose 部署 Laravel 应用程序.

有关利用 Docker 的更多资源,请查看以下教程:

祝您计算愉快!

author

Hark Labs

作者 · CloudSigma

Preslav Dobrev 是 CloudSigma 的创意设计师,专注于通过传统和创新营销渠道打造一致的企业形象。他擅长将艺术愿景与战略营销相融合,创造具有影响力的品牌叙事。

评论

暂无评论。发表第一条评论吧。