利用构建缓存机制缩短Docker镜像构建时间

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

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

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

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

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

  • 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命令会计算镜像内的文件和构建目录文件的校验和而后作比较来判断本层是否有改动。json

最理想的状况下,咱们但愿package.json或者composer.json变更的时候会从新的安装包,在没有变更的状况下使用缓存缩短构建时间。缓存

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

FROM node

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

COPY . /src

package.json里面写一个依赖包composer

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

而后再写一个index.jside

const app = require('express')();

app.listen(8080)

而后咱们进行第一次构建,看看docker history的输出ui

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改动致使这层及后续的缓存失效,而后从新安装包,实现了咱们但愿的结果。

相关文章
相关标签/搜索