iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
Kubernetes

從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台系列 第 3

[Day 3] 容器化實踐 (一):Spring Boot 3 的 Docker 化與最佳化 —— 從多階段構建到 JVM 參數,優化 Java 容器體質。

  • 分享至 

  • xImage
  •  

Day 3: 容器化實踐 (一):Spring Boot 3 的 Docker 化與最佳化

從多階段構建到 JVM 參數,優化 Java 容器體質。

1. 為什麼要容器化?

環境一致性是 CI/CD 的基石。對 Java 應用來說,不只要顧編譯產物 (JAR),還要顧 JVM 版本跟資源限制。「在我電腦上可以跑啊」這句工程師名言,就讓 Docker 來終結它吧。

2. 多階段構建 (Multi-stage Build) 策略

Java 容器化最大的坑就是 Image 太肥:Maven + JDK + 原始碼全部打包進去,動輒 800MB 起跳,推個 Image 都要等到天荒地老。解法是多階段構建——用 maven:3.9-eclipse-temurin-21 編譯,最後只把 JAR 複製進輕量的 eclipse-temurin:21-jre 運行環境。

這是 services/user-service/Dockerfile 的實際內容(三個 Stage):

# ---- Build Stage ----
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app

# 先只複製 pom.xml 下載依賴(善用 Docker Layer 快取)
COPY pom.xml .
RUN mvn dependency:go-offline -B

# 再複製原始碼進行編譯
COPY src/ ./src/
RUN mvn package -DskipTests -B

# ---- Agent Downloader ----
FROM alpine:latest AS agent-downloader
WORKDIR /agent
RUN apk add --no-cache curl
RUN curl -L https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar -o opentelemetry-javaagent.jar

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

# 安全性:以非 Root 用戶運行
RUN groupadd --gid 1001 appgroup && \
    useradd --uid 1001 --gid appgroup --shell /bin/sh appuser

COPY --from=builder /app/target/*.jar app.jar
COPY --from=agent-downloader /agent/opentelemetry-javaagent.jar .

USER appuser

三個重點:

  1. COPY pom.xmlCOPY src/ 分開:只要 pom.xml 沒變,mvn dependency:go-offline 這層就會命中快取,改一行程式碼不用重新下載整個 Maven 倉庫。
  2. 獨立的 agent-downloader stage:OpenTelemetry Java Agent(Day 24 分布式追蹤會用到)在獨立階段下載,不污染最終 Image。
  3. 非 Root 用戶運行USER appuser 是容器安全的基本功,之後 K8S 的 SecurityContext 也會感謝你。

3. Layer 快取的威力

第一次建置時,Maven 要下載所有依賴,我去泡了杯咖啡回來還在跑。那第二次建置(程式碼沒變)呢?直接看終端機輸出:

https://ithelp.ithome.com.tw/upload/images/20260805/20182549AoXLUmWUDC.png

▲ docker build 第二次建置:所有 Layer 命中 CACHED,整體只花 0.1 秒

所有步驟都顯示 CACHED,連最重的 mvn dependency:go-offlinemvn package 都是,整個建置 1 秒內結束。第一次看到的時候真的只有一個感想:太神啦。

這就是為什麼 Dockerfile 的指令順序這麼重要——變動頻率低的層(依賴下載)放前面,變動頻率高的層(原始碼)放後面,快取才吃得到。

4. JVM 在容器中的注意事項

  • 記憶體感知:Java 8u191+ / Java 11+ 的 JVM 已支援 UseContainerSupport(預設開啟),會根據容器的 cgroup 限制自動計算 Heap 大小。Java 21 在這塊已經很成熟。
  • 資源限制先留伏筆:K8S 的 requests/limits-XX:MaxRAMPercentage 怎麼搭配,Day 28 效能調優會深入聊。
  • 啟動速度:Spring Boot 3 支援 AOT 與 CDS,如果你的 Pod 需要快速擴容,值得研究。

5. 小結

今天我們把 Java 服務裝進了容器:多階段構建控制 Image 大小、Layer 快取把重建時間壓到秒級。明天輪到 Python——FastAPI + Delta Lake 的大數據容器,那邊有一個讓我卡了一陣子的 Schema 坑等著各位,敬請期待(?)。


上一篇
[Day 2] 系統架構全覽:Java + Python + React 的異構方案 —— 解析異構技術棧如何截長補短,打造高效分析平台。
系列文
從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言