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 システムは、システム管理者と DevOps がソリューションの構築に集中できるようにするなど、チームとアプリケーションに多くの利便性を提供します。サーバーレス プラットフォームでは、アプリを必要に応じて自動スケールアップしたり、従量課金制の課金システムで 0 にスケールダウンして費用を抑えたりできます。また、さまざまな一般的な開発言語を使用できます。

しかし、コンテナの柔軟性も魅力的です。コンテナでは任意の言語、ライブラリ、バイナリを選択できるため、サーバーレスの利便性とコンテナの柔軟性の両方を活用できます。これが 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 アプリです。App Engine へのデプロイ用にこのアプリを準備する方法については、README の手順に沿って行ってください。

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 サービスや タスクキュー サービスなど)は、手動で再作成する必要があります。これらの機能については、後続のモジュールで詳しく説明します。

このサンプルアプリでは以前のバンドル サービスを使用していませんが、アプリで以前のバンドル サービスを使用している場合は、次のガイドをご覧ください。

今後は 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 を定義します。

日食のテムリンから

ARG JAR_FILE=JAR_FILE_MUST_BE_SPECIFIED_AS_BUILD_ARG

${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 汎用ライセンスにより使用許諾されています。