在我们的服务中,由于运行该服务的依赖是确定的,因此为了尽可能的缩减基础镜像的体积,我们选择 alpine 版本作为生产环境的基础镜像 。
分级构建这时候,我们遇到了新的问题 。由于 alpine 的基本工具库过于简陋,而像 webpack 这样的打包工具背后可能使用的插件库极多,构建项目时对环境的依赖较大 。并且这些工具库只有编译时需要用到,在运行时是可以去除的 。对于这种情况,我们可以利用 Docker 的分级构建
的特性来解决这一问题 。
首先,我们可以在完整版镜像下进行依赖安装,并给该任务设立一个别名(此处为build
) 。
# 安装完整依赖并构建产物FROM node:14 AS buildWORKDIR /appCOPY package*.json /app/RUN ["npm", "install"]COPY . /app/RUN npm run build
之后我们可以启用另一个镜像任务来运行生产环境,生产的基础镜像就可以换成 alpine 版本了 。其中编译完成后的源码可以通过--from
参数获取到处于build
任务中的文件,移动到此任务内 。
FROM node:14-alpine AS releaseWORKDIR /releaseCOPY package*.json /RUN ["npm", "install", "--registry=http://r.tnpm.oa.com", "--production"]# 移入依赖与源码COPY public /release/publicCOPY --from=build /app/dist /release/dist# 启动服务EXPOSE 8000CMD ["node", "./dist/index.js"]
Docker 镜像的生成规则是,生成镜像的结果仅以最后一个镜像任务为准 。因此前面的任务并不会占用最终镜像的体积,从而完美解决这一问题 。
当然,随着项目越来越复杂,在运行时仍可能会遇到工具库报错,如果曝出问题的工具库所需依赖不多,我们可以自行补充所需的依赖,这样的镜像体积仍然能保持较小的水平 。
其中最常见的问题就是对node-gyp
与node-sass
库的引用 。由于这个库是用来将其他语言编写的模块转译为 node 模块,因此,我们需要手动增加g++ make python
这三个依赖 。
# 安装生产环境依赖(为兼容 node-gyp 所需环境需要对 alpine 进行改造)FROM node:14-alpine AS dependenciesRUN apk add --no-cache python make g++COPY package*.json /RUN ["npm", "install", "--registry=http://r.tnpm.oa.com", "--production"]RUN apk del .gyp
详情可见:https://github.com/nodejs/docker-node/issues/282合理规划 Docker Layer构建速度优化我们知道,Docker 使用 Layer 概念来创建与组织镜像,Dockerfile 的每条指令都会产生一个新的文件层,每层都包含执行命令前后的状态之间镜像的文件系统更改,文件层越多,镜像体积就越大 。而 Docker 使用缓存方式实现了构建速度的提升 。若 Dockerfile 中某层的语句及依赖未更改,则该层重建时可以直接复用本地缓存 。
如下所示,如果 log 中出现
Using cache
字样时,说明缓存生效了,该层将不会执行运算,直接拿原缓存作为该层的输出结果 。Step 2/3 : npm install ---> Using cache ---> efvbf79sd1eb
通过研究 Docker 缓存算法,发现在 Docker 构建过程中,如果某层无法应用缓存,则依赖此步的后续层都不能从缓存加载 。例如下面这个例子:COPY . .RUN npm install
此时如果我们更改了仓库的任意一个文件,此时因为npm install
层的上层依赖变更了,哪怕依赖没有进行任何变动,缓存也不会被复用 。因此,若想尽可能的利用上
npm install
层缓存,我们可以把 Dockerfile 改成这样:COPY package*.json .RUN npm installCOPY src .
这样在仅变更源码时,node_modules
的依赖缓存仍然能被利用上了 。由此,我们得到了优化原则:
经验总结扩展阅读
- 记一次 .NET 某企业OA后端服务 卡死分析
- Docker | 使用dockerfile生成镜像,清理docker空间
- 配置DNS域名解析服务
- CRESDA 陆地观测卫星数据服务订单ftp地址错误—已解决不能下载问题
- 服务员上鱼的正确方法
- Docker | 数据持久化与数据共享
- 泰到位服务3个小时干什么了
- Docker | Compose创建mysql容器
- 糟了,线上服务出现OOM了
- 物业服务不到位物业费可以不交吗