三、创建测试的 SpringBoot 应用
创建测试的 SpringBoot 项目,并且在 pom.xml 中开启镜像分层。
1、Maven 中引入相关依赖和插件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.6.RELEASE</version> </parent> <artifactId>springboot-dockerfile-layer</artifactId> <packaging>jar</packaging> <name>springboot-dockerfile-layer</name> <description>springboot build layer example</description> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <layers> <enabled>true</enabled> </layers> </configuration> </plugin> </plugins> </build> </project>
2、创建测试的 Controller 类
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping("/hello") public String hello() { return "hello world!"; } }
3、创建 SpringBoot 启动类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
四、创建两种构建镜像的 Dockerfile 脚本
为了方便体现出 SpringBoot 2.3.x 支持的分层构建 Dockerfile 的优点,这里在 Java 源码文件夹下,创建普通与分层两种构建镜像的 Dockerfile 脚本,后续会使用这两种脚本构建 Docker 镜像进行构建速度、推送速度、拉取速度的对比。
1、普通镜像构建脚本文件 dockerfile-number
FROM openjdk:8u275 VOLUME /tmp ADD target/*.jar app.jar RUN sh -c 'touch /app.jar' ENV TZ="Asia/Shanghai" RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone ENV JVM_OPTS="-XX:MaxRAMPercentage=80.0" ENV JAVA_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
说明:
- TZ: 时区设置,而 Asia/Shanghai 表示使用中国上海时区。
- JVM_OPTS: 指定 JVM 启动时候的参数,-XX:MaxRAMPercentage 参数和 -Xmx 类似,都是限制堆内存大小,只不过 -Xmx 需要手动指定限制大小,而 -XX:MaxRAMPercentage 则是根据虚拟机可用内存百分比限制。
- JAVA_OPTS: 在镜像启动时指定的自定义 Java 参数,例如 -Dspring.application.name=xxx。
2、分层镜像构建脚本文件 dockerfile-layer
FROM openjdk:8u275 as builder WORKDIR application COPY target/*.jar application.jar RUN java -Djarmode=layertools -jar application.jar extract FROM openjdk:8u275 WORKDIR application COPY --from=builder application/dependencies/ ./ COPY --from=builder application/snapshot-dependencies/ ./ COPY --from=builder application/spring-boot-loader/ ./ COPY --from=builder application/application/ ./ ENV TZ="Asia/Shanghai" ENV JVM_OPTS="-XX:MaxRAMPercentage=80.0" ENV JAVA_OPTS="" ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS org.springframework.boot.loader.JarLauncher"]
说明:
- TZ: 时区设置,而 Asia/Shanghai 表示使用中国上海时区。
- -Djarmode=layertools: 指定构建 Jar 的模式。
- extract: 从 Jar 包中提取构建镜像所需的内容。
- -from=builder 多级镜像构建中,从上一级镜像复制文件到当前镜像中。
五、使用两种 Dockerfile 构建项目镜像
1、在服务器一构建普通 Docker 镜像
(1)、第一次构建
## 执行 Maven 命令,将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-normal:0.0.1 .
Docker 镜像构建总共花费 24.98s 时间。
(2)、第二次构建(修改依赖 pom.xml 文件)
## 修改 pom.xml 里面的依赖,随意添加一个新的依赖包,然后再次将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-normal:0.0.2 .
Docker 镜像构建总共花费 1.27s 时间。
(3)、第三次构建(修改代码内容)
## 修改源代码任意内容后,然后再次将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-normal:0.0.3 .
Docker 镜像构建总共花费 1.32s 时间。
2、在服务器二构建分层 Docker 镜像
(1)、第一次构建
## 执行 Maven 命令,将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-layer:0.0.1 .
Docker 镜像构建总共花费 26.12s 时间。
(2)、第二次构建(修改依赖 pom.xml 文件)
## 修改 pom.xml 里面的依赖,随意添加一个新的依赖包,然后再次将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-layer:0.0.2 .
Docker 镜像构建总共花费 3.44s 时间。
(3)、第三次构建(修改代码内容)
## 修改源代码任意内容后,然后再次将源代码构建 Jar 包 $ mvn clean install ## 构建 SpringBoot 应用的 Docker 镜像 $ time docker build -t hub.mydlq.club/library/springboot-layer:0.0.3 .
Docker 镜像构建总共花费 2.82s 时间。
六、镜像推送到镜像仓库测试
1、推送镜像到镜像仓库测试
服务器一推送普通镜像到镜像仓库1:
## 第一次推送镜像 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.1 real 0m35.215s ## 第二次推送镜像 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.2 real 0m14.051s ## 第三次推送镜像 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.3 real 0m14.183s
服务器二推送分层镜像到镜像仓库2:
## 第一次推送镜像 $ time docker push hub.mydlq.club/library/springboot-layer:0.0.1 real 0m34.121s ## 第二次推送镜像 $ time docker push hub.mydlq.club/library/springboot-layer:0.0.2 real 0m13.605s ## 第三次推送镜像 $ time docker push hub.mydlq.club/library/springboot-layer:0.0.3 real 0m4.805s
2、镜像仓库拉取镜像测试
服务器一推送从镜像仓库1拉取镜像:
## 清理全部镜像 $ docker rm --force $(docker images -qa) ## 拉取镜像 springboot-normal:0.0.1 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.1 real 0m35.395s ## 拉取镜像 springboot-normal:0.0.2 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.2 real 0m6.501s ## 拉取镜像 springboot-normal:0.0.3 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.3 real 0m6.993s
服务器二推送从镜像仓库2拉取镜像:
## 清理全部镜像 $ docker rm --force $(docker images -qa) ## 拉取镜像 springboot-layer:0.0.1 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.1 real 0m30.615s ## 拉取镜像 springboot-layer:0.0.2 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.2 real 0m4.811s ## 拉取镜像 springboot-layer:0.0.3 $ time docker push hub.mydlq.club/library/springboot-normal:0.0.3 real 0m1.293s
七、镜像构建、推送、拉取时间汇总
1、不使用分层构建镜像
如下图:
2、使用分层构建镜像
如下图:
3、总结
上面进行了使用 SpringBoot2.3.x 分层的方式构建镜像与普通的方式构建镜像,在镜像的构建、推送、拉取方面进行了执行速度对比,总结出如下结论:
- 镜像构建: 在构建上,使用分层 Jar 构建镜像可能比普通方式构建镜像更繁琐,所以也更耗时,故而在构建上分层 Jar 构建镜像没有太多优势。
- 镜像推送: 在推送上,如果每次构建镜像都只是修改构建镜像项目的源码,使用分层 Jar 构建镜像,可以大大加快镜像推送速度。如果是修改构建镜像项目中的依赖包,则和普通构建一样速度很慢。