一个简单的 Node.js Docker 工作流程
Docker 是一款出色的工具,它可以帮助开发人员以标准化的方式更高效地构建、部署和运行应用程序。我们可以在与生产环境相同的环境中进行开发。通过在本地使用相同的设置,您可以加快调试速度,甚至预防即将出现的错误。在上一篇文章中,我介绍了一种使用 Docker 进行前端开发的简化方法,现在我将介绍如何在 Node.js 项目中使用 Docker。
应用程序
举个例子,我组装了一个基本的应用程序,并尽量保持其简洁。如果您喜欢自己尝试,可以克隆代码库并进行修改,看看效果如何。
// src/index.js
'use strict';
const express = require('express');
const port = process.env.PORT || 3000;
const app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`App listening on port ${port}!`));
该应用程序由一个文件组成,该文件启动 Web 服务器并响应请求。我使用了著名的Express Web 框架来响应请求,并通过环境变量设置了端口。我们需要它可配置,因为这个端口可能会与开发中使用的端口不同。
发展
为了发展,我们希望
- 与生产环境相同
- 轻松设置环境
- 在浏览器中自动查看文件更改
- 使用编辑器中的代码完成功能
为了满足所有要求,我们将使用 Docker 和 Docker Compose 为开发和生产创建一个相同的容器,并使用Nodemon 包在文件更改时重新启动应用程序。
我们可以将启动脚本从 更改为 ,以便在文件更改时重新启动node src/index.js
。nodemon --watch src src/index.js
它的功能与以前相同,并且每当src
文件夹内的文件发生变化时都会重新启动。
让我们进入更令人兴奋的部分,在本地启动容器。
# docker-compose.yml
version: "3"
services:
server:
image: node:12
working_dir: /app
volumes:
- ./:/app
ports:
- 3000:3000
environment:
- PORT=3000
command: sh -c "npm install && npm run dev"
您可能首先注意到的是,Docker Compose 配置文件不包含自定义 Docker 镜像。大多数情况下,我们不需要它,但如果需要,我们可以通过build
属性添加它。在我们的设置中,我们将使用 Node 基础镜像。
我没有选择复制 Dockerfile 中的文件,而是选择了使用 进行双向文件同步volumes
。虽然这比复制文件更耗费资源,但安装的 NPM 包会出现在主机上,从而支持代码补全,这使得它成为一种明智的选择。
我们不应该想当然地认为所有事情都是理所当然的:我们设置了可配置的环境变量。在我们的例子中,端口是可配置的,服务器监听传入调用的位置。在配置中设置它使其更具可读性,因为它位于ports
定义旁边:我们声明希望在主机上暴露哪些内部容器端口。
最后一步是使用command
属性启动应用程序。我们始终运行该npm install
命令,这可能会稍微影响启动性能,但也能确保依赖项在容器运行时仍然保持最新。您可以将其从 中移除command
,但这样一来,您必须在启动容器之前或文件内容发生package.json
更改时手动运行它。
生产
我们可以使用之前的设置顺利地开发应用程序,但我们还必须创建一个可部署到生产环境的容器。目前,无法再推迟创建自定义 Docker 镜像。让我们看看如何才能创建一个最佳的镜像。
# Dockerfile
FROM node:12 AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:12-alpine
WORKDIR /app
COPY --from=base /app .
COPY . .
EXPOSE 3000
CMD npm start
该文件首先声明了起始镜像,称为“base”。命名并非必需,但在使用 Dockers多阶段构建时,它能起到很大的作用。
我们只需复制软件包文件,因为它们对于安装开发中使用的相同版本是必需的。命令npm install
更改为npm ci --only=production
。它有两个主要区别。npm ci
它会安装锁定文件中定义的相同版本,并且不会像npm install
一样尝试更新它们。第二个区别是--only=production
跳过 的安装的标志devDependencies
,我们在生产环境中不需要它。
通过跳过 ,我们节省了大量宝贵的镜像空间devDependencies
,但镜像仍然很大(大约 500 MB)。Node 有一个更小的镜像,名为 alpine,它只包含必要的软件包:更少的软件包意味着更少的磁盘空间和内存,更快的速度和更高的安全性。软件包安装有时需要标准镜像,但使用 Docker 多阶段构建,我们可以在软件包安装完成后切换到较小的镜像,并复制上一步中的软件包。这样,我们就能兼得两全其美:小巧的镜像和安装任何软件包的能力。
如果我们使用 查看镜像的大小docker images
,我们可以看到它已经缩小到 100 MB 以下。镜像已准备就绪;我们可以将其部署到生产环境中。
概括
起初,我并不觉得为什么要用另一种开发必需的技术来让我的日常生活变得复杂。其他人不得不向我证明,同步文件夹后,volumes
我根本分辨不出在本地机器上开发的区别。之后,再加上我可以在本地计算机上针对相同的基础架构进行测试,他们说服了我每天使用 Docker。我希望上述工作流程也能帮助其他人爱上 Docker 的优势。
文章来源:https://dev.to/emarsys/a-simple-node-js-docker-workflow-18dh