关于spring-boot-编程思想:使用Spring-Boot创建docker-image

简介

在很久很久以前,咱们是怎么创立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的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理