Node.js服务端开发教程 (二):新的软件交付方式

最新推荐:《Vue3.0抢先学》系列学习教程

在上文中,我们从零开始安装了必需的一些NestJS开发环境,并使用命令行工具生成了第一个NestJS服务端程序,而且也初步了解了怎么把这个程序运行起来。

不过,作为一名优秀的软件工程师,光清楚开发程序的功能,是不太够的。因为软件早晚要拿到正式的环境、或其他各种各样的环境下去运行的,可怕的是,你根本不知道这些环境是什么操作系统,上面装了哪些软件,软件的版本是什么!有可能在你开发的电脑上运行的好好的程序,一到其他的电脑上就各种报错,根本运行不起来或是出现各种奇怪的问题。

这就是程序员界著名的“我本地是好的呀”问题。

为了解决这种环境差异带来的问题,业内陆续出现了各种各样的解决方案,比如早前的虚拟机,以及最近几年比较火的容器化。我们今天就想通过容器化的方式,将我们的第一个NestJS程序打包成一个拥有环境一致、代码一致、入口单一、不受外界影响的可交付产品。

说到容器化,有些朋友可能会脱口而出:Docker!对,没错,我们今天就是要使用到Docker。不过容器化工具不止Docker一种,像CoreOS之类的也是同类产品,只是Docker太出名了,导致很多朋友都把Docker当做容器化的代名词。另外容器化扩展出来的东西很庞大,比如容器编排(大家肯定听说过现在大名鼎鼎的Kubernetes,简称K8S;还有Docker自家旗下的Swarm),这些容器编排工具在集群管理和微服务领域有着非常重要的作用。内容太多,我们今天只粗浅的了解最简单的Docker使用。

安装Docker

没安装过Docker的朋友,先来安装下工具吧。如果你用的是Windows或Mac的电脑,可以从 https://www.docker.com/products/docker-desktop 这个网址下载Docker Desktop;Linux的用户可以通过各自的包管理工具(yum、apt等)进行安装。

安装完成后在命令行上输入下面的命令,确保Docker服务已经运行起来并正常可用:

docker ps

如果你看到了以下的信息(只要有红色的那几个表头就行),说明一切已经准备就绪:

如果你看到的是类似如下的信息,说明你的Docker服务器程序还没运行起来,请检查你的Docker Desktop(Windows、Mac)或dockerd(Linux)是不是正常启动了:

构建NestJS程序

还记得前文中,我们是怎么把程序运行起来?

npm run start 
# 使用 yarn 则是:yarn run start

这个命令其实执行了2个步骤:

  • 将TypeScript编写的程序构建转换成JavaScript程序,放到dist目录下
  • 通过Node.js执行dist目录下的main.js主程序

而对于我们发布程序的过程来说,只需要构建就可以了,并不需要运行。因此,我们只需要执行项目的构建命令:

npm run build
# 使用 yarn 则是:yarn run build

执行完成后,同样会构建出dist目录及目录下的程序文件。然后做个小实验,执行下面的命令:

node dist/main.js

是不是发现,它同样可以把我们的NestJS程序跑起来?其实,在通过构建后,dist目录下的文件就是一个单独可拿出去发布的东西了(其实还有package.json),把dist目录以及package.json复制到其他的电脑上,就可以把这个程序完整的运行起来(当然别忘了先通过npm或yarn安装依赖)。

介绍以上的内容,是为了让大家清楚的了解我们真正要发布的东西有哪些。我们要认识到,发布程序的时候,我们不可能把源代码都给到别人,让他们从源代码来运行我们的程序,这不仅不方便,也不太符合商业利益。

打包成镜像

下面,我们要真正开始用到Docker了!我们要为我们的项目中添加一些文件,让我们的项目具有容器化发布的能力。

先添加一个.dockerignore文件,内容如下:

node_modules
src
test

这个文件的作用,有点像Git的.gitignore文件。它是用来在Docker打包镜像复制文件的过程中,排除掉不需要复制的文件的。在这个文件中,排除了2部分的内容:

1.项目的源代码目录、测试代码目录

因为我们发布给别人的镜像里并不需要它们。

2. node_moudues依赖包目录

排除它,完全是为了避免操作系统不同的问题。试想一下,如果你本地开发机的操作系统是Windows的,你本地安装的node_modules目录中可能含有在安装时编译成跟当前操作系统密切关联的addons,这种addons在镜像操作系统中(一般都是Linux)没有办法运行,程序就会出错。所以,node_modules依赖包一定要在打包镜像的时候在镜像里面运行安装,才能保证依赖包是符合镜像所运行的操作系统的。

接着,要添加的文件是Dockerfile,Docker打包镜像的描述文件:

# 使用基于Alpine Linux制作的Node.js 12.13版本的基础镜像
FROM node:12.13-alpine

# 设置镜像的工作目录是 /app
WORKDIR /app

# 复制源代码目录内容到镜像的 /app 目录中去
#(会忽略掉.dockerignore中声明的内容)
COPY . /app

# 在镜像的工作目录下执行下列命令:
# 1. 设置yarn源为淘宝镜像源,加速依赖包下载
# 2. 执行yarn安装package.json中的项目依赖
RUN yarn config set registry http://registry.npm.taobao.org/ && \
    yarn

# 设置环境变量
ENV NODE_ENV=production

# 设置镜像启动后的容器对外暴露(外界可访问)的端口
EXPOSE 3000

# 镜像容器启动时执行的命令
# 即使用node来执行NestJS编译后的dist目录下的main.js
CMD ["node", "dist/main.js"]

有了上面的2个文件后,我们就可以开始打包镜像了。在我们的项目根目录下,执行:

docker build -t myserver:v1 .

命令行上一阵闪烁,打包结束!赶紧执行一段命令来确认一下打包后的镜像信息:

docker images

我们看到刚刚打包的名为myserver,版本标记为v1,大小为488MB的镜像。成功!

镜像有了,那我们怎么来运行这个镜像,它真的能跟我们预期的那样跑起来吗?快来试试下面的命令:

#以后台守护进程的方式,运行在3000端口
docker run -d -p 3000:3000 myserver:v1

执行完毕,那怎么来确认它真的已经跑起来了呢?docker ps 一下呗,如果你能在执行的结果列表里发现这个myserver:v1的镜像所产生的容器,则说明已经运行成功了:

docker ps

快去浏览器里打开 http://localhost:3000/ 看看那句既朴素又雅致,让人感到亲切又兴奋激动,好似来自远方的朋友的热情问候:Hello World!

发布镜像

前面打包完成的myserver:v1,已经是一个可以到处发布的镜像了。但是当前它还只是在你的电脑上呆着,怎么能让要使用它的其他人获取到这个镜像呢?这个就需要依靠镜像仓库了,就如我们的Dockerfile里那个 node:12.13-alpine 基础镜像,其实也是从远程的镜像仓库服务器上下载的,默认使用的是官方镜像仓库Docker Hub(https://hub.docker.com/)。我们可以在自己的服务器上安装docker registry,搭建一个自己的镜像仓库私服,保证企业内部授权用户才能访问。也可以使用一些第三方的云仓库,比如阿里云的容器镜像服务就挺好用的,上传下载速度也不错。

每个镜像仓库需要先登录才能使用,所以,请先在你使用的镜像仓库服务方获取必要的登录账号和密码等信息。然后登录:

docker login --username=your_account your_registry.domain.com

登录完成后,你就可以开始向仓库中上传镜像了:

#镜像设置别名,别名需要符合目标镜像仓库的规定
docker tag myserver:v1 registry.cn-shanghai.aliyuncs.com/moredist-test/myserver:v1

#上传镜像
docker push registry.cn-shanghai.aliyuncs.com/moredist-test/myserver:v1

成功执行后,本地的镜像就被上传到了镜像仓库服务器上去了。

至此,其他有权限访问该镜像仓库的人,就可以拉取你的程序镜像,并把镜像运行在任何安装了Docker的计算机上了。拉取镜像(未登录过的需要先登录):

docker pull registry.cn-shanghai.aliyuncs.com/moredist-test/myserver:v1

总结

我们花了一定的篇幅去介绍这种容器化的交付方式,并且乍一看还稍微有点工作量,但是这真的是一种非常值得你花点时间去学习的东西,这些投入,可以帮助你在以后的工作中有更高效、更优质的产出,如果借助于一些CI/CD工具(比如Jenkins、Gitlab等),可以为你和你的团队减少很多的重复劳动和出错的几率,让日常工作更轻松一些。同时,作为一个服务端开发人员,这些东西会让你产生更广阔的思考空间,得到更多的隐性能力提升。

加油吧,少年!

关注首发公众号:默碟

推荐阅读更多精彩内容