编写Dockerfile的小技巧:善用docker的cache机制

问题引入

在使用docker构建镜像时通常会遇到如下场景:

  1. 需要首先使用maven对项目进行编译打包,然后使用openjdk环境来运行jar包
  2. 需要使用pip安装python依赖之后才能运行程序

这些步骤往往都是比较花时间,而且完全是没必要每次构建时都重复运行的
(尤其是以下这种情况:在调试阶段,发现Dockerfile里有个COPY文件名写错了,然后修改后又重新运行docker build,然后长时间地等待依赖下载,这个过程很崩溃…)

cache机制

事实上,docker官方给出了这类问题的解决方案的,也就是cache机制,具体详情可参考官网
https://docs.docker.com/build/cache/
简单而言,docker在构建时,每条语句都构成一个layer,最终的docker镜像由一系列layer像栈一样的结构堆叠而成
编写Dockerfile的小技巧:善用docker的cache机制_第1张图片
在实际build时,每一个layer都会被缓存,在下一次build时,如果这一层的指令没发生变化,并且这层之前的层的指令也没发生变化,那么docker就会直接用cache里面缓存的layer,就不会再执行一次这个layer了
特别地,对于COPY和ADD等指令而言,“指令没有发生变化”指的是复制的文件内容没有发生变化,即上次复制的文件和这次复制的文件是相同的

案例分析

我们来看一下下面这个Dockerfile

FROM maven:3.8.3-openjdk-17 AS build-env
COPY ./pom.xml /app/
RUN mvn dependency:go-offline
COPY ./ /app/
RUN mvn clean && mvn package -DskipTests

FROM openjdk:17
COPY --from=build-env /app/target/*.jar /app/
CMD ["java","-jar","/app/backend.jar"]

已开始接触的时候,我还比较疑惑,为什么要单独先复制pom.xml,执行一次拉取依赖库,然后才是mvn package
在了解cache机制后,才明白这样做的目的是使用cache机制来缩减build时间
因为只要pom.xml没有发生变化,那么前三句对应的layer就都可以使用cache,其中第三句的拉取依赖操作是非常费时间的,使用cache就可以节省掉这部分时间

相对应的,以下是一个反面教材

FROM maven:3.8.3-openjdk-17 AS build-env
COPY ./ /app/
RUN mvn clean && mvn package -DskipTests

FROM openjdk:17
COPY --from=build-env /app/target/*.jar /app/
CMD ["java","-jar","/app/backend.jar"]

按照这种方法写Dockerfile的话,每次build时就一定需要重新下载依赖库,这个会导致每次的build时间都很长

cache机制的问题

当然,cache机制也有其问题,有个很常见的情况就是当远程仓库的依赖包更新了,然而docker构建时直接使用了cache,导致无法下载并使用最新的依赖包
例如,如果使用ubuntu,第二句就写RUN apt-get update,那么如果执行完build的一周之后再去执行build,可能远程仓库已经有更新的内容,而docker构建时会使用cache,导致这个apt-get update不会执行

你可能感兴趣的:(瞎捣鼓,docker,容器,运维)