Docker を使用した Google App Engine Java アプリから Cloud Run への移行

1. 概要

この一連の Codelab(ご自分のペースで進められる実践型のチュートリアル)は、一連の移行についてガイダンスを実施することにより、Google App Engine(標準)の Java デベロッパーがアプリをモダナイズできるよう支援することを目的としています。これらの手順に沿って、アプリをよりポータブルになるように更新し、Cloud Run、App Engine への Google Cloud のコンテナ ホスティング姉妹サービス、その他のコンテナ ホスティング サービス用にコンテナ化できます。

このチュートリアルでは、Dockerfile を使用して Cloud Run フルマネージド サービスにデプロイするために App Engine アプリをコンテナ化する方法について説明します。Dockerfile はこの移行に最も適したデプロイ方法ですが、ビルドプロセスをカスタマイズするためのオプションも最も多く提供します。

このチュートリアルでは、App Engine から Cloud Run に移行するために必要な手順を説明するだけでなく、Java 8 App Engine アプリを Java 17 にアップグレードする方法も説明します。

移行を検討しているアプリケーションで App Engine の以前のバンドル サービスやその他の App Engine 固有の機能が多用されている場合は、この Codelab よりも Java 11/17 用の App Engine バンドル サービスにアクセスするガイドの方が適している可能性があります。

GCP コンソールの

  • Cloud Shell を使用する
  • Cloud Run、Artifact Registry、Cloud Build の各 API を有効にする
  • Docker、Docker、Cloud Build を使用してアプリをコンテナ化する
  • コンテナ イメージを Cloud Run にデプロイする

必要なもの

アンケート

このチュートリアルをどのように使用されますか?

通読するのみ 通読し、演習を行う

Java のご利用経験はどの程度ありますか?

初心者 中級者 上級者

Google Cloud サービスの使用経験はどの程度ありますか?

初心者 中級者 上級者

2. 背景情報

App Engine や Cloud Functions などの PaaS システムは、SysAdmin や DevOps がソリューションの構築に集中できるようにするなど、チームやアプリケーションに多くの利便性を提供します。サーバーレス プラットフォームを使用すると、アプリは必要に応じて自動的にスケールアップし、従量課金制でゼロまでスケールダウンして費用を管理し、さまざまな一般的な開発言語を使用できます。

しかし、コンテナの柔軟性も魅力的です。任意の言語、ライブラリ、バイナリを選択できるコンテナは、サーバーレスの利便性とコンテナの柔軟性の両方を実現します。これが Google Cloud Run のすべてです。

Cloud Run の使用方法の学習は、この Codelab の範囲外です。Cloud Run のドキュメントをご覧ください。ここでは、Cloud Run(または他のコンテナ ホスト型サービス)用に App Engine アプリをコンテナ化する方法を理解することを目的としています。先に進む前に、ユーザー エクスペリエンスが若干異なることをはじめ、いくつか知っておくべきことがあります。

この Codelab では、コンテナをビルドしてデプロイする方法を学びます。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 フォルダに移動します。

このサンプルは、App Engine にデプロイすることを目的とした Java 8 のサーブレット ベースの Datastore アプリです。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 リポジトリを作成する

アプリをコンテナ化したら、イメージを push して保存する場所が必要になります。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 専用の機能が多用されている場合は、新しいランタイムにアップグレードしながら、これらのサービスへのアクセスを継続することをおすすめします。この Codelab では、スタンドアロン サービスをすでに使用しているか、スタンドアロン サービスを使用するようにリファクタリングできるアプリケーションの移行パスについて説明します。

1. Java 17 にアップグレードする

アプリが Java 8 を使用している場合は、セキュリティ アップデートに対応し、新しい言語機能を利用できるように、11 や 17 などの新しい LTS 候補にアップグレードすることを検討してください。

まず、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 の間を移行する際に考慮すべき相違点がいくつかあります。1 つの違いは、App Engine の Java 8 ランタイムがホストするアプリに Jetty サーバーを提供して管理していたのに対し、Cloud Run はそうではないことです。Spring Boot を使用して、ウェブサーバーとサーブレット コンテナを提供します。

次の依存関係を追加します。

<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 はサーブレットを修正なしで再利用できますが、サーブレットを検出可能にするには構成が必要です。

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 の構成、サービス、依存関係からの移行

Codelab の冒頭で説明したように、Cloud Run と App Engine は異なるユーザー エクスペリエンスを提供するように設計されています。App Engine が提供する特定の機能(Cron サービスや Task 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 にデプロイされます。

この Codelab の最後には、アプリは mod4-migrate-to-cloud-run フォルダにあるアプリと同じようになります。

このように、Java 8 App Engine アプリを Java 17 と Cloud Run に移行しました。これで、切り替えとホスティング オプションの選択に伴う作業について、より明確に理解できるようになりました。

6. 概要/クリーンアップ

これで、アプリのアップグレード、コンテナ化、移行が完了しました。これでチュートリアルは終了です。

ここから、Cloud Build でデプロイできるようになった CI/CD 機能とソフトウェア サプライ チェーンのセキュリティ機能について詳しく学習します。

省略可: サービスをクリーンアップまたは無効にする

このチュートリアルで App Engine にサンプルアプリをデプロイした場合は、課金されないようにアプリを無効にしてください。次の Codelab に進む準備ができた時点で、再度有効にできます。App Engine アプリを無効にしている間は、トラフィックが発生しないため料金は発生しませんが、Datastore の使用量無料割り当てを超えると、課金される場合があります。上限を超えないよう削除してください。

また、移行を続けず、完全に削除したい場合は、サービスを削除するか、プロジェクトを完全にシャットダウンしてください。

7. 参考情報

App Engine 移行モジュールの Codelab に関する問題 / フィードバック

この Codelab に問題が見つかった場合は、提出する前にまず問題を検索してください。新しい問題の検索と登録を行うためのリンク:

移行に関するリソース

オンライン リソース

このチュートリアルに関連する可能性のあるオンライン リソースは次のとおりです。

App Engine

その他のクラウド情報

動画

ライセンス

この作業はクリエイティブ・コモンズの表示 2.0 汎用ライセンスにより使用許諾されています。