モジュール 2: App Engine ndb から Cloud NDB への移行

この一連の Codelab(ご自分のペースで進められる実践型のチュートリアル)は、一連の移行についてのガイダンスを実施することにより、Google App Engine(標準)のデベロッパーがアプリを最新化できるよう支援することを目的としています。最も重要なステップは、次世代のランタイムは柔軟性が向上し、ユーザーにより多様なサービス オプションを提供するものであることから、元のランタイム バンドル サービスからの移行を行うことです。新しい世代のランタイムに移行することにより、Google Cloud プロダクトとの統合が容易になり、幅広いサポート対象サービスを使用し、現在の言語リリースをサポートできるようになります。

このチュートリアルでは、App Engine の組み込みの ndb(次のデータベース)クライアント ライブラリから Cloud NDB クライアント ライブラリに移行する方法を説明します。

次のことを行う方法について説明します

  • App Engine の ndb ライブラリを使用する(習熟されていない場合)
  • ndb から Cloud NDB に移行する
  • Python 3 へのアプリの移行をさらに進める

必要なもの

アンケート

この Codelab をどのように使用されますか?

全体を通して読むのみ 内容を読んで演習を完了する

モジュール 1 では、App Engine の組み込みの webapp2 から Flask にウェブ フレームワークを移行しました。この Codelab では、App Engine の ndb ライブラリから Google Cloud NDB に切り替えることにより、App Engine の組み込みサービスからの移行を継続します。

この移行を完了すると、次のことが可能になります。

  1. Python 3 への移行と次世代 App Engine ランタイムへの移行
  2. Cloud Datastore への移行(App Engine 以外のアプリのクライアント ライブラリ)
  3. Python 2(または 3)アプリをコンテナ化し、Cloud Run に移行する
  4. App Engine(push)タスクキューの使用を追加してから、Cloud Tasks に移行する

ただし、現時点ではこの状態には至っていません。これらの次のステップについて検討する前に、この Codelab を終了してください。このチュートリアルの移行では、次の主なステップに着目します。

  1. セットアップ / 事前作業
  2. Cloud NDB ライブラリを追加する
  3. アプリケーション ファイルを更新する

チュートリアルの主要部分について説明する前に、まずプロジェクトをセットアップし、コードを取得して、ベースライン アプリをデプロイすることで、作業コードの作成を開始します。

1. プロジェクトをセットアップする

モジュール 1 の Codelab を完了している場合は、同じプロジェクト(およびコード)を再利用することをおすすめします。または、新しいプロジェクトを作成するか、別の既存のプロジェクトを再利用することもできます。プロジェクトに有効な請求先アカウントがあり、App Engine が有効になっていることを確認します。

2. ベースライン サンプルアプリを入手する

前提条件の 1 つは、正常に機能するモジュール 1 サンプルアプリを用意することです。このチュートリアルを完了している場合は、ソリューションを使用します。チュートリアルは(上のリンクで)今から完了できます。またはスキップすることを希望される場合は、モジュール 1 リポジトリ(下のリンク)をコピーしてください。

ご自分のコードと Google で用意したコードのいずれを使用される場合も、モジュール 1 のコードから開始します。このモジュール 2 の Codelab では、各ステップについて順を追って説明します。完了すると、終了ポイントのコード(Python 2 から 3 への任意の「ボーナス」ポートを含む)が次のように表示されます。

開始モジュール 1 のコードフォルダには次の内容が含まれます。

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

モジュール 1 のチュートリアルを完了すると、Flask とその依存関係を含む lib フォルダも作成されます。lib フォルダが存在しない場合は、pip install -t lib -r requirements.txt コマンドを使用して作成します。これにより、次のステップでこのベースライン アプリをデプロイできるようになります。Python 2 と Python 3 の両方をインストールしている場合は、Python 3 との混同を回避するため、pip ではなく pip2 を使用することをおすすめします。

3. モジュール 1 アプリを(再)デプロイする

この段階で実施する必要がある残りの事前作業のステップ:

  1. gcloud コマンドライン ツールについて復習します(必要な場合)。
  2. モジュール 1 コードを App Engine に(再)デプロイします(必要な場合)。

各手順を正常に実行して、動作していることを確認したら、このチュートリアルに進み、構成ファイルを使用して作業を開始します。

当初の App Engine 組み込みサービスの多くは独自のプロダクトに採用されており、Datastore はそれらの一つです。現在、App Engine 以外のアプリCloud Datastore を利用できます。ndb の長期ユーザーを対象に、Google Cloud チームは Cloud Datastore と通信するための Cloud NDB クライアント ライブラリを作成しました。Python 2 と Python 3 の両方で使用できます。

確認ファイルを更新して、App Engine ndb を Cloud NDB に置き換え、アプリケーションを変更してみましょう。

1. requirements.txt の更新

モジュール 1 では、アプリの唯一の外部依存関係は Flask でした。ここでは、Cloud NDB を追加します。モジュール 1 の最後にある requirements.txt ファイルは次のようになります。

  • 変更前:
Flask==1.1.2

App Engine ndb からの移行には Cloud NDB ライブラリ(google-cloud-ndb)が必要であるため、ライブラリのパッケージを requirements.txt に追加します。

  • 変更後:
Flask==1.1.2
google-cloud-ndb==1.7.1

この Codelab の作成時点で、最新の推奨バージョンは 1.7.1 ですが、リポジトリの requirements.txt には新しいバージョンが存在する可能性があります。各ライブラリの最新バージョンを使用することをおすすめしますが、適切に機能しない場合は、古いリリースにロールバックできます。

lib フォルダがすでに作成されており、上に示した手順で作成していない場合は削除します。次に、必要に応じて pip の代わりに pip2 を使用して pip install -t lib -r requirements.txt コマンドで更新済みのライブラリをインストールします。

2. app.yaml の更新

google-cloud-ndb などの Google Cloud クライアント ライブラリを追加するには、いくつかの要件があり、それらはすべて「組み込み」ライブラリと Google サーバーですでに使用可能なサードパーティ パッケージの追加に関連します。requirements.txt に記載する、または pip install でコピーすることは行わないでください。唯一の要件は次のとおりです。

  1. app.yaml の組み込みライブラリを指定する
  2. 連動する可能性があるコピーされたサードパーティ ライブラリを参照するよう(lib で)指定する

モジュール 1 の app.yaml から開始します。

  • 変更前:
runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

次の行を app.yaml に追加して、新しい libraries セクションの grpciosetuptools のサードパーティ バンドル パッケージのペアを参照するようにします。

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

これらの組み込みライブラリを使用する理由は、gRPC が、google-cloud-ndb を含むすべての Google Cloud クライアント ライブラリで使用されるオープン RPC フレームワークであることです。grpcio ライブラリは Python gRPC アダプタであるため必須です。setuptools を含める理由はこの後紹介します。

  • 変更後:

上で示した変更を行うと、更新された app.yaml は次のようになります。

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

3. appengine_config.py の更新

setuptools ライブラリの一部である pkg_resources ツールを使用すると、組み込みのサードパーティ ライブラリがバンドルされたライブラリにアクセスできるようになります。pkg_resources を使用して lib 内のバンドル ライブラリを指すように、appengine_config.py を更新します。この変更を完了すると、ファイル全体は次のようになります。

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

構成ファイルの厳密な形式を適用対象外とすることで、ndb から Cloud NDB に移行できるようになりました。移行を完了するには、インポートされたライブラリを更新し、コンテキスト管理の使用を main.py に追加します。

1. インポート

main.py で次のインポート スワップを行います。

  • 変更前
from google.appengine.ext import ndb
  • 変更後:
from google.cloud import ndb

App Engine ライブラリから Google Cloud ライブラリへの変更は、今回の場合のように、わずかな変更である可能性があります。Google Cloud のフルプロダクトになった組み込みサービスの場合は、google.appengine ではなく google.cloud から属性をインポートします。

2. Datastore へのアクセス

Cloud NDB ライブラリを使用するには、アプリで Python コンテキスト マネージャーを使用する必要があります。それらの目的は、使用する前にリソースを取得できるように、リソースへのアクセスの「ゲート」となることです。コンテキスト マネージャーは、リソース割り当ての初期化(または RAII)と呼ばれるコンピュータ サイエンスの制御技術に基づいています。コンテキスト マネージャーを Python ファイル(アクセスするには、まず開く必要があります)と同時に使用し、「重要なセクション」内のコードを実行するには「スピンロック」を取得する必要があります。

同様に、Cloud NDB で Datastore コマンドを実行するには、まず Datastore と通信するためのクライアントのコンテキストを取得する必要があります。まず、Flask 初期化の直後main.pyds_client = ndb.Client() を追加してクライアント(ndb.Client())を作成します。

app = Flask(__name__)
ds_client = ndb.Client()

Python with コマンドは、オブジェクトのコンテキストを取得するためにのみ使用します。Datastore にアクセスするコードブロックを with ステートメントでラップします。

以下に示すのは、モジュール 1 の同じ関数です。新しいエンティティを Datastore に書き込み、読み取りを行い最後に追加されたエンティティを表示します。

  • 変更前:

コンテキスト管理を行っていない元のコードは次のとおりです。

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return (v.to_dict() for v in Visit.query().order(
            -Visit.timestamp).fetch(limit))
  • 変更後:

with ds_client.context(): を追加し、Datastore アクセスコードを with ブロックに移動します。

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return (v.to_dict() for v in Visit.query().order(
                -Visit.timestamp).fetch(limit))

主なドライバ アプリケーションは、ndb(に加えて Cloud NDB)コードが存在しないため、モジュール 1 で使用したものと同一です。

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

ベスト プラクティスは、アプリケーション コードとデータアクセスを明確に区別することです。この方法では、今回の移行で行ったようにデータ ストレージ メカニズムが変更されてもメインのアプリケーション コードは変化しません。

アプリケーションのデプロイ

gcloud app deploy を使用してアプリを再デプロイし、アプリが動作していることを確認します。これで、コードはモジュール 2 リポジトリのコードと一致するようになりました。

先行するいずれの Codelab の取り組みも行わずにこのシリーズに進んだ場合、アプリ自体は変更されません。このアプリは、メインのウェブページ(/)へのすべての訪問を登録します。一定回数以上サイトにアクセスすると、ウェブページは次のように表示されます。

visitme アプリ

このモジュール 2 の Codelab はこれで完了です。Datastore に関連する範囲では、これがこのシリーズで強く推奨される移行の最後にあたるため、これで終了です。

省略可: クリーンアップする

移行に関する次の Codelab に進む準備が整うまで、課金されるのを回避するためのクリーンアップを行うにはどうすればよいでしょうか。既存のデベロッパーの方は、App Engine の料金に関する情報をご覧ください。

省略可: アプリを無効にする

次のチュートリアルに進む準備がまだ完了していない場合は、アプリを無効にすることで課金されないようにできます。次の Codelab に進む準備ができたら、再度有効にできます。アプリが無効になっている間、料金のかかるトラフィックは発生しません。ただし、もう 1 つの課金対象は、Datastore の使用量です。無料割り当て量を超過した場合は課金が発生するため、上限を超えないよう削除してください。

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

次のステップ

ここでは、次の移行を柔軟に行えます。次のいずれかのオプションを選択します。

  • モジュール 2 ボーナス: 以下のチュートリアルのボーナスパートに進み、Python 3 と次世代の App Engine ランタイムへの移行をご確認ください。
  • モジュール 7: App Engine の push タスクキュー([push] タスクキューを使用する場合に必要)
    • App Engine taskqueue push タスクをモジュール 1 アプリに追加する
    • モジュール 8 で Cloud Tasks に移行するユーザーの準備を行う
  • モジュール 4: Docker を使用して Cloud Run に移行する
    • Docker を使用して Cloud Run で実行できるようにアプリをコンテナ化する
    • Python 2 を引き続き使用できるようにする
  • モジュール 5: Cloud Buildpacks を使用して Cloud Run に移行する
    • Cloud Buildpacks を使用して Cloud Run で実行するようにアプリをコンテナ化する
    • Docker、コンテナ、または Dockerfile についての知識は必要ありません
    • アプリを Python 3 に移行済みであることが必要です
  • モジュール 3:
    • Cloud NDB から Cloud Datastore に Datastore アクセスをモダナイズする
    • これは、Python 3 App Engine アプリと App Engine 以外のアプリに使用されるライブラリです

最新の App Engine のランタイムと機能にアクセスするには、Python 3 に移行することをおすすめします。サンプルアプリでは、Datastore は使用した唯一の組み込みサービスでした。ndb から Cloud NDB に移行したため、App Engine の Python 3 ランタイムに移植できるようになりました。

概要

Python 3 への移植は Google Cloud チュートリアルでは取り上げませんが、Codelab のこの部分では、Python 3 App Engine ランタイムの相違点についてデベロッパーの皆さんに説明します。次世代ランタイムの優れた機能の 1 つは、サードパーティ パッケージへのアクセスが簡素化されていることです。app.yaml で組み込みパッケージを指定する必要はありません。また、組み込みでないライブラリをコピーまたはアップロードする必要もありません。これらは、requirements.txt のリストに記載された中から暗黙的にインストールされます。

サンプルは非常に基本的なものであり、Cloud NDB は Python 2~3 と互換性があるため、アプリケーション コードの 3.x への明示的な移植は不要です。変更を加えられていないアプリが 2.x と 3.x で実行されている場合は、必要な変更のみが構成に含まれていることを示しています。

  1. app.yaml を Python 3 を参照するように簡略化し、サードパーティのライブラリを削除します。
  2. 不要になった時点で appengine_config.py フォルダと lib フォルダを削除します。

main.py に加えて、requirements.txt ファイルと templates/index.html ファイルも同一内容が保持されます。

app.yaml を簡略化する

変更前:

このサンプルアプリに対して実際に行った唯一の変更は、app.yaml を大幅に短縮したことです。リマインダーとして、モジュール 2 の最後に app.yaml で行った操作の内容を以下に記載します。

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

変更後:

Python 3 では、threadsafeapi_versionlibraries の各ディレクティブはすべて非推奨です。すべてのアプリがスレッドセーフであると推定され、Python 3 で api_version は使用されません。App Engine サービスにプリインストールされている組み込みのサードパーティ パッケージはなくなり、libraries も非推奨になりました。これらの変更の詳細については、app.yaml の変更に関するドキュメントをご覧ください。そのため、app.yaml から 3 つすべてを削除し、サポートされている Python 3 バージョン(以下を参照)にアップデートする必要があります。

handlers ディレクティブの使用

また、App Engine アプリケーションでトラフィックを転送するための handlers ディレクティブも非推奨になりました。次世代ランタイムでは、ウェブ フレームワークがアプリのルーティングを管理することを想定しているため、すべての「ハンドラ スクリプト」を「auto」に変更する必要があります。上記の変更を組み合わせると、app.yaml になります。

runtime: python38

handlers:
- url: /.*
  script: auto

script: auto の詳細については、ドキュメント ページをご覧ください。

handlers ディレクティブの削除

handlers はサポートが終了しているため、app.yaml の 1 行を残してセクション全体を削除することもできます。

runtime: python38

これによってデフォルトでは、すべてのアプリケーションで使用可能な Gunicorn WSGI ウェブサーバーが起動されます。gunicorn に精通されている場合は、このコマンドがベアボーン app.yaml で最初に起動すると実行されます。

gunicorn main:app --workers 2 -c /config/gunicorn.py

省略可: entrypoint ディレクティブを使用する

ただし、特定の起動コマンドをアプリケーションが必要とする場合は、entrypoint ディレクティブで指定でき、app.yaml は次のようになります。

runtime: python38
entrypoint: python main.py

この例では、Flask 開発用サーバーを gunicorn の代わりに使用するよう具体的にリクエストしています。また、開発用サーバーを起動するコードは、main.py の末尾に以下の小さなセクションを追加して、ポート 8080 の 0.0.0.0 インターフェースで起動できるようにアプリに追加する必要があります。

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

entrypoint の詳細については、ドキュメント ページをご覧ください。その他の例とベスト プラクティスについては、App Engine スタンダードのスタートアップ ドキュメントApp Engine フレキシブル環境のスタートアップ ドキュメントをご覧ください。

appengine_config.pylib を削除する

appengine_config.py ファイルと lib フォルダを削除します。Python 3 への移行では、App Engine は requirements.txt に表示されたパッケージを取得してインストールします。

appengine_config.py 構成ファイルは、サードパーティのライブラリまたはパッケージを、ご自分でコピーしたか、App Engine サーバーですでに使用可能なもの(組み込み)を使用しているかを認識するために使用されます。Python 3 に移行する際の、大きな変更の概要は次のとおりです。

  1. コピーされたサードパーティ ライブラリのバンドルはありません(requirements.txt に記載)
  2. pip installlib フォルダ内にない(つまり、lib フォルダの期間がない)
  3. app.yaml に組み込みのサードパーティ ライブラリが記載されていない
  4. サードパーティ ライブラリへのアプリの参照が不要であるため、appengine_config.py ファイルはありません。

必要であるのは、すべてのサードパーティ ライブラリを requirements.txt に記載することのみです。

アプリケーションのデプロイ

再デプロイしてアプリが動作するようにします。ご自分のソリューションが、モジュール 2 のサンプル Python 3 コードにどの程度類似したものであるかを確認することもできます。Python 2 との相違を可視化するには、コードを Python 2 バージョンと比較します。

モジュール 2 のボーナス ステップはこれで完了です。Python 3 ランタイム用の構成ファイルの準備に関するドキュメントをご覧ください。最後に、前述の「概要 / クリーンアップ」手順に戻って、次のステップとクリーンアップを行います。

ご自分のアプリケーションを準備する

アプリケーションを移行する際は、main.py とその他のアプリケーション ファイルを 3.x に移植する必要があります。そのため、可能な限り 2.x アプリケーションを「前方互換性」を有するものにすることをおすすめします。

これを実現するうえで有用なオンライン リソースは多数ありますが、重要なヒントをいくつか紹介します。

  1. すべてのアプリケーションの依存関係が 3.x と完全互換であることを確認します。
  2. アプリケーションが 2.6 以上で実行されるようにします(推奨は 2.7)。
  3. アプリケーションがテストスイート全体(および 80% 以上のカバレッジ)に合格していることを確認します。
  4. six、Future、Modernize などの互換性ライブラリを使用します。
  5. 下位互換性のない主な 2.x と 3.x の相違について認識します。
  6. すべての I/O で、Unicode 文字列とバイト文字列の互換性が失われる可能性があります。

サンプルアプリは、これらすべてを念頭に置いて設計されました。そのため、アプリは 2.x と 3.x で初期設定の状態で実行できます。これにより、次世代プラットフォームを使用するために変更が必要な箇所を示すことにフォーカスできます。

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

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

移行に関するリソース

モジュール 1(開始)とモジュール 2(終了)のリポジトリ フォルダへのリンクを以下の表に示します。これらのフォルダには、すべての App Engine Codelab 移行のリポジトリからアクセスすることもでき、クローンを作成する、または ZIP ファイルをダウンロードすることができます。

Codelab

Python 2

Python 3

モジュール 1

コード

該当なし

モジュール 2

コード

コード

App Engine リソース

この特定の移行に関する追加リソースは以下のとおりです。