相关文章推荐
注册/登录

使用Docker Compose改善Node.js的开发

开发 前端
本文中,我们以Express.js为一个示例展开,需要实现了解一丁点Node.js和npm的基础知识。还要了解Express.js框架的基础知识。

在过去的几年中,Docker和Node.js都变得非常流行。对于开发人员来说利用这些新技术来改善自己的开发体验很有必要,而且在此过程中还可以学习新技术。遵循"Coding到老,学习到老,折腾到老"的宗旨,本文我们将介绍将如何结合Node.js与Docker来改善开发人员体验,包括使用docker build和利用Docker Compose来实现无缝的本地前端开发环境。

[[375856]]

本文中,我们以Express.js为一个示例展开,需要实现了解一丁点Node.js和npm的基础知识。还要了解Express.js框架的基础知识。

对Docker也要有一定的概念和要会基础操作(不会也没关系,很容易)。

最后本文全程使用Linux(Mac) shell终端命令行。

创建Express.js项目

为了要生成示范应用程序,需要使用Express应用程序生成器。需要运行以下npx命令行:

  1. npx express-generator --view=pug --git <app-name> 

Express生成器将生成Express应用。--view=pub选项表示使用pug视图引擎。--git表示用来给项目添加一个git .gitignore文件。

生成效果如下:

测试Express应用

要测试该应用程序,需要运行npm install安装所有必需的npm模块。然后,运行以下命令以启动应用程序:

  1. DEBUG=nodejs-docker-express:* npm start 

如果没有异常,应该会到一条类似的消息。

  1. nodejs-docker-express:server Listening on port 3000 

上面的命令非常简单:它运行一个环境变量DEBUG=nodejs-docker-express,用来表示服务器进行详细的调试。

对Windows系统,使用的参数要修改为:

  1. set DEBUG=nodejs-docker-express:* & npm start 

现在打开浏览器,在地址栏并输入localhost:3000并访问:

这样示例的Express.js应用就已经在运行OK了。是不是非常简单?有此基本的"Hello,World!"为基础,我们进一步深入。

Docker多阶段构建

容器化应用程序有很多好处:首先,无论运行平台是什么,其行为都相同。借助Docker容器,应用程序可以轻松部署到各个公有容器云(比如AWS Fargate,Google Cloud Run),自建的K8S集群中,甚至本地docker上。

容器化,基础是Dockerfile。Dockerfile是构建Docker镜像的基础。用Dockerfile编译生成的镜像运行时,就称之为容器。

如图示,整个过程非常简单:从Dockerfile构建Docker镜像。运行镜像,得到运行时容器。

Dockerfile

Dockerfile有一些类似命令行的语句:

  1. FROM node:14-alpine as base 
  2. WORKDIR /src 
  3. COPY package*.json / 
  4. EXPOSE 3000 
  5. FROM base as production 
  6. ENV NODE_ENV=production 
  7. RUN npm ci 
  8. COPY . / 
  9. CMD ["node", "bin/www"] 
  10.  
  11. FROM base as dev 
  12. ENV NODE_ENV=development 
  13. RUN npm install -g nodemon && npm install 
  14. COPY . / 
  15. CMD ["nodemon", "bin/www"] 

通过Docker镜像的分层继承,创建了一个精简的production镜像和一个功能更丰富,以开发为重点的dev镜像。

在Dockerfile中,使用了多阶段构建,整个过程分为三个阶段:base,production和dev。production和dev依赖于base,base为node:14-alpine的基础镜像,该基础镜像需要从DockerHub获取,这是一个官方Alpine基础OS的Node.js官方镜像,主镜像为345MB,Node.js镜像大概不到40M。

  1. WORKDIR /src 
  2. COPY package*.json / 
  3. EXPOSE 3000 

WORKDIR语句设置了Docker运行的工作目录,其后的命令都在该工作目录运行。COPY语句,复制package*.json(package.json和package-lock.json)容器中。

EXPOSE语句,设置Node.js Express Web服务器的监听端口。上述步骤对于开发和生产阶段都是通用的。

现在我们来看看生产目标阶段是如何构建的。

production

在生产阶段,继续从基础阶段开始的工作,FROM语句指示Docker从base开始。ENV语句设置Docker将环境变量NODE_ENV为production。

  1. FROM base as production 
  2. ENV NODE_ENV=production 
  3. RUN npm ci 
  4. COPY . / 
  5. CMD ["node", "bin/www"] 

变量ENV设置为production可以使性能提高三倍,并且提供一些其他优化,比如缓存视图。npm install命令只会安装主要依赖项,忽略开发依赖项。这些设置非常适合生产环境。

接着使用RUN语句运行npm ci而非npm install。npm ci适用于持续集成和部署。和npm install相比,会绕过某些面向用户的功能。当然,npm ci需要一个package-lock.json文件才能工作。

之后,还是使用COPY语句将代码复制到工作目录。

最后使用CMD语句,运行Node应用服务器和/srcbin/www

我们利用了多阶段构建,并在开发阶段添加开发所需的组件:

  1. FROM base as dev 
  2. ENV NODE_ENV=development 
  3. RUN npm install -g nodemon && npm install 
  4. COPY . / 
  5. CMD ["nodemon", "bin/www"] 

大体上和生产极端类似,差异为NODE_ENV环境变量设置为development。

接着,用RUN语句安装nodemon。每当文件更改时,nodemon都会重新启动服务器,从而开发体验更加流畅。同时执行npm install,该命令会递归安装dev依赖项。例如,如果要使用Jest测试应用程序,那将是开发依赖项之一。

请注意,这两个命令通过&&放在一起,创建更少的Docker层,于构建缓存非常有用。这是撰写Dockerfile时候常用的一个技巧。

和生产阶段相同,将代码复制到容器。但是,用nodemon取代了Node服务器,这样在每次文件/src更改时会重新启动它。

.dockerignore

和git的.gitignore一样,docker也使用.dockerignore来忽略不想放入Docker镜像的文件。通过忽略无关的文件更改,它有助于使Docker镜像保持身材,而且能使构建缓存更高效。本示例中.dockerignore

  1. .git 
  2. node_modules 

非常简单,告诉Docker不COPY.git文件夹和node_modules从主机复制到Docker容器。

使用Docker Compose

到目前为止,我们创建一个使用运行Node.js Express应用程序Docker所需的大部分功能。为了更便捷,我们还建议用Docker Compose,这样可以更轻松地使用单个或多个容器运行应用程序。这样也无需要记住很长的命令来构建或运行容器。只需通过:

  1. docker-compose build 
  2. docker-compose up 

但是docker-compose使用yml的配置文件和dockerfile略有不同:

上述,我们指定Docker Compose的版本,在本例中为3.8,对应Docker引擎19.0.3支持的最新版本。这样可以支持多阶段Docker构建。

接着,指定正在使用的服务。在本教程中,只有一个名为web的服务,具有context为当前目录的构建以及一个重要的构建参数target设置为dev。这告诉Docker在dev阶段构建Docker映像。

之后,通过volumes制定 Docker卷。它指示Docker从Docker容器上的./和主机本地/src目录复制和同步更改。当我们在主机中更改文件时,这将很有用,并且文件也将立即反映到容器中。

command语句运行npm run start:dev,start:dev执行内容定义在package.json,内容为:

  1. "start:dev": "nodemon ./bin/www" 

表示使用nodemon启动Web服务器。在开发环境中,可以在每次保存文件时重新启动服务器。

接下来,用ports语句设置docker端口映射主机的3000端口与容器3000端口。在构建容器时,公开了端口3000, Web服务器就会在3000上运行。

最后,设置了两个环境变量。首先,将其NODE_ENV设置为development,因为这样可以看到详细的Debug信息,也没有任何视图缓存。然后,将debug设置为*,让Web服务器打印出所有内容的详细调试消息。

测试应用程序

前面,设置了弄好了基础构建配置文件,接着构建Docker镜像。使用BuildKit优化Docker构建。启用BuildKit可以更快地构建Docker镜像,运行以下命令:

  1. COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build 

该命令告诉Compose在BuildKit上构建Docker镜像。它应该在一段时间内运行并构建Docker镜像,如下所示:

Docker镜像大约在14秒内构建完成,使用BuildKit可以更快。运行该镜像:

docker-compose up

然后浏览器访问localhost:3000:

这样我们,自配置的应用程序在Docker上已经完美运行。我们来改改源文件,看看效果。

我们修改下源码将" Welcome to Express"更改为" Welcome to Express with Docker"来测试。在源文件目录/src下,找到routes/index.jsline文件,修改语句为:

  1. res.render('index', { title: 'Express with Docker' }); 

保存文件,然后可以看到Web服务器已经重新启动,表示Docker卷和nodemon可以都可以正常工作。

F5刷新浏览器,内容已经修改:

本文中我们利用Docker和Docker Compose构建了一个简单的Nodejs的开发和运行环境。Node.js和docker的配合很好。通过使用docker-compose,开发体验更加流畅。当然这这是一个很简单的开始,对于更复杂的应用(比如需要访问数据库)才是Docker Compose的用武之地,他可以同时启动和管理多个容器,比如给开发环境增加Mongo或MySQL添加为应用程序的数据源,只需很轻松地增加一个docker-compose配置的服务语句就可以搞定整个环境。

责任编辑:赵宁宁 今日头条
点赞
收藏
 
推荐文章