在前端开发过程当中,我们要解决的不止是页面的风格、浏览器的兼容、用户的体验,当然这些都是非常重要的一部分,但是有一部分,虽然平时我们不需要特别重视,但是它的存在,是对我们项目的及时修复处理一些及时性的问题,起到至关重要的作用。
看之前做好心里准备,也许这个东西有人会,但是之前我不会,会的别喷我,不会的可以问我
做这件事的目的
其实,这件事是一直之前我个人也没有注意到的一个问题,目光前期停留在怎么做页面,怎么写更好的效果,怎么样渲染的更快,怎么样用户体验最好,对项目部署这块一直没有上心研究过,因为没了解过这一块知识之前,我一直觉得这个时间它就是这么长,没办法优化的。
后来因为项目需要做本地版需求,镜像太大,前后端都需要做大程度的优化,这时候,我的目光才注视到了这一块的内容。
历程
之前的实现方式
在优化前的实现方式,其实很简单,就是在部署平台内,通过 git 拉取当前分支内容,然后根据 package.json
安装所有依赖,在进行 build
打包,然后在部署平台内进行镜像构建,构建完成后,通过平台发布镜像到容器。
优点: 省心,就点一下就完事
缺点: 镜像特大,发布慢,构建慢,一旦网络或者平台出现问题,镜像就有构建失败的风险,而且紧急修复时成本非常高
网上大部分实现方式
网上之前查询过一些方法,目前我查到的一些实现方式,就是通过 CI
,然后选择对应的分支去做 docker
镜像构建,最后发布到相应的容器当中。
优点: 镜像构建省心,可设置 cache
,减小镜像体积
缺点: 可控性差,github
项目使用 gitlab CI
时,频繁 push
还有一个 commit
更新延迟的情况,无法第一时间进行项目构建,不同的分支有不同的 npm
包需要去做 cache
处理,维护成本高
思考过程
本地模拟
在做优化之前,我是先去考虑的,这个可优化的范围在哪,是代码的体积吗?其实并不是,代码优化上几 M,也不现实,因为一共打包后代码也没有多大,这里不是我需要优化的重要的点,所以我准备先在本地按照平台构建镜像的方式,把镜像在本地做一遍构建;
这里需要一部分的 docker 方面的知识,但是不多,也不难,docker 的安装方式,我就不讲了,大家自行百度
通过本地执行 docker
命令:
docker build -t local-test:2150 PATH(自己当前项目目录) 复制代码
命令行就是这个样子,要安装一堆乱七八糟,因为前面说过,镜像的构建是基于平台的,所以平台要安装很多的包,很多的内容,最后镜像才能正常的构建完成。
本地查看镜像信息
镜像正常构建完成后,我们可以在本地查到当前构建好的镜像:
docker images 复制代码
然后我们找到当前构建好的本地镜像,后面就可以看见我们当前镜像的大小了:
现在我们就可以看出来,镜像特别大,1.65 G,很恐怖的一个大小,然后我们继续剖析,到底是什么占的这么大。
本地启 docker 容器
进入容器:
docker run -it local-test:2150 /bin/bash 复制代码
查看当前镜像所有文件大小:
cd / du -d1 -h 复制代码
这样可以看到所有目录的大小,现在我们就可以知道,到底是什么这么占用镜像大小:
看一下 code
:
一个 node_modules
完全没有用,可以干掉。
上面还有个 ./usr
文件夹,占了 1.2G ,也是类似的情况,这里就不占内容了。
其实镜像最主要的东西,就是 nginx
和 build
后的 dist
文件夹,其他大部分的内容,都是没有用的,可以删除的,我们大概就知道问题出自哪里了。
如果可以发现问题的本质,那就不是问题,是阅历 - 热情的刘大爷 复制代码
解决步骤
优化 dockerfile
先看一下我优化前的 dockerfile:
FROM debian:9 RUN echo \ deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib\ deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib\ deb http://mirrors.aliyun.com/debian-security stretch/updates main\ deb-src http://mirrors.aliyun.com/debian-security stretch/updates main\ deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib\ deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib\ deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib\ deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib\ > /etc/apt/sources.list RUN apt-get update && apt-get -y upgrade RUN apt-get install -y git-core curl build-essential RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - RUN apt-get install -y nodejs RUN npm config set registry https://registry.npm.taobao.org RUN mkdir /code ADD . /code WORKDIR /code RUN npm install RUN npm run build:test RUN cp -fr /code/dist/ /usr/share/nginx/html/ COPY nginx/prod.conf /etc/nginx/nginx.conf EXPOSE 80 STOPSIGNAL SIGTERM CMD ["nginx", "-g", "daemon off;"] 复制代码
细看一下内容,其实我们要关心的东西并不多,我们要做的就是把一个 build
好的包,放到 /usr/share/nginx/html/
, 然后把 nginx
配置放到 /etc/nginx/nginx.conf
当中,然后配置端口,启动 nginx
。
精简 dockerfile
把上面的东西删掉后,发现还需要 build
,这个时候,我想了个弯道超车的办法:本地 build
FROM debian:9 ADD ./dist/ /usr/share/nginx/html/dist/ COPY nginx/prod.conf /etc/nginx/nginx.conf EXPOSE 80 STOPSIGNAL SIGTERM CMD ["nginx", "-g", "daemon off;"] 复制代码
这样是不是很干净,都不用重新再在本地构建一遍镜像,我们也知道那些没有用的东西不会存在在现在构建的镜像当中,因为我们就没有做别的事情,就是两个 copy
动作,然后启动,完事。
自动构建
镜像优化完后,最大的问题我们就解决了,接下来要解决的事情,就是我们不可能没完没了的老是在本地 build
,比如比较忙的时候同时操作多个项目,或者需要频繁的在不同分支开发不同的功能,自己一遍一遍的 build
,会被自己搞死。
优化的目的我们也是为了提高工作效率,减少重复性工作成本,这样如果手动的,还不如那样,因为手动 build
总有失误的时候,常在河边走,哪有不湿鞋嘛。
在这里呢,我是基于 git hooks
做的一个自动化构建的实现,git hooks
有一个钩子是 pre-push
,就是在代码推送前做的一些行为。
在开发中,一般情况下,每次代码的推送,就意味着我可能要部署一版到服务器上,不论是测试还是正式。
我们每次发布的镜像, 镜像名是可以相同的,但是需要通过 tag
去做区分,我目前是通过时间字符串去做的 tag
// .huskyrc.js module.exports = { hooks: { 'pre-push': `\ npm run build && \ docker build -t images:tag PATH(当前项目目录) && \ // 这里发送到平台上,用于镜像的发布 `, }, } 复制代码
环境区分
在项目当中,不可能只有一个分支,也不可能只有一个服务器,最起码我们要区分一个测试,一个正式,两个服务器。
如果平时我们的工作,代码提交是按照 git flow
的方式来的,那么这个问题就很好解决了,获取当前分支:
// 获取当前分支名称 const branch = execSync('git rev-parse --abbrev-ref HEAD') .toString() .replace(/\s+/, '') 复制代码
通过分支去区分我们要执行的 build
命令,images
镜像名称,dockerfile
文件:
var branchAuthority = { 'dev': { docker: 'Dockerfile.test', image: 'local-test', build: 'npm run build:test', }, } 复制代码
修改 .huskyrc.js
:
module.exports = { hooks: { 'pre-push': `\ ${build} && \ docker build -f ${docker} -t ${image:tag} ${path} `, }, } 复制代码
整体的流程大概就是这个样子了,剩下的就是一些细节上的优化,比如如果规划 branchAuthority
结构,镜像构建完成、发布完成后是否需要删除本地镜像等优化型行为了。
总结
效果对比
优化前: 镜像大小 1.65G,构建及发布时间 600s 左右;
优化后: 镜像大小 350M,构建及发布时间 120s 左右
优化后的步骤
- 安装
git hooks
和dockerfile
- 做环境、镜像、docker 区分
- 本地构建完,本地启个容器,先测试一下流程有没有问题,没有问题在嵌入到
pre-push
中 - 最后就可以跑起来
结尾
其实整体梳理下来,没有什么技术型的难点,而且也很简单,就是一个优化的过程,自己没事干瞎琢磨出来的一个解决办法,也许大家还有更好的办法去解决项目构建部署的问题,有的话可以互相交流一下。
都将近一年没写文章了,这一年发生了很多事情,在加上工作上的变动,磨磨唧唧就一年过去了,也不知道这个排版和文字大家有没有看得懂,有没有不清楚的地方,有的话大家就指出来,接下来会正常更新文章。
作者:热情的刘大爷