Java 8 から最新の Java ランタイムに移行する

このページでは、第 1 世代から第 2 世代の Java ランタイムに移行する手順について説明します。サポートされている最新バージョンの Java を使用するように第 2 世代のアプリをアップグレードするには、既存のアプリケーションをアップグレードするをご覧ください。

Java 8 は、2024 年 1 月 31 日にサポートを終了しました。既存の Java 8 アプリケーションは引き続き実行され、トラフィックを受信します。ただし、サポート終了日を過ぎると、ランタイムを使用するアプリケーションの再デプロイが App Engine によってブロックされることがあります。このページのガイドラインに従って、サポートされている最新バージョンの Java に移行することをおすすめします。

第 2 世代 Java ランタイムに移行すると、最新の言語機能を使用し、慣用的なコードを使用して、より移植性の高いアプリを構築できます。

移行オプションについて

App Engine スタンダード環境では、ランタイム移行の労力と複雑さを軽減するため、以前のバンドル サービスや API(Memcache など)の多くは第 2 世代 Java ランタイムでも利用できるようになっています。Java アプリは、App Engine API JAR を介してバンドル サービスの API を呼び出し、Java 8 ランタイムとほぼ同じ機能にアクセスできます。

以前のバンドル サービスと同様の機能を備えた Google Cloud プロダクトを使用することもできます。これらの Google Cloud プロダクトでは、慣用的な Java 用 Cloud クライアント ライブラリが用意されています。Google Cloud で個別のプロダクトとして提供されないバンドル サービス(画像処理、検索、メッセージングなど)については、サードパーティ プロバイダまたはその他の回避策を使用できます。

バンドルされていないサービスへの移行については、バンドル サービスからの移行をご覧ください。

ランタイム移行を実行する方法は、以前のバンドル サービスを使用するかどうかによって異なります。

バンドル サービスを使用する場合の第 2 世代の Java ランタイムへの移行 バンドル サービスを使用しない場合の第 2 世代 Java ランタイムへの移行
App Engine API JAR を使用してバンドル サービスにアクセスします 必要に応じて、推奨の Google Cloud プロダクトまたはサードパーティ サービスを使用します。

アプリの構成には appengine-web.xmlweb.xml を使用します。

アプリで使用する機能によっては、追加の YAML ファイルの構成が必要になる場合があります。

アプリを構成するには、app.yaml を使用します。

アプリで使用する機能によっては、追加の YAML ファイルの構成が必要になる場合があります。

アプリは Jetty 経由でデプロイされます。WAR 形式を使用してアプリをパッケージ化します。 アプリは独自のサーバーによってデプロイされます。JAR 形式を使用してアプリをパッケージ化します。既存の WAR ファイルを実行可能な JAR に変換する方法については、WAR ファイルの再パッケージ化をご覧ください。

移行プロセスの概要

以下では、第 2 世代の Java ランタイムを使用するために、既存の App Engine Java 8 アプリとデプロイ プロセスに行う必要のある変更について説明します。

Java 8 ランタイムと第 2 世代の Java ランタイムの主な違い

App Engine スタンダード環境の Java 8 ランタイムと第 2 世代の Java ランタイムの違いの概要は次のとおりです。

Java 8 ランタイム 第 2 世代の Java ランタイム
サーバーのデプロイ Jetty を使用してデプロイされたサーバー 以前のバンドル サービスを使用していない場合は、サーバーを自分でデプロイする必要があります1
App Engine レガシー バンドル サービス 提供 提供
Java 用 Cloud クライアント ライブラリの使用
言語拡張とシステム ライブラリ サポート
外部ネットワークへのアクセス
ファイル システムへのアクセス /tmp に対する読み取り / 書き込みアクセス権 /tmp に対する読み取り / 書き込みアクセス権
言語ランタイム App Engine 用に修正済み 修正なし、オープンソース ランタイム
分離メカニズム gVisor ベースのコンテナ サンドボックス gVisor ベースのコンテナ サンドボックス
ローカル開発用サーバーでのテスト サポート対象 サポート対象
スレッドセーフ構成 appengine-web.xml ファイルで指定できます。 構成ファイルでは指定できません。すべてのアプリがスレッドセーフであると想定されます3
ロギング java.util.logging. ConsoleHandler を使用します。
stderr に書き込み、
各レコードの後でストリームを
フラッシュします。
標準 Cloud Logging 2
DataNucleus プラグイン 2.x のサポート サポート対象 サポート対象外4

注:

  1. アプリが以前のバンドル サービスを使用していない場合、PORT 環境変数で指定されたポート(推奨)またはポート 8080 で HTTP リクエストに応答するよう構成されたウェブサーバーをパッケージ化する限り、第 2 世代の Java ランタイムで任意の Java フレームワークを実行できます。たとえば、第 2 世代の Java ランタイムでは、Spring Boot Uber JAR をそのまま実行できます。その他の例については、フレームワークの柔軟性のセクションをご覧ください。

    アプリが以前のバンドル サービスを使用している場合、App Engine は Java 8 ランタイムの場合と同様に Jetty を使用してサービスをデプロイします。

  2. 第 2 世代の Java ランタイムのロギングは、Cloud Logging のロギング基準に従います。第 2 世代の Java ランタイムでは、アプリログがリクエストログにバンドルされなくなり、別のレコードに分離されます。第 2 世代の Java ランタイムでのログの読み取りと書き込みの詳細については、ロギングガイドをご覧ください。

  3. 第 2 世代の Java ランタイムでスレッドセーフでないアプリを構成し、以前のバンドル サービスを使用する場合は、Java 8 で <threadsafe>false</threadsafe> を設定した場合と同様に、app.yaml ファイルまたは appengine-web.xml ファイルのいずれかで最大同時実行数を 1 に設定します。

  4. Google では、第 2 世代ランタイムの DataNucleus ライブラリをサポートしていません。新しいバージョンの DataNucleus は、Java 8 で使用されているバージョンと下位互換性がありません。Datastore にアクセスするには、Datastore モードのクライアント ライブラリまたは Java Objectify(バージョン 6 以降)ソリューションを使用することをおすすめします。Objectify は、より高度な抽象化を行う Datastore 用のオープンソース API です。

メモリ使用量の違い

第 2 世代のランタイムでは、第 1 世代のランタイムよりもメモリ使用量のベースラインが高くなります。これは、ベースイメージのバージョンの違いや、2 つの世代でのメモリ使用量の計算方法の違いなど、複数の要因によるものです。

第 2 世代のランタイムでは、アプリプロセスで使用されるものとメモリに動的にキャッシュされるアプリケーション ファイルの数の合計としてインスタンスのメモリ使用量が計算されます。メモリ使用量の多いアプリケーションで、メモリの上限を超えてインスタンスがシャットダウンしないようにするには、より多くのメモリを備えているインスタンス クラスにアップグレードします。

CPU 使用率の違い

第 2 世代のランタイムでは、インスタンスのコールド スタート時の CPU 使用率のベースラインが高くなります。アプリケーションのスケーリング構成によっては、CPU 使用率に基づいてスケーリングするようにアプリケーションを構成すると、インスタンス数が想定よりも多くなるなど、意図しない結果が生じることがあります。この問題を回避するには、アプリケーションのスケーリング構成をテストして、インスタンス数が許容範囲内であることを確認します。

リクエスト ヘッダーの違い

第 1 世代のランタイムでは、アンダースコア付きのリクエスト ヘッダー(X-Test-Foo_bar など)をアプリケーションに転送できます。第 2 世代のランタイムでは、ホスト アーキテクチャに Nginx が導入されています。この変更の結果、第 2 世代のランタイムはアンダースコア(_)付きのヘッダーを自動的に削除するように構成されています。アプリケーションでの問題を回避するため、アプリケーションのリクエスト ヘッダーにアンダースコアを使用しないでください。

フレームワークの柔軟性

以前のバンドル サービスを使用している場合を除き、第 2 世代の Java ランタイムにはウェブサービス フレームワークは含まれません。つまり、サーブレット ベースのフレームワーク以外のフレームワークを使用できます。以前のバンドル サービスを使用している場合、第 2 世代の Java ランタイムは Jetty ウェブサービス フレームワークを提供します。

Google Cloud GitHub リポジトリには、一般的な Java ウェブ フレームワークを使用した hello world サンプルがあります。

XML から YAML へのファイル形式の移行

gcloud CLI は、次のファイル形式をサポートしていません。

  • cron.xml
  • datastore-index.xml
  • dispatch.xml
  • queue.xml

次の例は、xml ファイルを yaml ファイルに移行する方法を示しています。

自動的にファイルを移行する

xml ファイルを自動的に移行するには:

  1. gcloud CLI バージョン 226.0.0 以降が必要です。最新バージョンに更新するには:

    gcloud components update
    
  2. 移行するファイルごとに、次のいずれかのサブコマンド(cron-xml-to-yamldatastore-indexes-xml-to-yamldispatch-xml-to-yamlqueue-xml-to-yaml)とファイル名を指定します。

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. 本番環境にデプロイする前に、変換後のファイルを手動でダブルチェックします。

    xml ファイルから yaml ファイルへの正常な変換例については、手動でファイルを移行するのタブをご覧ください。

手動でファイルを移行する

xml ファイルを yaml ファイルに手動で移行するには:

cron.yaml

cron オブジェクトを含む cron.yaml ファイルを作成します。このオブジェクトにはオブジェクトのリストが含まれます。以下に示すように、各オブジェクトには cron.xml ファイルの <cron> タグ属性に対応するフィールドがあります。

変換後の cron.yaml ファイル:

cron:
- url: '/recache'
  schedule: 'every 2 minutes'
  description: 'Repopulate the cache every 2 minutes'
- url: '/weeklyreport'
  schedule: 'every monday 08:30'
  target: 'version-2'
  timezone: 'America/New_York'
  description: 'Mail out a weekly report'

元の cron.xml ファイル:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/recache</url>
    <description>Repopulate the cache every 2 minutes</description>
    <schedule>every 2 minutes</schedule>
  </cron>
  <cron>
    <url>/weeklyreport</url>
    <description>Mail out a weekly report</description>
    <schedule>every monday 08:30</schedule>
    <timezone>America/New_York</timezone>
    <target>version-2</target>
  </cron>
</cronentries>

詳細については、cron.yaml リファレンス ドキュメントをご覧ください。

dispatch.yaml

dispatch オブジェクトを含む dispatch.yaml ファイルを作成します。このオブジェクトにはオブジェクトのリストが含まれます。以下に示すように、各オブジェクトには dispatch.xml ファイルの <dispatch> タグ属性に対応するフィールドがあります。

変換後の dispatch.yaml ファイル:

dispatch:
- url: '*/favicon.ico'
  module: default
- url: 'simple-sample.uc.r.appspot.com/'
  module: default
- url: '*/mobile/*'
  module: mobile-frontend

元の dispatch.xml ファイル

<?xml version="1.0" encoding="UTF-8"?>
<dispatch-entries>
  <dispatch>
      <url>*/favicon.ico</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>simple-sample.uc.r.appspot.com/</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>*/mobile/*</url>
      <module>mobile-frontend</module>
  </dispatch>
</dispatch-entries>

詳細については、dispatch.yaml リファレンス ドキュメントをご覧ください。

index.yaml

indexes オブジェクトを含む index.yaml ファイルを作成します。このオブジェクトにはオブジェクトのリストが含まれます。以下に示すように、各オブジェクトには datastore-indexes.xml ファイルの <datastore-index> タグ属性に対応するフィールドがあります。

変換後の index.yaml ファイル:

indexes:
- ancestor: false
  kind: Employee
  properties:
  - direction: asc
    name: lastName
  - direction: desc
    name: hireDate
- ancestor: false
  kind: Project
  properties:
  - direction: asc
    name: dueDate
  - direction: desc
    name: cost

元の datastore-index.xml ファイル:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
 autoGenerate="true">
   <datastore-index kind="Employee" ancestor="false">
       <property name="lastName" direction="asc" />
       <property name="hireDate" direction="desc" />
   </datastore-index>
   <datastore-index kind="Project" ancestor="false">
       <property name="dueDate" direction="asc" />
       <property name="cost" direction="desc" />
   </datastore-index>
</datastore-indexes>

詳細については、index.yaml リファレンス ドキュメントをご覧ください。

queue.yaml

queue オブジェクトを含む queue.yaml ファイルを作成します。このオブジェクトにはオブジェクトのリストが含まれます。以下に示すように、各オブジェクトには queue.xml ファイルの <queue> タグ属性に対応するフィールドがあります。

変換後の queue.yaml ファイル:

queue:
- name: fooqueue
  mode: push
  rate: 1/s
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: barqueue
  mode: push
  rate: 1/s
  retry_parameters:
    min_backoff_seconds: 10
    max_backoff_seconds: 200
    max_doublings: 0

元の queue.xml ファイル:

<queue-entries>
  <queue>
    <name>fooqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <task-retry-limit>7</task-retry-limit>
      <task-age-limit>2d</task-age-limit>
    </retry-parameters>
  </queue>
  <queue>
    <name>barqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <min-backoff-seconds>10</min-backoff-seconds>
      <max-backoff-seconds>200</max-backoff-seconds>
      <max-doublings>0</max-doublings>
    </retry-parameters>
  </queue>
<queue-entries>