將 Java 應用程式容器化

已完成

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

如先前所提,容器會直接在主機作業系統、核心和硬體上執行,基本上只是另一個系統流程。 容器需要較少的系統資源,因而產生較小的磁碟使用量、較少的額外負荷,以及更快的應用程式啟動時間。 這些是隨需調整的絕佳使用案例。

有 Windows 容器和 Linux 容器。 在本課程模組中,您將利用廣泛使用的 Docker 執行階段來建置 Linux 容器映像。 然後,您會將 Linux 容器映像部署到本機電腦的主機作業系統。 最後,您會將 Linux 容器映像部署至 Azure Kubernetes Service。

Docker 概觀

Docker 執行階段用來建置、提取、執行和推送容器映像。 下圖描述這些使用案例,後面接著每個使用案例/Docker 命令的描述。

Diagram showing Docker commands.

Docker 命令 描述
docker build 建置容器映像,基本上是 Docker 最終從映像建立執行中容器所需的指示/階層。 此命令的結果是映像。
docker pull 容器會從映像初始化,這些映像是從登錄提取的 (例如 Azure Container Registry),而且這是 Azure Kubernetes Service 從中提取的位置。 此命令的結果是將在 Azure 中發生的映像網路提取。 請注意,您可以選擇在本機提取映像;這在建置需要相依性/階層的映像時很常見,而您的應用程式可能需要這些相依性/階層,例如應用程式伺服器。
docker run 映像的執行中執行個體是容器,而此命令會執行所有執行容器應用程式以及與執行中應用程式互動所需的所有階層。 此命令的結果是主機作業系統上執行中的應用程式流程。
docker push Azure Container Registry 會儲存映像,使其快速可用,並關閉網路以進行 Azure 部署和調整。

複製 Java 應用程式

首先,您會複製適用於航空公司預約的航班預訂系統存放庫,並 cd 到 Airlines Web 應用程式專案資料夾。

注意

如果 Azure Kubernetes Service 建立已在 CLI 索引標籤中順利完成,請使用該索引標籤;如果其仍在執行中,請開啟新的索引標籤,並 cd 至您偏好的位置來複製適用於航空公司預約的航班預訂系統。

在 CLI 中執行下列命令:

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

在 CLI 中執行下列命令:

cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines

注意

或者,如果已安裝 JAVA 與 Maven,您可以在 CLI 中執行下列命令,以瞭解在沒有 Docker 的情況下建置應用程式的體驗。 如果您沒有安裝 JAVA 和 Maven,則可以安全地跳到下一節建構 Docker 檔案。 在該區段中,您將使用 Docker 來提取 JAVA 和 Maven,以代表您執行組建。

或者,如果已安裝 Maven 和 JDK(8) 或更高版本,您可以在 CLI 中執行下列命令:

mvn clean install

注意

我們使用了 mvn clean install 命令來說明不使用 Docker 多階段建置的作業挑戰,接下來我們會進行這方面的討論。 同樣地,這個步驟是選用的;任一方式您都可以安全心地移動,而不需執行 Maven 命令。

Maven 應該已成功建置適用於航空公司預約的航班預訂系統 Web 應用程式封存成品 FlightBookingSystemSample-0.0.-SNAPSHOT.war,如下列輸出所示:

[INFO] Building war: /mnt/c/Users/chtrembl/dev/git/containerize-and-deploy-Java-app-to-Azure/Project/FlightBookingSystemSample/target/FlightBookingSystemSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  17.698 s
[INFO] Finished at: 2021-09-28T15:18:07-04:00
[INFO] ------------------------------------------------------------------------

想像您是一名 JAVA 開發人員,而且您剛建置了這個 FlightBookingSystemSample-0.0.1-SNAPSHOT.war。 您的下一步可能是與作業工程師合作,以將此成品部署至內部部署伺服器或虛擬機器。 若要讓應用程式成功啟動並執行,這需要伺服器和虛擬機器可供使用,並使用必要的相依性進行設定。 這很具挑戰性且耗時,特別是在增加負載影響到您應用程序的需求下。 透過容器,這些挑戰會得到緩解。

建構 Dockerfile

此時,您已準備好建構 Dockerfile。 Dockerfile 是一份文字文件,其中包含使用者可在命令列上執行以組合容器映像的所有命令,而每一個命令都是建置在彼此之上的階層 (可以進行快取以提高效率)。

例如,適用於航空公司預約的航班預訂系統必須部署至應用程式伺服器,並在其內部執行。 應用程式伺服器不會封裝在 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 內,這是 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 執行、接聽並處理 HTTP 要求、管理使用者工作階段,以及協助航班預約時所需的外部相依性。 如果這是傳統的非容器化部署,作業工程師會在某些實體伺服器和/或虛擬機器上安裝並設定應用程式伺服器,然後再將 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 部署至其中。 這些作業工程師也需要確保機器上使用的 JDK (mvn clean install 用來編譯 war 的工具) 實際上對應至應用程式伺服器所使用的相同 JRE。 管理這些相依性很具挑戰性且耗時。

使用 Dockerfile,您可以撰寫自動完成此動作所需的指示 (圖層),方法是藉由步驟中的必要圖層,確保適用於航空公司預約的航班預訂系統具有部署至 Docker 容器執行階段所需的所有相依性。 當您開始考慮非計劃性間隔的隨需規模時,這會非常引人注目。 值得注意的是,每個圖層都會利用 Docker 快取,其中包含容器映像在每個指示里程碑的狀態,以最佳化計算時間和重複使用。 如果階層未變更,則系統會使用快取的階層。 快取圖層的常見使用案例像是 Java 執行階段、應用程式伺服器和/或適用於航空公司預約的航班預訂系統 Web 應用程式的其他相依性等。 如果先前快取的階層上發生版本變更,將會建立新的快取項目。

下圖描述容器映像的圖層。 您會發現頂端圖層是讀/寫適用於航空公司預約的航班預訂系統 Web 應用程式圖層,其建置在先前的唯讀圖層之上,而唯讀圖層全都是由 Dockerfile 中的命令所產生。

Diagram showing the Docker layers.

Docker 也具備多階段組建的概念,這項功能可讓您建立較小的容器映像,其具有更佳的快取和較小的安全性磁碟使用量,可隨著時間增加 Dockerfile 的最佳化和維護;例如,您可以用來完成應用程式編譯 (FlightBookingSystemSample-0.0.1-SNAPSHOT.war) 的指示以及容器映像本身的組建,留下 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 編譯的剩餘部分,因而產生較小的磁碟使用量。 在長期執行中,這會在您開始思考這些在網路上四處移動的映像時得到回報。 使用多階段建置時,您會在 Dockerfile 中使用多個 FROM 陳述式。 每個 FROM 指示都可以使用不同的基底,而且其中每個陳述式都是以全新靜態圖像基礎映像開頭,移除快取圖層中任何正常情況下可能快取的不必要檔案。

務必確保應用程式是由對應至相同 JRE 的相同 JDK 所建置,而在執行階段將於容器映像中隔離此 JRE。 在下列範例中,您會有一個建置階段,該階段利用特定版本的 Maven 和特定版本的 JDK 來編譯 FlightBookingSystemSample-0.0.1-SNAPSHOT.war。 此階段可確保執行此階段的任何 Docker 執行階段都會取得預期產生的位元組程式碼,這是 Dockerfile 作者所指定的位元組程式碼 (否則,作業工程師必須將其 Java 和應用程式伺服器執行階段與開發人員的進行交叉參考)。 然後,封裝階段會使用特定版本的 Tomcat,以及對應至建置階段中 JDK 的 JRE。 同樣地,這樣做可確保所有相依性 (Java 開發套件 JDK、Java Runtime Environment JRE、應用程式伺服器) 受到控制及隔離,以確保此映像執行所在的所有機器上都會有預期的行為。

也值得注意的是,透過這個多階段建置,技術上「不」需要在系統上安裝 Maven 和 JAVA。 Docker 會提取這兩個項目搭配使用以建置應用程式以及應用程式執行階段,藉以避免任何潛在的版本設定衝突和非預期的行為;除非您一定要在 Docker 外部編譯程式碼並建置成品。

下圖描述多階段建置,以及每個階段中根據 Dockerfile 中所指定命令發生的情況。 在階段 0 (建置階段) 中,系統會編譯適用於航空公司預約的航班預訂系統 Web 應用程式,並產生 FlightBookingSystemSample-0.0.1-SNAPSHOT.war。 此階段允許用來編譯此應用程式的 Maven 和 JAVA 版本達成一致性。 一旦建立了 FlightBookingSystemSample-0.0.1-SNAPSHOT.war,這就是階段 1 (執行階段) 所需的唯一圖層,所有先前的圖層都可以捨棄。 然後,Docker 會使用這個來自階段 0 的 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 圖層,建構執行階段所需的其餘圖層;在此案例中,會設定應用程式伺服器並啟動應用程式。

Diagram showing the Docker multistage build.

在專案的根目錄 (containerize-and-deploy-Java-app-to-Azure/Project/Airlines) 中,建立稱為 Dockerfile 的檔案:

vi Dockerfile

將下列內容新增至 Dockerfile,然後按 ESC 鍵儲存並結束,接著輸入 :wq!,然後按 Enter

#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
COPY web ./web
RUN mvn clean package

#
# Package stage
#
FROM tomcat:8.5.72-jre11-openjdk-slim
COPY tomcat-users.xml /usr/local/tomcat/conf
COPY --from=build /build/target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war
EXPOSE 8080
CMD ["catalina.sh", "run"]

注意

或者,專案根目錄中的 Dockerfile_Solution 包含所需的內容。

此 Dockerfile 建置階段有六個指示:

Docker 命令 描述
FROM FROM maven 會是從中建置此 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 的基礎階層,這是特定版本的 Maven 和特定版本的 JDK,可確保執行此建置的所有機器上都會發生相同的位元組程式碼編譯。
WORKDIR WORKDIR 是用來定義容器在任何給定時間的工作目錄;在此案例中,會是所編譯成品的所在位置。
COPY COPY 會從 Docker 使用者端的目前目錄新增檔案。 設定 Maven 編譯所需的檔案,Docker 內容將需要 pom.xml。
COPY 設定 Maven 編譯所需的檔案。 Docker 內容將需要包含適用於航空公司預約的航班預訂系統 Web 應用程式的 src 資料夾。
COPY 設定 Maven 編譯所需的檔案。 Web Docker 內容將需要的資料夾包含適用於航空公司預約的航班預訂系統 Web 應用程式相依性。
RUN RUN mvn clean package 指示是用來執行以目前的映像為基礎的任何命令。 在此案例中,會使用 RUN 來執行 Maven 建置,這會編譯 FlightBookingSystemSample-0.0.1-SNAPSHOT.war

此 Docker 檔案套件階段有五個指示:

Docker 命令 描述
FROM FROM tomcat 會是此容器映像將在其上建置的基礎階層。 適用於航空公司預約的航班預訂系統容器映像將會是建置在 tomcat 映像之上的映像。 Docker 執行階段會嘗試在本機尋找 tomcat 映像。 如果沒有此版本,則會從登錄中提取一個。 如果檢查這裡所參考的 tomcat 映像,您會發現其是使用許多其他階層所建置的,這一切都讓其可作為已封裝的應用程式伺服器容器映像來重複使用,以在部署其 JAVA 應用程式時使用。 針對課程模組的目的,我們選取並測試了 tomcat:8.5.72-jre11-openjdk-slim。 請注意,一旦 Docker 辨識了第二個 FROM 指示,來自第一個建置階段的所有先前圖層都會消失。
COPY COPY tomcat-users.xml 會將 tomcat-users.xml 檔案複製到 tomcat 容器映像,以便每次建立容器映像時,其都會出現在容器映像中,而該檔案會管理適用於航空公司預約的航班預訂系統使用者 (使用 Tomcat 身分識別在原始檔控制內進行管理;這通常會位於外部身分識別管理系統中)。
ADD ADD target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war 會將 Maven 編譯的 FlightBookingSystemSample-0.0.1-SNAPSHOT.war 複製到 tomcat 映像 webapps 資料夾,以確保當 Tomcat 初始化時,其將會找到要在應用程式伺服器上安裝的 FlightBookingSystemSample-0.0.1-SNAPSHOT.war
EXPOSE 因為 Tomcat 設定為接聽連接埠 8080 上的流量,因此需要 EXPOSE 8080。 這可確保 Docker 流程會接聽此連接埠。
CMD CMD 指示會設定在執行容器時要執行的命令。 在此案例中,CMD ["catalina.sh", "run"] 會指示 Docker 初始化 Tomcat 應用程式伺服器。

注意

若在 FROM tomcat 行上沒有版本標籤,則會套用最新的版本。 一般而言,您會想要利用版本標籤 (記住,已套用快取,因此,如果階層持續變更,您將會產生未測試建置/階層的頻寬、延遲、計算時間和/或副作用)。 為了本課程模組,我們已預先選取特定的 Maven、Tomcat、Java JRE/JDK 標籤,這些標籤都已通過測試可在執行階段使用 FlightBookingSystemSample-0.0.1-SNAPSHOT.war

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