by 雪隐 from https://juejin.cn/user/1433418895994094
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权
大家好我是雪隐,请叫我雪宝,欢迎来到NodeJS框架NestJS的第四个提示和技巧系列。我会介绍下用NestJS用Docker发布时我遇到的一些问题。
优化前的打包方式
在我不懂事的时候,我一般会把发布的Dockerfile和Docker-compose写成下面这个样子,有没有小伙伴们和我一样的。
Dockfile文件
FROM node:latest
# 创建目录
RUN mkdir -p /home/app/
# 指定工作目录
WORKDIR /home/app/
# 复制package.json文件
COPY ./dist/package.json ./
# 安装依赖
RUN npm install
# 复制编译好的项目文件
COPY ./dist .
# 复制配置文件
COPY ./.config ./.config/
# 创建log的目录卷
VOLUME [“/logs”]
# 暴露8000端口
EXPOSE 8000
# 程序启动命令
CMD ["node", "src/main.js"]
Docker-compose文件
version: "3"
services:
gateway-service-dev:
container_name: gateway-service-dev
build:
# 编译并且关联镜像
context: ./
dockerfile: Dockerfile
ports:
# 端口
- "8000:8000"
volumes:
# 挂载日志
- ./nestjs/logs:/home/app/logs
environment:
# 环境变量
RUNNING_ENV: 'prod'
如果只是自己玩玩还好,如果这是要打包上线的,而且发布的人不是自己,那么很有可能会被发布的人鄙视或者投来异样的眼光,尤其是如果项目的功能很小,你会发现,这样发布Nest至少要占用1.5G左右甚至更多的磁盘空间。
这里面有2个比较有问题的点,一个是Dockerfile的FROM node:latest,它的镜像大小估计在1G左右,第二个是RUN npm install即使您的Nest项目什么也不做,估计也会有200~300M的依赖会被下载进来,而且比较费时。
还有一个小问题是,根本不需要Dockerfile这个文件。因为Docker-compose完全可以一手操办所有的事情。
问题解析
FROM node:latest
首先不建议大家直接写latest,因为环境的升级可能会导致某些功能无法使用。最好固定一个版本。之后再讨论优化。在讨论优化之前,先请大家看下这篇文章。这文章的写了很详细Node关于Docker的最佳实践。
How to Use the Node Docker Official Image
不嫌麻烦的朋友的还是建议完整看一遍原文。我这里简单的描述下重点内容。
镜像node:大版本或者node:大版本-bullseye的包里面含有大量开发工具包。我们拿node:19-bullseye的版本来举例,如果要发布的话建议我们给环境瘦身。通过下面的命令可以删除开发依赖包(devDependencies)。
FROM node:19-bullseye
RUN npm prune --production
甚至我们可以直接利用已经瘦身好的环境node:19-bollsesye-slim。它直接比总包少了75%的体积。
更厉害的情况是可以用最小包node:19-alpine,但是官方没有对他进行维护(如果是很懂的朋友可以直接用这个包,包里面很多基础的工具都没有,比如curl等等,所以安装这个包的时候最好手动装一些自己需要的基础工具)。
其他还有很多建议,比如不要放在根目录,设置用户权限和环境变量,限制内存上限等等。
RUN npm install
这个命令相信大家都很熟悉,但是大家要清楚,NestJS基本都是用TypeScript开发的,有很多的第三方type文件,还有启动的工具包 webpack等等。直接npm install是很不负责任的。这些只在开发中起作用的包也会被一并安装到正式环境中。
// package.json
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
// 正式环境中用不到的包。
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
正确的打包方式。
RUN npm install --production
另外,NestJS是利用Webpack来打包,但是Nest官方的Webpack默认基础配置文件把一些包排除了,我们甚至可以自己写webpack.config.js,把所有线上要用到的依赖都打包在一起。从而可以省略掉npm install这一步。有兴趣的小伙伴们可以研究下。
- 整合成一个
最后,我把最初的2个文件整合成立了一个,只是一个简单的例子,上线版本权限什么的大家自己去考虑。请记住上传node_modules到服务器的时候最好压缩成zip文件,然后在服务器解压,这样会快一些。另外安装依赖的时候一定要带上--production这个参数。
version: "3"
services:
instructor-nodejs:
image: "node:19-bullseye-slim"
working_dir: /home/app
container_name: instructor-nodejs
ports:
- "3078:3078"
volumes:
# 挂载日志
- ./logs:/home/app/logs
# 挂载配置文件
- ./config:/home/app/config
# 挂载各种第三方插件
- ./node_modules:/home/app/node_modules
# 挂载主要程序
- ./dist:/home/app/dist
command: node /home/app/dist/main.js
environment:
RUNNING_ENV: 'prod'
结论
千万不要被Java的小伙伴给鄙视了。一个JDK镜像也就500M。拿着1G镜像去发布依赖,肯定不是太妥当的。