將 Java 應用程式容器化

已完成

在此單元中,您會容器化 Java 應用程式。

如先前所述,容器會直接在主機作系統、核心和硬體上執行,就像一般系統進程一樣。 容器需要較少的系統資源,因而產生較小的使用量、較少的額外負荷,以及更快的應用程式啟動時間。 這些好處是隨需擴展的絕佳使用範例。

有 Windows 容器和 Linux 容器。 在本課程模組中,您會使用廣泛使用的 Docker 運行時間來建置 Linux 容器映射。 接著,您會將 Linux 容器映射部署到本機電腦的主機作系統。 最後,您會將Linux容器映像部署至 Azure Kubernetes Service。

Docker 概觀

Docker 運行時間可用來建置、提取、執行和推送容器映射,如下圖所示:

顯示連接用戶端、Docker 主機與登錄之 Docker 建置、提取、推送及執行命令的示意圖。

下表描述每個 Docker 命令:

Docker 命令 說明
docker build 建置容器映像,其中包含 Docker 從映像建立執行中容器所需的指示或層。 此命令的結果是影像。
docker pull 從映像登錄庫下載映像檔到客戶端所選 Docker 守護程序的映像存放區中。 現有的影像圖層可以重複使用。 在此模組中,您的本地 Docker 環境在開發過程中會使用來自容器登錄的映像檔;之後,AKS 節點會從 Azure Container Registry 拉取應用程式映像以供部署。
docker run 從影像建立並啟動一個新的容器。 容器會執行其設定好的啟動流程,但可由命令列覆寫。 影像層提供容器的檔案系統;容器啟動時,dockerfile 建置指令不會再執行。
docker push Azure Container Registry 會儲存映像,使其快速可用,並關閉網路以進行 Azure 部署和調整。

複製Java應用程式

首先,複製航空公司航班預訂系統的存放庫,並導航至航空公司網頁應用程式項目資料夾。

備註

如果在 CLI 索引標籤中完成 Azure Kubernetes Service 建立,請使用該索引標籤。如果仍在執行中,請開啟新的索引標籤,並流覽至您想要複製航空公司預訂航班預訂系統的位置。

執行以下命令:

git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

或者,如果您已安裝 Java 和 Maven,您可以在終端機控制台中執行下列命令,以了解在沒有 Docker 的情況下建置應用程式的體驗。 如果您沒有安裝 Java 和 Maven,可以安全地繼續進行下 一節建構 Docker 檔案。 在該區段中,您會使用 Docker 來提取 JAVA 和 Maven,以代表您執行組建。

mvn clean package

備註

mvn clean package我們使用 命令來說明不使用 Docker 多階段組建的作業挑戰,我們接下來會討論這些挑戰。 同樣地,此步驟是選擇性的。 無論哪種方式,您都可以放心地在不執行 Maven 命令的情況下繼續。

若流程成功執行,Maven 便已成功建置「機票預訂系統」的航空訂位 Web 應用程式封存成品 AirlinesReservationSample-0.0.1-SNAPSHOT.war,如下列歷史輸出所示。 你的建置時間戳記和持續時間可能會有所不同:

[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------

假設您是 Java 開發人員,而且您剛建置 AirlinesReservationSample-0.0.1-SNAPSHOT.war。 您的下一個步驟可能是與作業工程師合作,讓此成品部署至內部部署伺服器或虛擬機。 若要讓應用程式順利啟動並執行,伺服器和虛擬機必須可供使用,並設定必要的相依性。 此過程具有挑戰性且耗時,尤其是在需求量增加時,當應用程式遇到更高負載。 使用容器時,這些挑戰會得到緩解。

建構 Dockerfile

現在您已準備好撰寫 Dockerfile。 Dockerfile 是一份文字檔,其中包含使用者將在命令行上執行的所有命令,以組合容器映像。 容器映像可以包含多個唯讀檔案系統層。 Docker 可以對於未更動的步驟重複使用快取的建置結果。 層次相互堆疊。

例如,航空公司預訂航班預訂系統必須部署至應用程式伺服器內部並執行。 應用程式伺服器未封裝在 AirlinesReservationSample-0.0.1-SNAPSHOT.war 內。 這是 AirlinesReservationSample-0.0.1-SNAPSHOT.war 執行、接聽和處理 HTTP 要求、管理用戶會話以及加速航班預訂所需的外部相依性。 如果您使用傳統的非容器化部署,作業工程師會在部署 AirlinesReservationSample-0.0.1-SNAPSHOT.war 之前,先在某些實體伺服器或虛擬機上安裝及設定應用程式伺服器。 這些作業工程師也需要確保機器上使用的 JDK (mvn clean package 用來編譯 WAR 的工具) 實際上對應至應用程式伺服器所使用的相同 JRE。 管理這些相依性非常困難且耗時。

使用 Dockerfile,您可以撰寫自動完成此目標所需的指示或階層,方法是藉由步驟中的必要圖層,確保適用於航空公司預約的航班預訂系統具有部署至 Docker 容器執行階段所需的所有相依性。 當您以非規劃間隔使用隨選調整時,此解決方案非常具有吸引力。 每一層都會使用 Docker 快取,其中包含每個指示性里程碑的容器映像檔狀態,優化計算時間和重複利用。 如果圖層未變更,則會使用快取的圖層。 快取層的常見使用案例包括 Java 執行環境、應用程式伺服器,以及航空公司訂位 Web 應用程式的航班訂票系統的其他相依性。 如果先前快取的階層上發生版本變更,將會建立新的快取項目。

容器映像包含唯讀的檔案系統層。 在此範例中,Dockerfile 在建置時將 Tomcat 和 Flight Booking System WAR 檔案複製到映像檔中。 Docker 在從映像建立容器時會新增一個獨立的可寫層。 每個容器的可寫層會記錄自身的檔案系統變更,且不修改映像圖層。

備註

對於儲存在容器可寫層的檔案,新增、修改和刪除會影響該容器,而非原始影像。 更改來自映像層的檔案時,會使用寫入時複製(copy-on-write)機制。

Docker 具有多階段組建的概念,這項功能可讓您建立較小的容器映像檔,並且具有更好的快取功能和較小的安全性風險,從而可以隨著時間不斷優化和維護 Dockerfile。 例如,您可以將用於編譯和建置應用程式的容器建置階段與執行應用程式的階段分開。 這項功能可讓您只將建置過程中產生的構建工件複製到生產容器映像檔,以減少佔用。 因為容器映像會快取,如果沒有變更,則可以重複使用快取的映射,降低從網路下載的成本和時間。

在生產環境中公開的服務必須謹慎管理,才能獲得安全性。 因此,生產環境會使用並操作安全的容器映像。 範例使用了 Microsoft 提供的 Azure Linux 映像檔。

最後階段使用 Microsoft Build of OpenJDK 17 在 Azure Linux 3.0 上。 17-azurelinux標籤明確標示這個基底。 舊17-mariner標籤仍是相容別名,對應 Azure Linux 映像;不應與已淘汰的17-mariner-cm1及17-mariner-cm2映像混淆。 欲了解更多資訊,請參閱 Microsoft Build of OpenJDK 容器映像。

備註

Microsoft 提供隨 OpenJDK 捆綁的容器映像檔,包括 UbuntuAzure Linux 及distroless映像檔。 distroless 映像的映像大小為最小,但對其執行 Tomcat 是一項挑戰。 為了達到輕量化設計,distroless 映像移除許多命令和工具,包括 Shell,這表示您無法呼叫 catalina.sh 來啟動 Tomcat。 distroless 映像適用於執行可執行的 JAR,例如搭配 Spring Boot 或 Quarkus 使用的 JAR。

在下列範例中,在建置階段和最終階段中都會使用相同版本的 Microsoft OpenJDK 組建。 此方法可確保您使用服務部署 Tomcat 所使用的相同 JDK 版本來建置原始程式碼,這有助於避免因版本不符而發生非預期的行為。

下圖描述多階段組建,以及根據 Dockerfile 中指定的命令,在每個階段中發生的狀況:

圖示顯示 Dockerfile 指令在 Tomcat 安裝、程式碼建置及執行階段建立映像層。

在第 0 階段,Tomcat 會從版本特定的 Apache 封存下載,使用 SHA-512 校驗和驗證,並解壓縮至 Ubuntu 映像中環境變數所指定的目錄下。 TOMCAT_VERSION 變數指定版本,而 TOMCAT_SHA512 建構參數則指定其預期校驗和。 選擇較新的支援版本時,請同時更新兩個值。 Docker 可以在其指令與輸入不變的情況下重用快取的安裝層。 Tomcat 安裝會被複製到最終階段環境以供使用。

在階段 1 中,Maven 會安裝在 Ubuntu 映射上,而建立的原始程式碼和組態檔會在建置 Maven 專案之前先複製。 每一層都會快取,因此 OS 映像和 Maven 映像層會重複使用快取。 如果組態檔、原始程式碼檔案或 Web 目錄更新,則會重建變更之後的層次。 如果在編譯期間順利完成組建,就會在目標目錄下產生名為AirlinesReservationSample-0.0.1-SNAPSHOT.war的成品。 此成品會複製到最後階段環境以供使用。

在最後階段,來自第 0 階段與第 1 階段的 Tomcat 與 Java 建構構件,會分別被複製到 Azure Linux 3.0 上的 Microsoft Build of OpenJDK 17 映像中。 名為 app 的使用者擁有專案內使用的所有檔案,而且應用程式也會以 app 使用者身分執行,而不是擁有 root 許可權。 此設定可確保容器映像可以安全地運作,而不需要授與不必要的許可權。 最後,會公開埠號碼 8080,並執行 catalina.sh 腳本以啟動Tomcat。 當您的本機 Docker Desktop 上執行此作業時,您可以透過 URL http://localhost:8080/AirlinesReservationSample存取它。

在 containerize-and-deploy-Java-app-to-Azure/Project/Airlines中,使用已安裝的文字編輯器建立名為 Dockerfile的檔案。 新增以下內容並儲存檔案:

Important

在建立公開映像檔前,請先在 Apache Tomcat 下載頁面 查看目前支援的 Tomcat 10.1.x 版本,並檢視 安全通知。 使用 Apache 發布的版本資訊,一起更新 TOMCAT_VERSION 和 TOMCAT_SHA512。 仍可在 Apache 封存取得的成品,並不代表其版本是最新或建議使用的。

############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat

ENV CATALINA_HOME=/usr/local/tomcat

# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb

# Install Tomcat and required packages
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl ca-certificates && \
    curl -fSL \
      "https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
      -o /tmp/tomcat.tar.gz && \
    echo "${TOMCAT_SHA512}  /tmp/tomcat.tar.gz" | sha512sum -c - && \
    mkdir -p "${CATALINA_HOME}" && \
    tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
    rm /tmp/tomcat.tar.gz && \
    apt-get remove --purge -y curl && \
    apt-get autoremove -y && \
    apt-get clean

############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build

WORKDIR /build

# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version

# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web

# Build the project
RUN mvn clean package

############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-azurelinux

# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH

# This is the user that runs the Tomcat process
USER app

# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war

# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]

備註

使用此單元中展示的 Dockerfile 進行此練習。 應用程式必須複製到webapps/AirlinesReservationSample.war,讓以下單元中的 URL 可以使用/AirlinesReservationSample/上下文。 不同的 WAR 檔名會改變預設的 Tomcat 應用程式上下文。

Dockerfile 分成三個階段,如下表所述:

  • Tomcat 安裝階段:

    Docker 命令 說明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat 將基底映像檔設定為在 Ubuntu 上的 Microsoft 組建的 OpenJDK 17,並將此階段命名為 tomcat。 這是安裝 Tomcat 的位置。
    ENV ENV CATALINA_HOME=/usr/local/tomcat 會設定 Tomcat 安裝目錄的環境變數。
    ENV ENV TOMCAT_VERSION=10.1.61 為此範例釘選 Tomcat 版本。 檢視支援版本,並視需要將其與 TOMCAT_SHA512 一併更新。
    ARG ARG TOMCAT_SHA512 提供 Apache 公開的所選封存檔 SHA-512 校驗碼作為建置參數。
    RUN 安裝 curl 並取得 CA 憑證,然後用 curl -fSL 來下載版本專屬的 Apache 封存檔。 -f 遇到 HTTP 錯誤時失敗;-L 追蹤重新導向。 封存檔會先以 CATALINA_HOME 驗證,再使用 sha512sum -c - 解壓縮到 --strip-components=1。 &&鏈在故障時停止,而不是在清理時掩蓋錯誤。 最後,它會移除壓縮檔和不需要的套件,並清理套件快取。
  • 使用Java 17編譯的建置階段:

    Docker 命令 說明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build 將基底映像檔設定為在 Ubuntu 上的 Microsoft 組建的 OpenJDK 17,並將此階段命名為 build。 這個階段用於編譯 Java 應用程式。
    WORKDIR WORKDIR /build 會將容器內的工作目錄設定為 /build,其中會複製和編譯原始程式碼。
    RUN RUN apt-get update && apt-get install -y maven && mvn --version 會安裝 Maven,這是用於 Java 專案的組建自動化工具,並驗證其安裝。
    COPY COPY pom.xml . 會將 Maven 組態檔複製到工作目錄中。 此檔案對於建置專案而言很重要。
    COPY COPY src ./src 將原始碼目錄複製到容器中。 這是 Java 應用程式程式代碼所在的位置。
    COPY COPY web ./web 將 Web 資源目錄複製到容器中。 這包括組建所需的 Web 應用程式資源。
    RUN RUN mvn clean package 會執行 Maven 建置程式,它會編譯 Java 應用程式,並將其封裝成 WAR 檔案。
  • 套件最終階段:

    Docker 命令 說明
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-azurelinux將基礎映像設定為 Azure Linux 3.0 上的 Microsoft Build of OpenJDK 17,用於應用程式的最終部署。
    ENV ENV CATALINA_HOME=/usr/local/tomcat 會設定 Tomcat 安裝目錄的環境變數,類似於安裝階段。
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH 將 Tomcat bin 目錄新增至系統 PATH,以便輕鬆地執行 Tomcat 命令。
    USER USER app 指定 Tomcat 進程執行所在的使用者,藉由不以根使用者身分執行來增強安全性。
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME} 從 tomcat 階段複製Tomcat安裝,並將擁有權設定為 app 使用者。
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf 將 Tomcat 使用者組態檔複製到容器,並將擁有權設定為 app 使用者。
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war 將編譯的 WAR 檔案從 build 階段複製到 Tomcat webapps 目錄,並將擁有權設定為 app 使用者。
    EXPOSE EXPOSE 8080 說明應用程式預期會在 TCP 埠 8080 上監聽。 它不會發佈主機連接埠。 下一個單元會使用docker run上的-p 8080:8080選項來發佈連接埠。
    CMD CMD ["catalina.sh", "run"] 會指定要在執行容器時啟動 Tomcat 的命令。

如需 Dockerfile 建構的詳細資訊,請參閱 Dockerfile 參考。