简介

在很久很久以前,我们是怎么创建Spring Boot的docker image呢?最最通用的办法就是将Spring boot的应用程序打包成一个fat jar,然后写一个docker file,将这个fat jar制作成为一个docker image然后运行。

今天我们来体验一下Spring Boot 2.3.3 带来的快速创建docker image的功能。

传统做法和它的缺点

现在我们创建一个非常简单的Spring Boot程序:

@SpringBootApplication
@RestController
public class Application { public static void main(String[] args) {
SpringApplication.run(Application.class, args);
} @GetMapping("/getInfo")
public String getInfo() {
return "www.flydean.com";
}
}

默认情况下,我们build出来的是一个fat jar:springboot-with-docker-0.0.1-SNAPSHOT.jar

我们解压看一下它的内容:

Spring boot的fat jar分为三个部分,第一部分就是BOOT-INF, 里面的class目录放的是我们自己编写的class文件。而lib目录存放的是项目依赖的其他jar包。

第二部分是META-INF,里面定义了jar包的属性信息。

第三部分是Spring Boot的类加载器,fat jar包的启动是通过Spring Boot的jarLauncher来创建LaunchedURLClassLoader,通过它来加载lib下面的jar包,最后以一个新线程启动应用的Main函数。

这里不多讲Spring Boot的启动。

我们看一下,如果想要用这个fat jar来创建docker image应该怎么写:

FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/springboot-with-docker-0.0.1-SNAPSHOT.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

这样写有两个问题。

第一个问题:我们是用的far jar,在使用far jar的过程中会有一定的性能问题,肯定要比解压过后的性能要低,尤其是在容器环境中运行的情况下,可能会更加突出。

第二个问题:我们知道docker的image是按layer来构建的,按layer构建的好处就是可以减少image构建的时间和重用之前的layer。

但是如果使用的是fat jar包,即使我们只修改了我们自己的代码,也会导致整个fat jar重新更新,从而影响docker image的构建速度。

使用Buildpacks

传统的办法除了有上面的两个问题,还有一个就是需要自己构建docker file,有没有一键构建docker image的方法呢?

答案是肯定的。

Spring Boot在2.3.0之后,引入了Cloud Native 的buildpacks,通过这个工具,我们可以非常非常方便的创建docker image。

在Maven和Gradle中,Spring Boot引入了新的phase: spring-boot:build-image

我们可以直接运行:

 mvn  spring-boot:build-image

运行之,很不幸的是,你可能会遇到下面的错误:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) on project springboot-with-docker: Execution default-cli of goal org.springframework.boot:spring-boot-maven-plugin:2.3.3.RELEASE:build-image failed: Docker API call to 'localhost/v1.24/images/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase-platform-api-0.3' failed with status code 500 "Internal Server Error" and message "Get https://gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)" -> [Help 1]

这是因为我们无法从gcr.io中拉取镜像!

没关系,如果你会正确的上网方式的话,那么我估计你已经找到了一个代理。

将你的代理配置到Docker的代理项里面,我使用的是Docker desktop,下面是我的配置:

重新运行 mvn spring-boot:build-image

等待执行结果:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ springboot-with-docker ---
[INFO] Building image 'docker.io/library/springboot-with-docker:0.0.1-SNAPSHOT'
[INFO]
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%

你可以看到,我们的确是需要从gcr.io拉取image。

Layered Jars

如果你不想使用Cloud Native Buildpacks,还是想使用传统的Dockerfile。 没关系,SpringBoot为我们提供了独特的分层jar包系统。

怎么开启呢? 我们需要在POM文件中加上下面的配置:

    <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>

再次打包,看下jar包的内容:

看起来和之前的jar包没什么不同,只不过多了一个layers.idx 这个index文件:

- "dependencies":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "application":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"

index文件主要分为4个部分:

  • dependencies - 非SNAPSHOT的依赖jar包
  • snapshot-dependencies - SNAPSHOT的依赖jar包
  • spring-boot-loader - Spring boot的class loader文件
  • application - 应用程序的class和resources文件

注意,这里的index文件是有顺序的,它和我们将要添加到docker image中的layer顺序是一致的。

最少变化的将会最先添加到layer中,变动最大的放在最后面的layer。

我们可以使用layertools jarmode来对生成的fat jar进行校验或者解压缩:

java -Djarmode=layertools -jar springboot-with-docker-0.0.1-SNAPSHOT.jar
Usage:
java -Djarmode=layertools -jar springboot-with-docker-0.0.1-SNAPSHOT.jar Available commands:
list List layers from the jar that can be extracted
extract Extracts layers from the jar for image creation
help Help about any command

使用list命令,我们可列出jar包中的layer信息。使用extract我们可以解压出不同的layer。

我们执行下extract命令,看下结果:

可以看到,我们根据layers.idx解压出了不同的文件夹。

我们看一下使用layer的dockerFile应该怎么写:

FROM adoptopenjdk:11-jre-hotspot as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract FROM adoptopenjdk:11-jre-hotspot
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

这样我们的一个分层的DockerImage就创建完成了。

自定义Layer

如果我们需要自定义Layer该怎么做呢?

我们可以创建一个独立的layers.xml文件:

<layers xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-2.3.xsd">
<application>
<into layer="spring-boot-loader">
<include>org/springframework/boot/loader/**</include>
</into>
<into layer="application" />
</application>
<dependencies>
<into layer="snapshot-dependencies">
<include>*:*:*SNAPSHOT</include>
</into>
<into layer="company-dependencies">
<include>com.flydean:*</include>
</into>
<into layer="dependencies"/>
</dependencies>
<layerOrder>
<layer>dependencies</layer>
<layer>spring-boot-loader</layer>
<layer>snapshot-dependencies</layer>
<layer>company-dependencies</layer>
<layer>application</layer>
</layerOrder>
</layers>

怎么使用这个layer.xml呢?

添加到build plugin中就可以了:

    <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/src/main/resources/layers.xml</configuration>
</layers>
</configuration>
</plugin>
</plugins>
</build>

本文的例子:springboot-with-docker

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/springboot-docker-image/

本文来源:flydean的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

使用Spring Boot创建docker image的更多相关文章

  1. Spring Boot 创建 Docker 镜像

    随着越来越多的组织转向容器和虚拟服务器,Docker正成为软件开发工作流程中一个更重要的部分.为此,Spring Boot 2.3中最新的功能之中,提供了为Spring Boot应用程序创建 Dock ...

  2. spring cloud教程之使用spring boot创建一个应用

    <7天学会spring cloud>第一天,熟悉spring boot,并使用spring boot创建一个应用. Spring Boot是Spring团队推出的新框架,它所使用的核心技术 ...

  3. Spring Boot 和 Docker 实现微服务部署

    Spring boot 开发轻巧的微服务提供了便利,Docker 的发展又极大的方便了微服务的部署.这篇文章介绍一下如果借助 maven 来快速的生成微服务的镜像以及快速启动服务. 其实将 Sprin ...

  4. Spring Boot with Docker

    翻译自:https://spring.io/guides/gs/spring-boot-docker/ Spring Boot with Docker 这篇教程带你一步步构建一个Docker镜像用来运 ...

  5. spring boot——结合docker

    spring boot——结合docker 前言 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 liunx机器上,也可以实现虚 ...

  6. 利用spring boot创建java app

    利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...

  7. Spring Kafka整合Spring Boot创建生产者客户端案例

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 创建一个kafka-producer-master的maven工程.整个项目结构如下: ...

  8. 【spring boot】5.spring boot 创建web项目并使用jsp作前台页面

    贼烦的是,使用spring boot 创建web项目,然后我再idea下创建的,but 仅仅启动spring boot的启动类,就算整个项目都是好着的,就算是能够进入controller中,也不能成功 ...

  9. Spring Boot 创建hello world项目

    Spring Boot 创建hello world项目 1.创建项目 最近在学习Spring Boot,这里记录使用IDEA创建Spring Boot的的过程 在1出勾选,选择2,点击Next 这里填 ...

随机推荐

  1. 如何PJ IDEA

    1.打开:http://idea.medeming.com/jets/,点击下载PJ码 2.下载完成后解压,会得到2个文件 3.启动IDEA时,将PJ码复制到: 点击ok,就可进入idea中.接着PJ ...

  2. React 和 VUE 的区别和优缺点

    前言 React 是由Facebook创建的JavaScript UI框架,React推广了 Virtual DOM( 虚拟 DOM )并创造了 JSX 语法.JSX 语法的出现允许我们在 javas ...

  3. 解决ExcelReport导出Excel报Number of rules must not exceed 3错误的问题

    报错信息: Number of rules must not exceed 3 [ArgumentException: Number of rules must not exceed 3] NPOI. ...

  4. 【python】调用sm.ms图床api接口,实现上传图片并返回url

    图床简介 sm.ms网站提供免费的图床服务.单图上传大小限制5MB,每次最多上传10张,支持多种图片链接格式和api接口调用. 获取令牌 注册账号并登录,点击User-Dashboard 点击API ...

  5. mariadb 2

    mariadb第二章-增删改   MariaDB 数据类型 MariaDB数据类型可以分为数字,日期和时间以及字符串值. 使用数据类型的原则:够用就行, 尽量使用范围小的,而不用大的 常用的数据类型 ...

  6. 云计算openstack核心组件——cinder存储服务(11)

    一.cinder 介绍:   理解 Block Storage 操作系统获得存储空间的方式一般有两种: 通过某种协议(SAS,SCSI,SAN,iSCSI 等)挂接裸硬盘,然后分区.格式化.创建文件系 ...

  7. netty字节分包

    高并发压测时,发现来自网关的消息出现粘包现象:分包就是势在必行的 前置和处理平台(暂时)使用netty通话,由于都是服务器平台使用DelimiterBasedFrameDecoder来解决分包 和网关 ...

  8. 2020 计蒜之道 预赛 第三场 石子游戏(简单)(暴力DP)

    石子游戏(简单) 原题链接 思路: 通过形式容易看出是一道DP.其中异或和的情况只有64种,所以我们可以开一维来记录当前异或和的状态. 利用dp[当前位置][异或和][是否选择当前]来进行状态转移.时 ...

  9. FreeSWITCH 处理Refer盲转时,UUI传递不对(没有将SIP 消息头Refer-To中的User-to-User传递给B-Leg)

    运行环境:     CentOS 7.6     FreeSWICH 1.6.18   一.问题场景:     FreeSWITCH收到REFER命令后,重新发起的INVITE消息中的 "U ...

  10. 坚果云+svn实现异地非局域网个人代码版本管理

    原理大概是A地的设备作为服务端创建仓库,将仓库传上坚果云,同步到B地,再拉取仓库的代码