iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0

昨天,我們完成了註冊功能相關的 DTO、Service 、 Controller 與Spring Security 的設定內容,走完了開發帳號註冊 API的流程。

不過,過程中我們沒有在本機安裝資料庫,或在IDE上新增資料庫連線設定。

因為我希望在這次開發的過程中,透過 Docker 來啟動相依的服務,取代需要手動安裝在電腦上的過程。對於這個專案來說,這麼做的好處有:

  • 環境一致性:打包成 image 後,可確保開發、測試到最終部署的環境都完全相同,避免在不同電腦上無法執行的窘境。
  • 避免版本衝突:未來若有其他專案需要不同版本的 PostgreSQL,它們可以各自存在於獨立的容器中,互不干擾。
  • 對齊上雲目標:專案之後打算部署到雲端並使用 Cloud SQL,因此在本機開發階段,使用容器化的資料庫就足以滿足需求,無需在本機安裝完整的資料庫軟體。

關於容器化,我也是一知半解,選擇了最常見的 Docker ,透過建立這個專案的過程,來慢慢摸索這個工具的使用方式,所以文中若有筆誤之處,由衷歡迎各位提出修正的建議!!

總之,今天我們會先介紹 Docker 中幾個比較重要的組件。而後說明如何使用 Docker 來為 auth-service 這項服務本身建立標準化的運行環境。

考量到篇幅,明天再介紹如何透過 docker-compose 來進行多容器管理,一鍵啟動所有服務(屆時也就會看到今天所提到的:如何透過容器啟用我們的資料庫服務)。最後再用 API 測試工具來驗證我們過去幾天的成果!

簡易的 Docker 說明:以簡單的建築工程為例

在撰寫 Dockerfiledocker-compose.yml 之前,想用一個建築工程的比喻,來理解這幾個核心概念之間的關係:

  • Dockerfile:施工說明書

    一份文字檔,裡面記錄了如何從無到有,一步步蓋出房子的所有指令與步驟(例如:先打地基、再砌磚、然後安裝門窗)。

  • Image (映像檔)房屋的設計藍圖

    按照 Dockerfile(施工說明書)中的說明繪製出的房屋設計圖。

    是一個靜態且唯讀的 (read-only) 模板,裡面說明了房子本身(應用程式)以及所有必要的傢俱和管線(例如作業系統、JRE、各種函式庫)。

  • Container (容器)根據藍圖實際蓋好的房子

    根據 Image 內容建立的實例。您可以根據同一份藍圖,蓋出許多棟一模一樣、且互相獨立的房子。房子蓋好後,人們(請求)才能進去活動,它是動態的。

  • docker-compose整個社區的開發計畫書

    僅有房子(應用程式本身)的社區是不能運作的,鄰近還會需要發電廠(好比資料庫服務 )、區公所(提供前端介面的服務)等,docker-compose 就像包含這些依賴的開發計畫書。它定義了:

    1. 社區需要哪些建築物、服務單位 (services)。
    2. 這些建築物或單位間的道路該如何連接 (network)。
    3. 建築與單位建立的先後順序 (depends_on)。

    有了這份計畫書,我們就可以用一個指令 (docker-compose up),一鍵完成整個社區的建造與啟動。

總結這些組件的關聯如下,括弧中的英文代表執行指令:

Dockerfile (說明書) -- (build) --> Image (藍圖) -- (run) --> Container (實際的房子)

docker-compose (社區計畫書) -- (up) --> 管理多個 Container (整個社區)

因為篇幅的關係,無法在這篇文章裡很完整的說明容器化的概念,有興趣的朋友可參考網路大神們的介紹。

auth-service dockerfile

我們在 auth-service 資料夾下建立 Dockerfile,其內容如下:

# ---- Build Stage ----

# 指定執行環境映像檔
FROM maven:3.9-eclipse-temurin-21 AS build

# 指定容器內的工作目錄
WORKDIR /app

# 1. 複製專案 POM 檔
# 因為根目錄是專案目錄,所以來源路徑是 'pom.xml'
COPY pom.xml .

# 2. 複製 auth-service 模組的 POM 檔
COPY auth-service/pom.xml ./auth-service/

# 3. 為了利用快取,先下載依賴
# 使用 -f 參數,明確指定 Maven 使用子模組的 POM 檔來下載依賴
RUN mvn -f auth-service/pom.xml dependency:go-offline

# 4. 複製 auth-service 模組的原始碼
# 來源路徑是 'auth-service/src',目標路徑是容器內的 './auth-service/src'
COPY auth-service/src ./auth-service/src

# 5. 執行 Maven 打包指令
# 這次直接在根目錄的 pom.xml 上下文執行,並指定只打包 auth-service
RUN mvn -pl auth-service -am clean package -DskipTests

# ---- Run Stage ----
FROM eclipse-temurin:21-jre
WORKDIR /app

# 從 build 階段,複製打包好的 .jar 檔
COPY --from=build /app/auth-service/target/*.jar app.jar

# 啟動容器時,執行上面那個打包好的jar檔
ENTRYPOINT ["java", "-jar", "app.jar"]

Docker 關鍵字說明

關於以上 Dockerfile 中常見的關鍵字作用說明,可以參考以下表格:

關鍵字 作用
FROM 指定此階段要基於哪個基礎映像檔 (Base Image)(也就是執行環境)來建置。
AS 為建置階段取別名,方便後續階段引用。
WORKDIR 設定容器內後續指令的工作目錄 (Working Directory)。
COPY 從本機複製檔案到容器內。
RUN 在容器內執行 Shell 指令。
ENTRYPOINT 設定容器啟動時會執行的指令。

Docker 分層快取機制(Layer Caching)

Docker 在建置映像檔時,是逐行執行 Dockerfile 中的指令的。每一行指令都會建立一個分層 (Layer)。

當 Docker 重新建置時,它會檢查每一行的指令及其依賴的檔案是否有變動。如果沒有變動,Docker 就會直接使用上次建置時留下的快取分層,而不會重新執行該指令。

我們的依賴 (pom.xml) 通常不常變動,而原始碼 (src 資料夾) 則會頻繁修改。因此,我們先 COPY pom.xml 並 RUN 下載依賴,再 COPY src。這樣一來,只要 pom.xml 沒變,即使我們修改了 Java 程式碼,Docker 在重建時也可以跳過依賴下載步驟,直接使用快取,大幅提升建置效率。

多階段建置(Multi-stage build)

這裡採用 Multi-stage 的方式建立Dockerfile,這兩個 stage 的分別是:

  • Build Stage (建構階段)
    • 這個階段使用完整 Maven 和 JDK 的映像檔,它的任務是:編譯並打包我們的應用程式
  • Run Stage (運行階段)
    • 這個階段使用了一個體積小很多的 JRE (Java Runtime Environment) 映像檔,它只包含運行 Java 程式所需的最小環境。
    • 只從 build 階段複製了打包好的那個 .jar 檔並執行。

這麼做的好處除了建構過程更清晰外,還可以:

  • 大幅縮小最終映像檔體積
    最終的映像檔只會根據 Run Stage 的內容來建立。所有在 Build Stage 中使用到的肥大基礎映像檔、建構工具、原始碼和中間產物,都會被完全拋棄,不會佔用任何空間。
  • 提升安全性
    最終的 Run Stage 是從一個極簡的 JRE 環境開始建立的,不包含原始碼、JDK 或任何建構工具 (如 Maven)。這大幅減少了容器的攻擊面,即使被入侵,攻擊者能利用的工具和資訊也極其有限。

因為這個服務依賴資料庫服務,因此今天撰寫好dockerfile後暫不執行,明天我們完成docker-compose後再啟動服務進行測試。


上一篇
Day 6:Auth Service - 實作註冊功能(業務邏輯與API端點)
下一篇
Day 8:docker-compose
系列文
吃出一個SideProject!8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言