使用Spring Boot創(chuàng)建docker image

簡介

在很久很久以前,我們是怎么創(chuàng)建Spring Boot的docker image呢?最最通用的辦法就是將Spring boot的應(yīng)用程序打包成一個fat jar,然后寫一個docker file佳恬,將這個fat jar制作成為一個docker image然后運行寞蚌。

今天我們來體驗一下Spring Boot 2.3.3 帶來的快速創(chuàng)建docker image的功能。

傳統(tǒng)做法和它的缺點

現(xiàn)在我們創(chuàng)建一個非常簡單的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";

? ? }

}


福利 福利 福利 免費領(lǐng)取Java架構(gòu)技能地圖 注意了是免費送

?麸折、???

點擊我獲取

?

默認情況下锡凝,我們build出來的是一個fat jar:springboot-with-docker-0.0.1-SNAPSHOT.jar

我們解壓看一下它的內(nèi)容:

?

Spring boot的fat jar分為三個部分,第一部分就是BOOT-INF, 里面的class目錄放的是我們自己編寫的class文件垢啼。而lib目錄存放的是項目依賴的其他jar包窜锯。

第二部分是META-INF,里面定義了jar包的屬性信息芭析。

第三部分是Spring Boot的類加載器锚扎,fat jar包的啟動是通過Spring Boot的jarLauncher來創(chuàng)建LaunchedURLClassLoader,通過它來加載lib下面的jar包馁启,最后以一個新線程啟動應(yīng)用的Main函數(shù)驾孔。

這里不多講Spring Boot的啟動。

我們看一下,如果想要用這個fat jar來創(chuàng)建docker image應(yīng)該怎么寫:

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"]

復(fù)制代碼

這樣寫有兩個問題助币。

第一個問題:我們是用的far jar浪听,在使用far jar的過程中會有一定的性能問題,肯定要比解壓過后的性能要低眉菱,尤其是在容器環(huán)境中運行的情況下迹栓,可能會更加突出。

第二個問題:我們知道docker的image是按layer來構(gòu)建的俭缓,按layer構(gòu)建的好處就是可以減少image構(gòu)建的時間和重用之前的layer克伊。

但是如果使用的是fat jar包,即使我們只修改了我們自己的代碼华坦,也會導(dǎo)致整個fat jar重新更新愿吹,從而影響docker image的構(gòu)建速度。

使用Buildpacks

傳統(tǒng)的辦法除了有上面的兩個問題惜姐,還有一個就是需要自己構(gòu)建docker file犁跪,有沒有一鍵構(gòu)建docker image的方法呢?

答案是肯定的歹袁。

Spring Boot在2.3.0之后坷衍,引入了Cloud Native 的buildpacks,通過這個工具条舔,我們可以非常非常方便的創(chuàng)建docker image枫耳。

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

我們可以直接運行:

mvn? spring-boot:build-image

復(fù)制代碼

運行之孟抗,很不幸的是迁杨,你可能會遇到下面的錯誤:

[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]

復(fù)制代碼

這是因為我們無法從gcr.io中拉取鏡像!

沒關(guān)系凄硼,如果你會正確的上網(wǎng)方式的話铅协,那么我估計你已經(jīng)找到了一個代理。

將你的代理配置到Docker的代理項里面帆喇,我使用的是Docker desktop,下面是我的配置:

?

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

等待執(zhí)行結(jié)果:

[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%

復(fù)制代碼

你可以看到警医,我們的確是需要從gcr.io拉取image。

Layered Jars

如果你不想使用Cloud Native Buildpacks坯钦,還是想使用傳統(tǒng)的Dockerfile。 沒關(guān)系侈玄,SpringBoot為我們提供了獨特的分層jar包系統(tǒng)婉刀。

怎么開啟呢? 我們需要在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>

復(fù)制代碼

再次打包序仙,看下jar包的內(nèi)容:

?

看起來和之前的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/"

復(fù)制代碼

index文件主要分為4個部分:

dependencies - 非SNAPSHOT的依賴jar包

snapshot-dependencies - SNAPSHOT的依賴jar包

spring-boot-loader - Spring boot的class loader文件

application - 應(yīng)用程序的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

復(fù)制代碼

使用list命令棒动,我們可列出jar包中的layer信息糙申。使用extract我們可以解壓出不同的layer。

我們執(zhí)行下extract命令船惨,看下結(jié)果:

?

可以看到柜裸,我們根據(jù)layers.idx解壓出了不同的文件夾。

我們看一下使用layer的dockerFile應(yīng)該怎么寫:

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"]

復(fù)制代碼

這樣我們的一個分層的DockerImage就創(chuàng)建完成了粱锐。

自定義Layer

如果我們需要自定義Layer該怎么做呢疙挺?

我們可以創(chuàng)建一個獨立的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>

復(fù)制代碼

怎么使用這個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>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怜浅,一起剝皮案震驚了整個濱河市铐然,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恶座,老刑警劉巖锦爵,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奥裸,居然都是意外死亡险掀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門湾宙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來樟氢,“玉大人,你說我怎么就攤上這事侠鳄〔嚎校” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵伟恶,是天一觀的道長碴开。 經(jīng)常有香客問我,道長博秫,這世上最難降的妖魔是什么潦牛? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮挡育,結(jié)果婚禮上巴碗,老公的妹妹穿的比我還像新娘。我一直安慰自己即寒,他們只是感情好橡淆,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布召噩。 她就那樣靜靜地躺著,像睡著了一般逸爵。 火紅的嫁衣襯著肌膚如雪具滴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天师倔,我揣著相機與錄音构韵,去河邊找鬼。 笑死溯革,一個胖子當(dāng)著我的面吹牛贞绳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播致稀,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼冈闭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抖单?” 一聲冷哼從身側(cè)響起萎攒,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矛绘,沒想到半個月后耍休,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡货矮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年羊精,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片囚玫。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡喧锦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抓督,到底是詐尸還是另有隱情燃少,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布铃在,位于F島的核電站阵具,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏定铜。R本人自食惡果不足惜阳液,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宿稀。 院中可真熱鬧趁舀,春花似錦、人聲如沸祝沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罩锐。三九已至奉狈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涩惑,已是汗流浹背仁期。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竭恬,地道東北人跛蛋。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像痊硕,于是被迫代替她去往敵國和親赊级。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361