在使用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
简单来讲就是若是第n层有改动,则n层之后的缓存都会失效,大多数状况下判断有无改动的方法是判断这层的指令和缓存中的构建指令是否一致,可是对于COPY和ADD命令会计算镜像内的文件和构建目录文件的校验和而后作比较来判断本层是否有改动。json
最理想的状况下,咱们但愿package.json
或者composer.json
变更的时候会从新的安装包,在没有变更的状况下使用缓存缩短构建时间。缓存
了解上面的规则后咱们再来看看上面那个Dockerfile,若是咱们不修改任何代码的话第二次构建也是能使用缓存的,可是若是咱们修改了代码,COPY . /src
这层的缓存就会失效,同时下一层的缓存也会失效。可是大多数状况下,从新构建镜像就意味着代码有修改,可是package.json
和composer.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.js
ide
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
改动致使这层及后续的缓存失效,而后从新安装包,实现了咱们但愿的结果。