透過 Docker 從 Google App Engine Java 應用程式遷移至 Cloud Run

1. 總覽

本系列程式碼實驗室 (自學式實作教學課程) 旨在協助 Google App Engine (標準) Java 開發人員完成一系列遷移作業,進而將應用程式現代化。按照這些步驟操作,即可更新應用程式,使其更具可攜性,並決定是否要將應用程式容器化,以便用於 Cloud Run (Google Cloud 的容器代管服務,與 App Engine 類似) 和其他容器代管服務。

本教學課程說明如何使用 Dockerfile 將 App Engine 應用程式容器化,以便部署至 Cloud Run 全代管服務。Dockerfile 是最實用的遷移部署方法,但同時也提供最多選項,可自訂建構程序。

除了說明從 App Engine 遷移至 Cloud Run 的必要步驟,我們也會教您如何將 Java 8 App Engine 應用程式升級至 Java 17。

如果您想遷移的應用程式大量使用 App Engine 舊版套裝服務或其他 App Engine 特定功能,建議您先參閱「存取 Java 11/17 適用的 App Engine 套裝服務」指南,再進行本程式碼研究室的內容。

在接下來的研究室中

  • 使用 Cloud Shell
  • 啟用 Cloud Run、Artifact Registry 和 Cloud Build API
  • 使用 Docker、Docker 和 Cloud Build 將應用程式容器化
  • 將容器映像檔部署至 Cloud Run

軟硬體需求

問卷調查

您會如何使用本教學課程?

僅閱讀 閱讀並完成練習

你對 Java 的體驗如何?

新手 中級 熟練

您對使用 Google Cloud 服務的體驗滿意嗎?

新手 中級 熟練

2. 背景

App Engine 和 Cloud Functions 等 PaaS 系統可為團隊和應用程式提供許多便利功能,例如讓系統管理員和 DevOps 專注於建構解決方案。使用無伺服器平台時,應用程式可視需要自動擴充資源,並在用量為零時縮減資源,以按用量計費的方式控管費用,還能使用各種常見的開發語言。

不過,容器的彈性也相當吸引人。容器可讓您選擇任何語言、程式庫和二進位檔,兼具無伺服器架構的便利性與容器的彈性。這就是 Google Cloud Run 的用途。

本程式碼研究室不會說明如何使用 Cloud Run,相關內容請參閱 Cloud Run 說明文件。本節的目標是讓您熟悉如何將 App Engine 應用程式容器化,以便在 Cloud Run (或其他容器代管服務) 中執行。繼續操作前,請先瞭解幾件事,主要是使用者體驗會略有不同。

在本程式碼研究室中,您將瞭解如何建構及部署容器。您將瞭解如何使用 Dockerfile 將應用程式容器化、從 App Engine 設定遷移,以及 (選擇性) 定義 Cloud Build 的建構步驟。這項作業會涉及停用特定 App Engine 功能。如果不想採用這種做法,您還是可以將應用程式保留在 App Engine 上,同時升級至 Java 11/17 執行階段。

3. 設定/準備工作

1. 設定專案

在本教學課程中,您將使用 全新專案中的 appengine-java-migration-samples 存放區範例應用程式。確認專案具備有效的帳單帳戶。

如果您打算將現有的 App Engine 應用程式移至 Cloud Run,可以改用該應用程式來進行後續步驟。

執行下列指令,為專案啟用必要 API:

gcloud services enable artifactregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com

2. 取得基準範例應用程式

在自己的電腦或 Cloud Shell 上複製範例應用程式,然後前往 baseline 資料夾。

這個範例是 Java 8 的 Servlet 型 Datastore 應用程式,適用於部署至 App Engine。請按照 README 中的操作說明,準備將這個應用程式部署至 App Engine。

3. (選用) 部署基準應用程式

如果您想在遷移至 Cloud Run 前,確認應用程式可在 App Engine 上運作,才需要執行下列步驟。

請參閱 README.md 中的步驟:

  1. 安裝/重新熟悉 gcloud CLI
  2. 使用 gcloud init 為專案初始化 gcloud CLI
  3. 使用 gcloud app create 建立 App Engine 專案
  4. 將範例應用程式部署至 App Engine
./mvnw package appengine:deploy -Dapp.projectId=$PROJECT_ID
  1. 確認應用程式在 App Engine 上順利執行

4. 建立 Artifact Registry 存放區

將應用程式容器化後,您需要將映像檔推送至某處並儲存。在 Google Cloud 中,建議使用 Artifact Registry 執行這項操作。

使用 gcloud 建立名為 migration 的存放區,如下所示:

gcloud artifacts repositories create migration --repository-format=docker \
--description="Docker repository for the migrated app" \
--location="northamerica-northeast1"

請注意,這個存放區使用 docker 格式類型,但有多種存放區類型可供使用。

此時您已擁有基準 App Engine 應用程式,Google Cloud 專案也已準備好將其遷移至 Cloud Run。

4. 修改應用程式檔案

如果您的應用程式大量使用 App Engine 的舊版套裝組合服務、設定或其他僅限 App Engine 的功能,建議您在升級至新版執行階段時繼續存取這些服務。本程式碼研究室會說明應用程式的遷移路徑,這些應用程式已使用獨立服務,或可進行重構以使用獨立服務。

1. 升級至 Java 17

如果您的應用程式使用 Java 8,建議升級至較新的 LTS 候選版本 (例如 11 或 17),以便取得安全性更新並使用新的語言功能。

首先,請更新 pom.xml 中的屬性,加入下列項目:

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

這會將專案版本設為 17,通知編譯器外掛程式您想存取 Java 17 語言功能,並希望編譯的類別與 Java 17 JVM 相容。

2. 包括網路伺服器

在 App Engine 和 Cloud Run 之間遷移時,請考量兩者之間的差異。其中一項差異是,App Engine 的 Java 8 執行階段會為代管的應用程式提供及管理 Jetty 伺服器,但 Cloud Run 不會。我們會使用 Spring Boot 提供網路伺服器和 Servlet 容器。

新增下列相依項目:

<dependencies>
<!-- ... -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <version>2.6.6</version>
       <exclusions>
           <!-- Exclude the Tomcat dependency -->
           <exclusion>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-tomcat</artifactId>
           </exclusion>
       </exclusions>
   </dependency>
   <!-- Use Jetty instead -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jetty</artifactId>
       <version>2.6.6</version>
   </dependency>
<!-- ... -->
</dependencies>

Spring Boot 預設會嵌入 Tomcat 伺服器,但本範例會排除該構件,並繼續使用 Jetty,盡量減少遷移後預設行為的差異。

3. 設定 Spring Boot

Spring Boot 能夠重複使用您的 Servlet,無須修改,但需要進行一些設定,確保 Servlet 可供探索。

com.example.appengine 套件中建立下列 MigratedServletApplication.java 類別:

package com.example.appengine;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan
@SpringBootApplication
@EnableAutoConfiguration
public class MigratedServletApplication {
    public static void main(String[] args) {
        SpringApplication.run(MigratedServletApplication.class, args);
    }
}

請注意,這包括 @ServletComponentScan 註解,該註解會 (預設在目前套件中) 尋找任何 @WebServlets,並如預期提供這些註解。

4. 將應用程式封裝為 JAR

雖然可以從 WAR 開始容器化應用程式,但如果將應用程式封裝為可執行的 JAR,會更加容易。這不需要太多設定,特別是使用 Maven 做為建構工具的專案,因為 JAR 封裝是預設行為。

移除 pom.xml 檔案中的 packaging 標記:

<packaging>war</packaging>

接著,新增 spring-boot-maven-plugin

<plugins>
<!-- ... -->
  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.6.6</version>
  </plugin>
<!-- ... -->
</plugins>

5. 從 App Engine 設定、服務和依附元件遷移

如本程式碼研究室開頭所述,Cloud Run 和 App Engine 的設計宗旨是提供不同的使用者體驗。App Engine 提供的某些現成功能 (例如 CronTask Queue 服務) 必須手動重新建立,我們會在後續單元中詳細說明。

範例應用程式不會使用舊版套裝服務,但如果您的應用程式會使用,請參閱下列指南:

由於您之後會部署至 Cloud Run,因此可以移除 appengine-maven-plugin

<plugin>
 <groupId>com.google.cloud.tools</groupId>
 <artifactId>appengine-maven-plugin</artifactId>
 <version>2.4.1</version>
 <configuration>
   <!-- can be set w/ -DprojectId=myProjectId on command line -->
   <projectId>${app.projectId}</projectId>
   <!-- set the GAE version or use "GCLOUD_CONFIG" for an autogenerated GAE version -->
   <version>GCLOUD_CONFIG</version>
 </configuration>
</plugin>

5. 容器化應用程式

此時,您已準備好告知 Cloud Build 如何實際建構應用程式的容器。使用這種容器化方法時,不需要單獨的建構設定檔 (cloudbuild.yaml)。我們可以先定義最簡單的 Dockerfile:

FROM eclipse-temurin

ARG JAR_FILE=JAR_FILE_MUST_BE_SPECIFIED_AS_BUILD_ARG

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java", "-jar","/app.jar"]

這個 Dockerfile 會將 Spring Boot 服務的 uber-jar 版本,以單一層級的形式進行封裝。這是最簡單的 Dockerfile 容器化方法,但有許多缺點,特別是與依附元件相對穩定的重複時間相比。這類疑慮是這種容器化方法被視為更進階的原因。不過,撰寫自己的 Dockerfile 可完全控管基礎映像檔,並享有精心分層映像檔的效能優勢。

2**. 執行建構程序**

您已告知 Cloud Build 想要的建構步驟,現在可以一鍵部署。

執行下列指令:

gcloud builds submit --tag LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE_NAME

將上述指令中的預留位置值替換為下列值:

  • LOCATION:存放區的單一區域或多區域位置。
  • PROJECT_ID:您的 Cloud 專案 ID。
  • REPOSITORY:Artifact Registry 存放區的名稱。
  • IMAGE_NAME:容器映像檔的名稱。

程序完成後,容器映像檔就會建構完成、儲存在 Artifact Registry 中,並部署至 Cloud Run。

完成本程式碼研究室後,您的應用程式應與 mod4-migrate-to-cloud-run 資料夾中的應用程式相同。

這樣就大功告成囉!您已成功將 Java 8 App Engine 應用程式遷移至 Java 17 和 Cloud Run,現在對切換及選擇代管選項所涉及的工作有更清楚的瞭解。

6. 摘要/清除

恭喜,您已升級、容器化及遷移應用程式,本教學課程到此結束!

接下來,您可以進一步瞭解 CI/CD 和軟體供應鏈安全功能,這些功能現在都能透過 Cloud Build 部署:

選用:清除及/或停用服務

如果您在本教學課程期間將範例應用程式部署至 App Engine,請記得停用該應用程式,以免產生費用。準備好進行下一個程式碼研究室時,可以重新啟用。停用 App Engine 應用程式後,應用程式不會收到任何流量,因此不會產生費用,但如果Datastore 用量超出免費配額,系統仍會收費,因此請刪除足夠的資料,確保用量低於配額限制。

另一方面,如果您不打算繼續遷移,並想完全刪除所有內容,可以刪除服務完全關閉專案

7. 其他資源

App Engine 遷移模組程式碼研究室問題/意見回饋

如果發現本程式碼研究室有任何問題,請先搜尋問題,再提出回報。搜尋及建立新問題的連結:

遷移資源

線上資源

以下是可能與本教學課程相關的線上資源:

App Engine

其他雲端資訊

影片

授權

這項內容採用的授權為 Creative Commons 姓名標示 2.0 通用授權。