在使用Docker部署PHP或者node.js应用时,常用的方法是将代码和环境镜像打包成一个镜像然后运行,一些云厂商提供了非常便捷的操作,只需要把我们的代码提交到VCS上,然后它们就会帮我们拉取代码并根据代码包内的Dockerfile构建我们的镜像然后部署到集群里。

PHP和node.js都有非常不错的生态,有各种各样的包,但是一旦引入的包多了我们的项目内的文件就会变得非常多,所以在使用VCS协作的时候我们都会忽略掉依赖包目录(node_modules / vendor)。但是我们忽略了包目录后在构建镜像的时候就要使用composer或者npm把包重新装回去,所以Dockerfile大概长这样


FROM node
COPY . /src
RUN cd /src && npm install

这样看起来没什么问题,但是如果包一旦多起来安装的时候需要花费很长的时间,修复紧急bug的情况下等待的时间就是煎熬,那我们有没有什么办法能让这个过程更快一些呢?

我们知道Docker构建是分层的,一条指令一层,在没有带--no-cache=true指令的情况下如果某一层没有改动,Docker就不会重新构建这一层而是会使用缓存,先来看看Docker官方文档的描述

  • Starting with a parent image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was built using the exact same instruction. If not, the cache is invalidated.
  • In most cases, simply comparing the instruction in the Dockerfile with one of the child images is sufficient. However, certain instructions require more examination and explanation.
  • For the ADD and COPY instructions, the contents of the file(s) in the image are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the file(s) are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the file(s), such as the contents and metadata, then the cache is invalidated.

简单来说就是如果第n层有改动,则n层以后的缓存都会失效,大多数情况下判断有无改动的方法是判断这层的指令和缓存中的构建指令是否一致,但是对于COPY和ADD命令会计算镜像内的文件和构建目录文件的校验和然后做比较来判断本层是否有改动。

最理想的情况下,我们希望package.json或者composer.json变动的时候会重新的安装包,在没有变动的情况下使用缓存缩短构建时间。

了解上面的规则后我们再来看看上面那个Dockerfile,如果我们不修改任何代码的话第二次构建也是能使用缓存的,但是如果我们修改了代码,COPY . /src这层的缓存就会失效,同时下一层的缓存也会失效。但是大多数情况下,重新构建镜像就意味着代码有修改,但是package.jsoncomposer.json这两个文件并不会频繁的修改,所以我们需要把这两个文件以及install的操作分离出来,所以有


FROM node COPY package.json /src/package.json
RUN cd /src && npm install COPY . /src

package.json里面写一个依赖包


{
"dependencies": {
"express": "^4.16.4"
}
}

然后再写一个index.js


const app = require('express')(); app.listen(8080)

然后我们进行第一次构建,看看docker history的输出


LIN2UR:~ lin2ur$ docker history demo
IMAGE CREATED CREATED BY SIZE COMMENT
3c913c9e997b 6 seconds ago /bin/sh -c #(nop) COPY dir:e3c12f06720cf5f3b… 1.6MB
21373087419a 6 seconds ago /bin/sh -c cd /src && npm install 3MB
64896ee5240d 14 seconds ago /bin/sh -c #(nop) COPY file:87de28b86afd2c1c… 53B

把每一层的IMAGE ID和Dockerfile里面的指令对应起来就是
64896ee5240d => COPY package.json /src/package.json
21373087419a => RUN cd /src && npm install
3c913c9e997b => COPY . /src

现在我们来修改一下index.js模拟我们业务代码变动然后再进行构建


LIN2UR:~ lin2ur$ docker history demo
IMAGE CREATED CREATED BY SIZE COMMENT
5d697905ad0a 6 seconds ago /bin/sh -c #(nop) COPY dir:d698db67dac047bd2… 1.6MB
21373087419a 4 minutes ago /bin/sh -c cd /src && npm install 3MB
64896ee5240d 4 minutes ago /bin/sh -c #(nop) COPY file:87de28b86afd2c1c… 53B

可以看到除了最上一层外其他两层的IMAGE ID是没有变化的,再来看看docker build命令的输出


LIN2UR:~ lin2ur$ docker build --rm -f "Dockerfile" -t demo .
Sending build context to Docker daemon 1.902MB
Step 1/4 : FROM node
---> c63e58f0a7b2
Step 2/4 : COPY package.json /src/package.json
---> Using cache
---> 64896ee5240d
Step 3/4 : RUN cd /src && npm install
---> Using cache
---> 21373087419a
Step 4/4 : COPY . /src
---> 5d697905ad0a
Successfully built 5d697905ad0a
Successfully tagged demo:latest

可以看到步骤2和3都使用了缓存,比第一次构建的时间缩短不少。现在我们在package.json里面再加一个包模拟依赖包变动


LIN2UR:~ lin2ur$ docker history demo
IMAGE CREATED CREATED BY SIZE COMMENT
020ce95b1987 29 seconds ago /bin/sh -c #(nop) COPY dir:ea4d7afd475895520… 1.6MB
d9697dfc7022 31 seconds ago /bin/sh -c cd /src && npm install 3MB
71d8a2fb458a 38 seconds ago /bin/sh -c #(nop) COPY file:87bd25345a96e6b3… 51B

这次底下两层的IMAGE ID都变了,意味着没有使用缓存,再来看看docker build命令的输出


LIN2UR:~ lin2ur$ docker build --rm -f "Dockerfile" -t demo .
Sending build context to Docker daemon 1.902MB
Step 1/4 : FROM node
---> c63e58f0a7b2
Step 2/4 : COPY package.json /src/package.json
---> 71d8a2fb458a
Step 3/4 : RUN cd /src && npm install
---> Running in ce424d6af936
Step 4/4 : COPY . /src
---> 020ce95b1987
Successfully built 020ce95b1987
Successfully tagged demo:latest

由于第二层的package.json改动导致这层及后续的缓存失效,然后重新安装包,实现了我们希望的结果。

来源:https://segmentfault.com/a/1190000018222648

利用构建缓存机制缩短Docker镜像构建时间的更多相关文章

  1. Docker镜像构建(五)

    Docker 镜像介绍 Docker镜像构建分为两种,一种是手动构建,另一种是Dockerfile(自动构建) 手动构建docker镜像 案例:我们基于centos镜像进行构建,制作自己的nginx镜 ...

  2. 4、Docker 镜像构建

    Docker 镜像构建 构建分为两种 手动构建 自动构建dockerfile 手动构建 首先启动一个Centos 容器,然后在容器中安装一个nginx [root@node ~]# docker ru ...

  3. 在sun jdk 8镜像基础上构建maven 3的docker镜像

    2019独角兽企业重金招聘Python工程师标准>>> 在https://my.oschina.net/ytqvip/blog/1595054文章的sun jdk 8镜像基础上构建m ...

  4. apisix docker镜像构建及插件化开发

    高能劝退:lua开发,适合小白看!!! 前段时间有个项目,用的java程序做网关,压测tps只有1k多点,惨不忍睹. 后来公司有个大佬改用apisix做网关,tps飙升到1w多. 于是对神奇的apis ...

  5. apisix网关-构建docker镜像构建及插件化开发

    高能劝退:lua开发,适合小白看!!! 前段时间有个项目,用的java程序做网关,压测tps只有1k多点,惨不忍睹. 后来公司有个大佬改用apisix做网关,tps飙升到1w多. 于是对神奇的apis ...

  6. Docker镜像构建原理解析(不装docker也能构建镜像)

    在devops流程里面 构建镜像是一个非常重要的过程,一般构建镜像是写dockerfile文件然后通过docker client来构建的image. docker client 会先检查本地有没有im ...

  7. Docker镜像构建

    一.简介 在构建容器化应用时,相当重要的步骤莫过于镜像制作,本文将介绍镜像制作方法以及镜像制作的建议.通常镜像的制作有两种方式: 使用现有的容器使用docker commit 生成镜像 使用Docke ...

  8. Docker镜像构建的两种方式(六)--技术流ken

    镜像构建介绍 在什么情况下我们需要自己构建镜像那? (1)当我们找不到现有的镜像,比如自己开发的应用程序 (2)需要在镜像中加入特定的功能 docker构建镜像有两种方式:docker commit命 ...

  9. Docker镜像构建文件Dockerfile及相关命令介绍

    使用docker build命令或使用Docker Hub的自动构建功能构建Docker镜像时,都需要一个Dockerfile文件.Dockerfile文件是一个由一系列构建指令组成的文本文件,doc ...

随机推荐

  1. pstools psexec mimikatz

    Psexec原理 - oneVs1的专栏 - 博客频道 - CSDN.NET 在远程终端(3389.mstsc.exe).虚拟桌面中抓取密码的方法: 通常你在远程终端中运行该程序会提示:存储空间不足, ...

  2. 图论最短路——spfa

    今天开始图论的最短路的最后复习,今天自己手打spfa虽然看了一眼书,但是也算是自己打出来的,毕竟很久没打了,而且还是一遍a代码下来15min左右就搞完了,成就感++.所以呢来篇博客记录一下. 香甜的黄 ...

  3. grpc protobuf

    1.ProtoBuffer是google的一款非常高效的数据传输格式框架 2.一个方法仅能接受一个参数 3.对于定义的message,每个值都有一个唯一的number类型的数字,根据官方文档的解释:它 ...

  4. CH0102 64位整数乘法 数论

    正解:数论/一个神仙想法 解题报告: 先放传送门qwq 两种方法,都还挺妙的就都写了qwq 第一种是快速幂 把b用二进制表示成,ck*2k+ck-1*2k-1+...+c0*20 然后就可以表示成,a ...

  5. EControl平台测试向生产版本工程切换说明

    第一步,备份生产环境版本,假设生产环境版本工程名为SEHEControl,记录版本说明第二部,拷贝测试版本到新文件夹,假设测试版本工程名为SEHEControlTest第三步,进入工程文件夹,修改SL ...

  6. 前端 HTML 常用标签 head标签相关内容

    HTML常用标签 head标签 我们首先来介绍一下head标签的主要内容和作用,文档的头部描述了文档的各种属性和信息,包括文档的标题.编码方式及URL等信息,这些信息大部分是用于提供索引,辩认或其他方 ...

  7. Ubuntu14.04+eclipse下cocos2d-x3.0正式版环境的搭建

    环境: ubuntu14.04 adt-bundle-linux-x86_64 android-ndk-r9d-linux-x86_64 cocos2d-x-3.0正式版 apache-ant 1.9 ...

  8. 万恶之源 - Python数据类型二

    列表 列表的介绍  列表是python的基础数据类型之一 ,其他编程语言也有类似的数据类型. 比如JS中的数 组, java中的数组等等. 它是以[ ]括起来, 每个元素用' , '隔开而且可以存放各 ...

  9. Mac SVN版本从1.9降到1.8

    假设系统已安装brew,在终端执行下列命令: brew update brew install subversion18 echo 'export PATH="/usr/local/opt/ ...

  10. Py之np.concatenate函数【转载】

    转自:https://docs.scipy.org/doc/numpy/reference/generated/numpy.concatenate.html 1.nupmy.concatenate函数 ...