在使用Docker部署PHP或node.js应用时,常用的方法是将代码和环境镜像打包成一个镜像运行。一些云厂商提供了非常方便的操作。把我们的代码提交到VCS,然后他们会帮我们拉取代码,根据代码包中的Dockerfile构建我们的镜像,部署到集群中。PHP和node.js都有很好的生态,有各种各样的包,但是一旦引入更多的包,我们项目中的文件就会变得非常大,所以我们在使用VCS协同的时候会忽略掉依赖包目录(节点模块/供应商)。但是,忽略包目录后,我们在构建镜像时需要使用composer或npm重新安装包,因此Dockerfile如下所示:FROMnodeCOPY。/srcRUNcd/src&&npminstall貌似没什么问题,但是如果packageonce安装多了需要很长时间,修复紧急bug的情况下的等待时间很煎熬。我们有什么办法可以使这个过程更快?我们知道Docker构建是分层的,一个指令层,如果没有--no-cache=true指令某一层没有变化,Docker不会重建这一层而是会使用缓存,我们来看看Docker官方文档的描述。从缓存中已有的父图像开始,将下一条指令与从该基础图像派生的所有子图像进行比较,以查看其中一个是否是使用完全相同的指令构建的。如果不是,则缓存无效。在大多数情况下,只需将Dockerfile中的指令与其中一个子图像进行比较就足够了。但是,某些说明需要更多的检查和解释。对于ADD和COPY指令,检查映像中文件的内容并为每个文件计算校验和。这些校验和不考虑文件的最后修改时间和最后访问时间。在此期间缓存查找,将校验和与现有图像中的校验和进行比较。如果文件中发生任何更改,例如内容和元数据,则缓存将失效。简单的说,如果第n层发生变化,那么第n层之后的缓存就会失效。大多数情况下,判断是否有变化的方式是判断这一层的指令与缓存中的构建指令是否一致,但是对于COPY和ADD命令,镜像中的文件和构建目录文件将计算校验和,然后比较以确定这一层是否发生了变化。理想情况下,我们希望当package.json或composer.json发生变化时,重新安装包,如果没有变化则使用缓存来缩短构建时间。了解了上面的规则之后,我们再来看看上面的Dockerfile。如果我们不修改任何代码,第二次构建也可以使用缓存,但是如果我们修改代码,COPY./src层的缓存就会失效。同时下一级缓存也会失效。但是大多数情况下,重建镜像意味着修改了代码,但是package.json和composer.json这两个文件并不经常修改,所以我们需要把这两个文件和install的操作分开,所以就有了FROMnodeCOPYpackage.json/src/package.jsonRUNcd/src&&npminstallCOPY./src在package.json中写一个依赖包{"dependencies":{"express":"^4.16.4"}}然后再写一个索引。jsconstapp=require('express')();app.listen(8080)然后我们第一次构建,看看dockerhistoryLIN2UR的输出:~lin2ur$dockerhistorydemoIMAGECREATEDCREATEDBYSIZECOMMENT3c913c9e997b6secondsago/bin/sh-c#(nop)COPYdir:e3c12f06720cf5f3b...1.6MB21373087419a6秒前/bin/sh-ccd/src&&npminstall3MB64896ee5240d14秒前/bin/sh-c#(nop)COPY文件:87de28b86afd2c1c...53BDockerfile中指令对应各层的IMAGEID为64896ee5240d=>COPYpackage.json/src/package.json21373087419a=>RUNcd/src&&npminstall3c913c9e997b=>复制。/src现在让我们修改index.js以模拟我们的业务代码更改,然后构建LIN2UR:~lin2ur$dockerhistorydemoIMAGECREATEDCREATEDBYSIZECOMMENT5d697905ad0a6secondsago/bin/sh-c#(nop)COPYdir:d698db67dac047bd2...1.6MB21373087419a4分钟前/bin/sh-ccd/src&&npminstall3MB64896ee5240d4分钟前/bin/sh-c#(nop)COPYfile:87de28b86afd3Bc1cok...看到另外两层的IMAGEID没有变除了顶层,让我们看看dockerbuild命令LIN2UR的输出:~lin2ur$dockerbuild--rm-f"Dockerfile"-tdemo.SendingbuildcontexttoDockerdaemon1.902MBStep1/4:FROMnode--->c63e58f0a7b2步骤2/4:复制package.json/src/package.json--->使用缓存--->64896ee5240d步骤3/4:运行cd/src&&npminstall--->使用缓存--->21373087419a步骤4/4:复制。/src--->5d697905ad0aSuccessfullybuilt5d697905ad0aSuccessfullytaggeddemo:latest可以看到第2步和第3步都使用了缓存,比第一次构建时间短很多现在我们在package.json中添加另一个包来模拟依赖包的变化LIN2UR:~lin2ur$dockerhistorydemoIMAGECREATEDCREATEDBYSIZECOMMENT020ce95b198729secondsago/bin/sh-c#(nop)COPYdir:ea4d7afd475895520...1.6MBd9697dfc1987secondsago/bin/sh-ccd/src&&npminstall3MB71d8a2fb458a38secondsago/bin/sh-c#(nop)COPYfile:87bd25345a96e6b3...51B这次最下面两层的IMAGEID变了,说明有no使用缓存,然后查看dockerbuild命令LIN2UR的输出:~lin2ur$dockerbuild--rm-f"Dockerfile"-tdemo.SendingbuildcontexttoDockerdaemon1.902MBStep1/4:FROMnode--->c63e58f0a7b2Step2/4:复制package.json/src/package.json--->71d8a2fb458aStep3/4:运行cd/src&&npminstall--->在ce424d6af936中运行步骤4/4:复制。/src--->020ce95b1987Successfullybuilt020ce95b1987Successfullytaggeddemo:latest由于第二层package.json发生变化,导致本层及后续缓存失效,重新安装包,达到预期效果
