在上文中, 我们从零开始安装了必需的一些 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 等), 可以为你和你的团队减少很多的重复劳动和出错的几率, 让日常工作更轻松一些. 同时, 作为一个服务端开发人员, 这些东西会让你产生更广阔的思考空间, 得到更多的隐性能力提升.
加油吧, 少年!
来源: http://www.jianshu.com/p/a55cc21834f2