Spring Boot Docker入門模板與4個最佳實踐

banq發表於2024-02-24

在本部落格中,您將學習一些主要針對 Spring Boot 應用程式的 Docker 最佳實踐。您將透過將這些實踐應用到示例應用程式來學習這些實踐。享受!

入門模板
將用作入門模板起點的 Dockerfile 如下:


FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e
WORKDIR /opt/app
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
ARG JAR_FILE
COPY target/${JAR_FILE} app.jar
RUN chown -R javauser:javauser .
USER javauser
ENTRYPOINT [<font>"java", "-jar", "app.jar"]

這個 Dockerfile 執行以下操作:

  • FROM:以eclipse-temurin:17Java Docker映象為基礎映象;
  • WORKDIR:設定/opt/app為工作目錄;
  • RUN:建立系統組和系統使用者;
  • ARG:提供一個引數JAR_FILE,這樣就不必將 jar 檔名硬編碼到 Dockerfile 中;
  • COPY:將jar檔案複製到Docker映象中;
  • RUN:將 的所有者更改WORKDIR為之前建立的系統使用者;
  • USER:確保使用之前建立的系統使用者;
  • ENTRYPOINT:啟動 Spring Boot 應用程式。

在接下來的部分中,您將更改此 Dockerfile 以遵循最佳實踐。每個段落生成的 Dockerfile 可在 git 儲存庫的Dockerfiles目錄中找到。在每個段落的末尾,將在適用的情況下提及相應的最終 Dockerfile 的名稱。

本部落格中使用的程式碼可在GitHub上找到。

1、健康檢查
將執行狀況檢查新增到 Dockerfile 中,以暴露容器的執行狀況。根據此狀態,可以重新啟動容器。這可以透過HEALTHCHECK命令來完成。新增以下健康檢查:

EALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http:<font>//localhost:8080/actuator/health/ | grep UP || exit 1<i>

  • interval間隔:每 30 秒執行一次癒合檢查。對於生產應用,最好選擇 5 分鐘這樣的間隔。為了進行一些測試,選擇一個較小的值會比較容易。這樣就不必每次都等待五分鐘。
  • timeout超時:執行健康檢查的三秒超時。
  • retries重試:表示在健康狀態發生變化之前必須連續執行的檢查次數。預設值為 3,這在生產中是個不錯的數字。出於測試目的,可將其設定為一次,這意味著在一次不成功的檢查後,健康狀態將變為不健康。
  • command命令:Spring 激勵器端點將用作健康檢查。獲取響應並將其匯入 grep,以驗證健康狀態是否為 UP。建議不要為此目的使用 curl,因為並非每個映象都有 curl。你需要在映象中額外安裝 curl,這會使映象增大數 MB。


2、Docker Compose
Docker Compose 為您提供了用一條命令同時啟動多個容器的機會。除此之外,它還能讓你記錄你的服務,即使你只有一個服務需要管理。Docker Compose 過去是與 Docker 分開安裝的,但如今它已成為 Docker 本身的一部分。你需要編寫一個包含該配置的 compose.yml 檔案。讓我們看看在健康檢查中使用的兩個容器是如何配置的。

services:
  dockerbestpractices:
    image: mydeveloperplanet/dockerbestpractices:0.0.1-SNAPSHOT
 
  autoheal:
    image: willfarrell/autoheal:1.2.0
    restart: always
    environment:
      AUTOHEAL_CONTAINER_LABEL: all
    volumes:
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock

配置了兩個服務(讀作:容器)。一個用於 dockerbestpractices 映象,一個用於 autoheal 映象。自動修復映象會在重啟後重新啟動,它定義了一個環境變數,並掛載了一個卷。

在可以找到 compose.yml 檔案的目錄下執行以下命令:
$ docker compose up

在日誌記錄中,你會看到兩個容器都已啟動。開啟另一個終端視窗,導航到可以找到 compose.yml 的目錄。很多命令都可以與 Docker Compose 結合使用。

3、多階段構建
有時,在 Docker 容器中構建應用程式會很方便。這樣做的好處是,你不需要在系統中安裝完整的開發環境,而且可以更方便地交換開發環境。不過,在容器內構建應用程式也有一個問題。尤其是當您想使用同一個容器執行應用程式時。原始碼和完整的開發環境將出現在生產容器中,從安全形度來看,這不是一個好主意。

您可以編寫單獨的 Dockerfile 來規避這個問題:一個用於構建,一個用於執行應用程式。但這相當麻煩。解決辦法是使用多階段構建。

使用多階段構建,可以將構建階段與執行階段分開。Dockerfile 檔案如下:

FROM maven:3.8.6-eclipse-temurin-17-alpine@sha256:e88c1a981319789d0c00cd508af67a9c46524f177ecc66ca37c107d4c371d23b AS builder
WORKDIR /build
COPY . .
RUN mvn clean package -DskipTests
 
FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e
WORKDIR /opt/app
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
COPY --from=builder /build/target/mydockerbestpracticesplanet-0.0.1-SNAPSHOT.jar app.jar
RUN chown -R javauser:javauser .
USER javauser
HEALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http:<font>//localhost:8080/actuator/health/ | grep UP || exit 1<i>
ENTRYPOINT [
"java", "-jar", "app.jar"]


如您所見,該 Dockerfile 包含兩條 FROM 語句。第一條用於構建應用程式:

  • FROM:包含 Maven 和 Java 17 的 Docker 映象,這是構建應用程式所需的;
  • WORKDIR:設定工作目錄;
  • COPY:將當前目錄複製到容器中的工作目錄;
  • RUN:構建 jar 檔案的命令。

FROM 語句中還新增了其他內容。最後,新增 AS 生成器。這樣,這個容器就有了標籤,可以用來構建執行應用程式的映象。第二部分與之前的 Dockerfile 完全相同,除了兩行。

刪除以下兩行:
ARG JAR_FILE
COPY target/${JAR_FILE} app.jar

這幾行確保將本地構建的 jar 檔案複製到映象中。這幾行被替換為下面一行:
COPY --from=builder /build/target/mydockerbestpracticesplanet-0.0.1-SNAPSHOT.jar app.jar

透過這一行,您可以表明要將檔案從生成容器複製到新映象中。

當你構建這個 Dockerfile 時,你會發現構建容器會執行構建,最後建立了用於執行應用程式的映象。在構建映象的過程中,你還會注意到所有 Maven 依賴項都已下載。

生成的 Dockerfile 可在 git 倉庫中找到,名稱為 7-Dockerfile-multi-stage-build。

4、Spring Boot Docker 層

  • Docker 映象由層組成。
  • Dockerfile 中的每一條命令都會產生一個新層。
  • 當你初始化 Docker 映象時,所有層都會被檢索和儲存。
  • 如果你更新了 Docker 映象,但只更改了 jar 檔案,那麼其他層就不會被重新檢索。

這樣,Docker 映象的儲存效率會更高。

但是,在使用 Spring Boot 時,會建立一個胖 jar。
也就是說,當你只修改了部分程式碼時,就會建立一個新的胖jar,其依賴關係保持不變。
因此,每次建立新的 Docker 映象時,都會在新的層中新增幾兆位元組,而沒有任何必要。

簡而言之,Spring Boot 可以將胖 jar 分割成多個目錄:

  • /dependencies
  • /spring-boot-loader
  • /snapshot-dependencies
  • /application

應用程式程式碼將存放在 application 目錄中,而依賴項則存放在 dependencies 目錄中。

為此,您將使用多階段構建。

第一階段將把 jar 檔案複製到 JDK Docker 映象中,然後提取胖 jar。

FROM eclipse-temurin:17.0.4.1_1-jre-alpine@sha256:e1506ba20f0cb2af6f23e24c7f8855b417f0b085708acd9b85344a884ba77767 AS builder
WORKDIR application
ARG JAR_FILE
COPY target/${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

第二部分將把分割的目錄複製到新的映象中。COPY 命令會替換 jar 檔案。

FROM eclipse-temurin:17.0.4.1_1-jre-alpine@sha256:e1506ba20f0cb2af6f23e24c7f8855b417f0b085708acd9b85344a884ba77767
WORKDIR /opt/app
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
RUN chown -R javauser:javauser .
USER javauser
HEALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http:<font>//localhost:8080/actuator/health/ | grep UP || exit 1<i>
ENTRYPOINT [
"java", "org.springframework.boot.loader.JarLauncher"]

構建並執行容器。在執行容器時,你不會注意到任何不同之處。主要優勢在於 Docker 映象的儲存方式。

生成的 Dockerfile 位於 git 倉庫,名稱為 8-Dockerfile-spring-boot-docker-layers。

 

相關文章