為 Spring Boot 生成 Docker image(Multi-stage build)

古古

2025/05/22


本文介紹要如何使用 Dockerfile 的 Multi-stage build 寫法,為 Spring Boot 生成更精簡的 Docker image。

什麼是 Multi-stage build? #

在生成 docker image 時,通常會透過撰寫 Dockerfile 來生成,而 Dockerfile 有兩種寫法可以用,一種就是用基本的 Dockerfile 寫法,另一種則是更進階的 Multi-stage build 寫法。

針對這種需要編譯的程式語言(ex: Java、C++),用 Multi-stage build 會更好,因為 Multi-stage build 的概念是「依序 build 多個 image,但是只取最終的 image 來生成」

所以就可以先在前面的 Build stage 中先編譯 Spring Boot 檔案,此時在這個 image 裡面會有許多跟「執行」無關的檔案,像是 src、pom.xml、target 資料夾中的一堆編譯檔…等等,這些檔案其實跟執行無關,跟執行真正有關的就只有 .jar 檔而已。

而在 Build stage 執行完之後,就可以馬上把這個 .jar 檔拉到 Package stage 裡面,並且最終只生成 Package stage 的 image 出來,這樣子中間那些編譯檔就不用也被包進 image 裡面,徒增 image 大小了,讚!!

另外補充一下,其實 Build、Package 這些名稱完全就是自己想叫什麼就叫什麼,沒有任何規範,只是一個概念而已,實際上可以有多個 stage,並且每個 stage 的名字可以自由取名。

在 Spring Boot 中使用 Multi-stage build 來生成 image #

要在 Spring Boot 使用 Dockerfile 來 build image 的話,首先要先在 Spring Boot 的資料夾底下創建一個 Dockerfile (注意是要放在和 src 平行的層級,不是放在 src 裡面)。

然後就可以在 Dockerfile 中撰寫 Multi-stage build 的程式:

# -- build stage --
# 在 build 階段可以挑選 maven:3.9.0-eclipse-temurin-17 當作基底 image,他裡面已經預裝好 Maven 和 Java 17,讚!(所以挑 image 真的也是一門藝術,挑到對的 image 就可以省去很多自己安裝的步驟)
# 記得最後面一定要加 AS xxx,這樣才能使用 Multi-stage build 的功能
FROM maven:3.9.0-eclipse-temurin-17 AS build

# 拉取必要的 source code 和 pom.xml 進到 image 裡面(即是把 src 底下的所有程式複製到 image 的 /root/src 中,把 pom.xml 複製到 image 的 /root 裡)
# 至於如何知道要放在 /root 下而不是放在 /usr 下,下面會講
COPY src /root/src
COPY pom.xml /root

# cd 到 /root 資料夾裡面,並且執行 mvn build Spring Boot
WORKDIR /root
RUN mvn clean package -Dmaven.test.skip=true

# -- package stage --
# 因為在執行階段其實只需要 jre 就好,不需要整個 jdk 都拉進來,所以這裡採用的是 eclipse-temurin:17-jre,可以讓 image size 變得更小,選 image 的藝術 again
FROM eclipse-temurin:17-jre

# 偷 build 階段生成好的 .jar 檔進到此 image 裡面,所以就是複製 build 階段的 /root/target/*.jar 過來,並且將他改名成 app.jar
COPY --from=build /root/target/*.jar app.jar

# 聲明要開啟 8080 port
EXPOSE 8080

# 指定運行此 package image 的指令
CMD ["java", "-jar", "app.jar"]

寫好之後就可以在 Spring Boot 中開啟 terminal,然後執行 docker build -t my/springboot:1.0.0 .,表示要運行當前的 Dockerfile,生成此 Spring Boot 程式的 image 出來(注意此處只會生成最終的 package image 出來,前面的 build image 就會被丟掉了)。

此時就會在本機中生成一個 my/springboot:1.0.0 的 image 出來,接著只要執行 docker run -p 8080:8080 my/springboot:1.0.0,就可以用 docker 執行 Spring Boot 程式了,讚!

如何知道要把 src 複製到 /root/src 底下? #

在撰寫 Dockefile image 時,最常見的困擾就是「不知道要把檔案複製到哪裡去」。

舉例來說,像是在上面的 build 階段時,就必須要把 Spring Boot 的 src 複製到 build image 中的 /root/src 底下,然後要把 pom.xml 複製到 /root 底下:

# 使用 maven:3.9.0-eclipse-temurin-17 當做基底 image
FROM maven:3.9.0-eclipse-temurin-17

# 複製 src 底下的程式到 image 的 /root/src 中,複製 pom.xml 複製到 image 的 /root 中
COPY src /root/src
COPY pom.xml /root

之所以會知道要複製到這些位置,除了靠經驗傳承之外,其實也可以透過爬 Docker Hub 上的 image 介紹得知。

maven:eclipse-temurin 為例,在 maven image 中的第 27、28 行就有定義 ARG USER_HOME_DIR=/rootENV MAVEN_CONFIG=/root/.m2,所以其實這個 image 預設的 user home 就是 /root,而不是 /usr,因此才會把 src 程式複製到 /root 底下。

所以如果想要客製化自己的 settings.xml 的話,那就要把 setting.xml 複製到 /root/.m2/setting.xml 的位置,這樣才會真的生效!

因此在用任何 image 時,建議也可以來 Docker Hub 中查看 image 的介紹,可以更透徹的了解這個 image 中的系統結構。

結語 #

這篇文章我們介紹了如何使用 Dockerfile 去生成 Spring Boot 的 image,並且採用了 Multi-image build 的技術,減少最終生成的 image 大小。

如果你對後端技術有興趣的話,也歡迎免費訂閱《古古的後端筆記》電子報,每週二為你送上一篇後端技術分享,那我們就下一篇文章見啦!

補充:我開設的 Spring Boot 零基礎入門Spring Security 零基礎入門GitHub 免費架站術 已在 Hahow 平台上架啦!輸入折扣碼「HH202505KU」即可享 85 折優惠。

免費訂閱《古古的後端筆記》電子報

每週二學習後端技術,和 3400 人一起變強💪