前言
在很久很久以前,我們是怎么創建Spring Boot的docker image呢?最最通用的辦法就是將Spring boot的應用程序打包成一個fat jar,然后寫一個docker file,將這個fat jar制作成為一個docker image然后運行。
今天我們來體驗一下Spring Boot 2.3.3 帶來的快速創建docker image的功能。
傳統做法和它的缺點
現在我們創建一個非常簡單的Spring Boot程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@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應該怎么寫:
1
2
3
4
5
|
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
我們可以直接運行:
1
|
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文件中加上下面的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<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文件:
1
2
3
4
5
6
7
8
9
10
|
- "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進行校驗或者解壓縮:
1
2
3
4
5
6
7
8
|
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應該怎么寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
< 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中就可以了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<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
總結
到此這篇關于利用Spring Boot創建docker image完整步驟的文章就介紹到這了,更多相關Spring Boot創建docker image內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:http://www.flydean.com/springboot-docker-image/