镜像构建篇 – 我是如何实现 docker 镜像 2 分钟构建、部署

微信扫一扫,分享到朋友圈

镜像构建篇 – 我是如何实现 docker 镜像 2 分钟构建、部署

在前端开发过程当中,我们要解决的不止是页面的风格、浏览器的兼容、用户的体验,当然这些都是非常重要的一部分,但是有一部分,虽然平时我们不需要特别重视,但是它的存在,是对我们项目的及时修复处理一些及时性的问题,起到至关重要的作用。
看之前做好心里准备,也许这个东西有人会,但是之前我不会,会的别喷我,不会的可以问我

做这件事的目的
其实,这件事是一直之前我个人也没有注意到的一个问题,目光前期停留在怎么做页面,怎么写更好的效果,怎么样渲染的更快,怎么样用户体验最好,对项目部署这块一直没有上心研究过,因为没了解过这一块知识之前,我一直觉得这个时间它就是这么长,没办法优化的。
后来因为项目需要做本地版需求,镜像太大,前后端都需要做大程度的优化,这时候,我的目光才注视到了这一块的内容。
历程
之前的实现方式
在优化前的实现方式,其实很简单,就是在部署平台内,通过 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 中
最后就可以跑起来

结尾
其实整体梳理下来,没有什么技术型的难点,而且也很简单,就是一个优化的过程,自己没事干瞎琢磨出来的一个解决办法,也许大家还有更好的办法去解决项目构建部署的问题,有的话可以互相交流一下。
都将近一年没写文章了,这一年发生了很多事情,在加上工作上的变动,磨磨唧唧就一年过去了,也不知道这个排版和文字大家有没有看得懂,有没有不清楚的地方,有的话大家就指出来,接下来会正常更新文章。

微信扫一扫,分享到朋友圈

镜像构建篇 – 我是如何实现 docker 镜像 2 分钟构建、部署

如何编写一个简单的Linux驱动(二)——设备操作集file_operations

上一篇

禁止孩子上网、玩手机?4岁你可以这么做,再大些呢?

下一篇

你也可能喜欢

镜像构建篇 – 我是如何实现 docker 镜像 2 分钟构建、部署

长按储存图像,分享给朋友