解决 Docker 多阶段构建 layer does not exist 错误
系统环境:
- Docker 版本:19.03.13
- 操作系统版本:CentOS 7.8
一、问题描述
我们在日常使用 Docker 过程中,会经常自己构建一些应用程序的镜像,其中 Docker 官方提供了多阶段构建镜像功能,使我们在构建镜像时减少部署的镜像的层数,从而减小镜像整体大小。
本人这里在使用 Jenkins 进行持续集成操作时,尝试使用 Java 的 SpringBoot 框架提供的分层构建镜像方案构建镜像,在执行过程中遇到了一些问题,这里先贴出 Dockerfile 内容:
## 构建过程镜像FROM openjdk:8u262WORKDIR applicationCOPY target/*.jar application.jarRUN java -Djarmode=layertools -jar application.jar extract
## 构建真正运行部署的镜像FROM openjdk:8u262WORKDIR applicationCOPY --from=0 application/dependencies/ ./COPY --from=0 application/snapshot-dependencies/ ./COPY --from=0 application/spring-boot-loader/ ./COPY --from=0 application/application/ ./ENV JVM_OPTS="-XX:MaxRAMPercentage=80.0"ENV JAVA_OPTS=""ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS org.springframework.boot.loader.JarLauncher"]在本地 Docker 执行上面 Dockerfile,使用多阶段构建,发现在执行过程中是不存在上述问题。不过将这个镜像放在持续集成工具 Jenkins 中后,就出现如下错误:
Step 1/15 : FROM openjdk:8u262 ---> 51d6b33ebe8aStep 2/15 : WORKDIR application ---> Running in b0398a3754d7Removing intermediate container b0398a3754d7 ---> 408784f98bc2Step 3/15 : COPY target/*.jar application.jar ---> e13bbd838db4Step 4/15 : RUN java -Djarmode=layertools -jar application.jar extract ---> Running in 3a23e82340c3Removing intermediate container 3a23e82340c3 ---> d545089f321dStep 5/15 : FROM openjdk:8u262 ---> 51d6b33ebe8aStep 6/15 : WORKDIR application ---> Using cache ---> 408784f98bc2Step 7/15 : COPY --from=0 application/dependencies/ ./ ---> 09fa3826ddd5Step 8/15 : COPY --from=0 application/snapshot-dependencies/ ./ ---> b1484b758fadStep 9/15 : COPY --from=0 application/spring-boot-loader/ ./failed to export image: failed to create image: failed to get layer sha256:9...3c205: layer does not exist显示 layer does not exist 镜像层不存,导致不能继续构建镜像问题。
二、问题分析
经过网上搜索相关问题,寻找发生该错误的原因,发现并没与一种很好的说法到底是什么原因导致的该错误的发生,不过大部分人推测可能是因为 Overlay2 存储引擎兼容引擎的一些问题。
三、解决问题
本人从地址 https://github.com/moby/moby/issues/37965 参考到,大部分发生 layer does not exist 错误都是在第二阶段复制时发生。所以,有人给了一种解决方案,就是在每次执行 COPY --form 后添加 RUN true 命令。
COPY --from=0 /a ./RUN true ##添加RUN trueCOPY --from=0 /b ./RUN true ##添加RUN trueCOPY --from=0 /c ./RUN true ##添加RUN true...本人尝试使用这种方案解决这个问题,在第二阶段构建时的 COPY --form 后面添加 RUN true 命令,修改后的 Dockerfile 如下:
## 构建第一级镜像FROM openjdk:8u262WORKDIR applicationCOPY target/*.jar application.jarRUN java -Djarmode=layertools -jar application.jar extract
## 构建第二级镜像FROM openjdk:8u262WORKDIR applicationCOPY --from=0 application/dependencies/ ./RUN true ##添加RUN trueCOPY --from=0 application/snapshot-dependencies/ ./RUN true ##添加RUN trueCOPY --from=0 application/spring-boot-loader/ ./RUN true ##添加RUN trueCOPY --from=0 application/application/ ./RUN trueENV TZ="Asia/Shanghai"RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezoneENV JVM_OPTS="-XX:MaxRAMPercentage=80.0"ENV JAVA_OPTS=""ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS org.springframework.boot.loader.JarLauncher"]然后执行持续集成构建,确实不会再发送上述错误了。遇到相同错误的朋友可以添加 RUN true 尝试一下。
---END---
