App Engine ユーザー サービスから Cloud Identity Platform に移行する(モジュール 21)

1. 概要

サーバーレス移行ステーション シリーズの Codelab(ご自分のペースで進められる実践型のチュートリアル)と関連動画は、主にレガシー サービスからの移行を 1 つ以上行うことで、Google Cloud サーバーレスのデベロッパーがアプリケーションをモダナイズできるよう支援することを目的としています。これにより、アプリの移植性が向上し、オプションと柔軟性が高まるため、幅広い Cloud プロダクトとの統合やアクセスが可能になり、新しい言語リリースへのアップグレードが容易になります。このシリーズは、当初は初期の Cloud ユーザー、主に App Engine(標準環境)のデベロッパーを対象としていましたが、Cloud FunctionsCloud Run などの他のサーバーレス プラットフォームや、該当する場合は他のプラットフォームも対象とするほど幅広くなっています。

この Codelab の目的は、Python 2 App Engine デベロッパーが App Engine Users API/サービスから Cloud Identity Platform(GCIP)に移行する方法を示すことです。また、Datastore アクセス(主に移行モジュール 2 で説明)のための App Engine NDB から Cloud NDB への暗黙的な移行と、Python 3 へのアップグレードもあります。

モジュール 20 では、モジュール 1 のサンプルアプリに Users API の使用を追加する方法について説明しました。このモジュールでは、完成したモジュール 20 のアプリを取得し、その使用状況を Cloud Identity Platform に移行します。

GCP コンソールの

  • App Engine Users サービスの使用を Cloud Identity Platform に置き換える
  • App Engine NDB の使用を Cloud NDB に置き換えます(モジュール 2 もご覧ください)。
  • Firebase Auth を使用してさまざまな認証 ID プロバイダを設定する
  • Cloud Resource Manager API を使用してプロジェクトの IAM 情報を取得する
  • Firebase Admin SDK を使用してユーザー情報を取得する
  • サンプル アプリケーションを Python 3 に移植する

必要なもの

アンケート

このチュートリアルの利用方法をお選びください。

全体を通して読むだけ 内容を読んで演習をやり遂げる

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

初心者 中級者 上級者

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

初心者 中級者 上級者

2. 背景情報

App Engine Users サービスは、App Engine アプリで使用するユーザー認証システムです。ID プロバイダとして Google ログインを提供し、アプリで使用する便利なログイン リンクとログアウト リンクを提供し、管理者ユーザーと管理者専用機能のコンセプトをサポートします。アプリケーションの移植性を高めるため、Google Cloud では、以前の App Engine バンドル サービスから Cloud スタンドアロン サービスへの移行をおすすめします。たとえば、Users サービスから Cloud Identity Platform への移行などです。

Identity Platform は Firebase Authentication をベースにしており、多要素認証、OIDC と SAML SSO のサポート、マルチテナンシー、99.95% の SLA など、多数のエンタープライズ機能を追加します。これらの違いは、Identity Platform と Firebase Authentication のプロダクト比較ページでも確認できます。どちらのサービスも、ユーザー サービスが提供する機能よりもはるかに多くの機能を備えています。

このモジュール 21 の Codelab では、アプリのユーザー認証を Users サービスから、モジュール 20 で説明した機能に最も近い Identity Platform の機能に切り替える方法を説明します。モジュール 21 では、Datastore アクセス用の App Engine NDB から Cloud NDB への移行も取り上げ、モジュール 2 の移行を繰り返します。

モジュール 20 のコードは Python 2 サンプルアプリとして「宣伝」されていますが、ソース自体は Python 2 と 3 の両方に対応しており、モジュール 21 で Identity Platform(と Cloud NDB)に移行した後もその状態が維持されます。Identity Platform への移行は任意であるため、Python 3 にアップグレードしながら Users サービスを引き続き使用できます。Python 3 などの第 2 世代ランタイムにアップグレードしながらバンドル サービスを引き続き使用する方法については、モジュール 17 の Codelab と動画をご覧ください。

このチュートリアルでは、次の手順について説明します。

  1. セットアップ / 事前作業
  2. 構成の更新
  3. アプリケーション コードを変更する

3. セットアップ/事前作業

このセクションでは、次の方法を説明します。

  1. Cloud プロジェクトを設定する
  2. ベースライン サンプルアプリを入手する
  3. ベースライン アプリを(再)デプロイして検証する
  4. 新しい Google Cloud サービス/API を有効にする

これらの手順により、スタンドアロンの Cloud サービスへの移行の準備が整った動作コードから開始できます。

1. プロジェクトのセットアップ

モジュール 20 の Codelab を完了している場合は、同じプロジェクト(とコード)を再利用します。または、新しいプロジェクトを作成するか、別の既存のプロジェクトを再利用することもできます。プロジェクトにアクティブな請求先アカウントがあり、App Engine アプリが有効になっていることを確認します。プロジェクト ID を確認し、この Codelab で使用できるようにしておきます。PROJ_ID 変数が表示されたら、この ID を使用します。

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

前提条件の 1 つは、機能するモジュール 20 App Engine アプリを用意することです。そのため、Codelab(推奨、上のリンク)を完了するか、リポジトリからモジュール 20 のコードをコピーしてください。ご自分のコードと Google で用意したコードのいずれを使用される場合も、ここから開始します(「START」)。この Codelab では、移行の手順を説明します。完了すると、モジュール 21 のリポジトリ フォルダ(「FINISH」)にあるコードに似たコードが生成されます。

Module 20 のリポジトリ フォルダをコピーします。出力は次のようになります。モジュール 20 の Codelab を実行した場合は、lib フォルダが存在する可能性があります。

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

3. ベースライン アプリを(再)デプロイして検証する

モジュール 20 アプリをデプロイする手順は次のとおりです。

  1. lib フォルダが存在する場合は削除し、pip install -t lib -r requirements.txt を実行して再入力します。Python 2 と 3 の両方がインストールされている場合は、pip2 を使用する必要がある場合があります。
  2. gcloud コマンドライン ツールがインストールされ、初期化されていること、およびその使用方法を確認していることを確認します。
  3. gcloud コマンドを発行するたびに PROJ_ID を入力したくない場合は、最初に gcloud config set project PROJ_ID を使用して Cloud プロジェクトを設定します。
  4. gcloud app deploy を使用してサンプルアプリをデプロイする
  5. アプリがエラーなく想定どおりに動作することを確認します。モジュール 20 の Codelab を完了している場合、アプリの上部にユーザーのログイン情報(ユーザーのメールアドレス、管理バッジ(ある場合)、ログイン/ログアウト ボタン)と、最近のアクセス(下の図を参照)が表示されます。

907e64c19ef964f8.png

一般ユーザーとしてログインすると、ユーザーのメールアドレスが表示され、[ログイン] ボタンが [ログアウト] ボタンに変わります。

ad7b59916b69a035.png

管理者ユーザーとしてログインすると、ユーザーのメールアドレスの横に「(管理者)」と表示されます。

867bcb3334149e4.png

4. 新しい Google Cloud API/サービスを有効にする

はじめに

モジュール 20 アプリは、追加の設定を必要としないバンドル サービスである App Engine NDB API と Users API を使用しますが、スタンドアロンの Cloud サービスは追加の設定を必要とします。更新されたアプリは、Cloud Identity Platform と Cloud DatastoreCloud NDB クライアント ライブラリ経由)の両方を使用します。また、App Engine 管理ユーザーを特定する必要があるため、Cloud Resource Manager API も使用する必要があります。

費用

  • App Engine と Cloud Datastore には 「常時無料」枠の割り当てがあります。この上限を超えない限り、このチュートリアルを完了しても料金は発生しません。詳細については、App Engine の料金ページCloud Datastore の料金ページもご覧ください。
  • Cloud Identity Platform の使用料金は、月間アクティブ ユーザー数(MAU)または認証確認の数に応じて請求されます。各使用モデルで「無料」版を利用できます。詳細については、料金ページをご覧ください。また、App Engine と Cloud Datastore では課金が必要ですが、GCIP 自体の使用では、計測なしの 1 日あたりの割り当てを超えない限り、課金を有効にする必要はありません。課金が必要な Cloud API/サービスを使用しない Cloud プロジェクトでは、この点を考慮してください。
  • Cloud Resource Manager API の使用は、料金ページに記載されているように、ほとんどの場合無料です。

ユーザーは、必要に応じて Cloud コンソールまたはコマンドライン(Cloud SDK の一部である gcloud コマンドを使用)から Cloud APIs を有効にします。まず、Cloud Datastore API と Cloud Resource Manager API から始めましょう。

Cloud Console から

Cloud Console で API Manager の [ライブラリ] ページ(正しいプロジェクト)に移動し、検索バーを使用して API を検索します。c7a740304e9d35b.png

次の API を有効にします。

各 API の [有効にする] ボタンを個別にクリックします。請求情報の入力を求められることがあります。たとえば、Resource Manager API のページは次のようになります。

fc7bd8f4c49d12e5.png

有効にすると(通常は数秒後)、ボタンが [管理] に変わります。

8eca12d6cc7b45b0.png

同様に Cloud Datastore を有効にします。

83811599b110e46b.png

コマンドラインから

コンソールから API を有効にすると、視覚的にわかりやすくなりますが、コマンドラインを好むユーザーもいます。また、任意の数の API を一度に有効にできるというメリットもあります。次のコマンドを発行して、Cloud Datastore API と Cloud Resource Manager API の両方を有効にし、オペレーションが完了するまで待ちます。

$ gcloud services enable cloudresourcemanager.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

お支払い情報の入力を求められることがあります。

上記のコマンドで使用されている各 API の「URL」は API サービス名と呼ばれ、各 API のライブラリ ページの下部にあります。独自のアプリで他の Cloud APIs を有効にする場合は、対応する API ページでそれぞれのサービス名を確認できます。このコマンドは、有効にできる API のすべてのサービス名を一覧表示します。

gcloud services list --available --filter="name:googleapis.com"

Cloud コンソールまたはコマンドラインで上記の手順を完了すると、サンプルがこれらの API にアクセスできるようになります。次の手順では、Cloud Identity Platform を有効にして、必要なコード変更を行います。

Cloud Identity Platform を有効にして設定する(Cloud コンソールのみ)

Cloud Identity Platform は、Google Cloud の外部のリソース(Firebase Authentication など)に接続するか、それらに依存しているため、Marketplace サービスです。現時点では、Cloud コンソールからのみ Marketplace サービスを有効にできます。手順は次のとおりです。

  1. Cloud Marketplace の Cloud Identity Platform ページに移動し、[有効にする] ボタンをクリックします。Firebase Authentication からアップグレードします(プロンプトが表示された場合)。アップグレードすると、背景セクションで説明した機能など、追加機能が利用できるようになります。[有効にする] ボタンがハイライト表示された Marketplace ページ: 28475f1c9b29de69.png
  2. Identity Platform を有効にすると、[ID プロバイダ] ページに自動的に移動することがあります。まだの場合は、こちらの便利なリンクからアクセスしてください。fc2d92d42a5d1dd7.png
  3. Google Auth プロバイダを有効にします。プロバイダが設定されていない場合は、[プロバイダを追加] をクリックして [Google] を選択します。この画面に戻ると、[Google] エントリが有効になっています。このチュートリアルでは、App Engine Users サービスを軽量の Google ログイン サービスとしてミラーリングするために、Google のみを認証プロバイダとして使用しています。独自のアプリでは、追加の認証プロバイダを有効にできます。
  4. Google とその他の必要な認証プロバイダを選択して設定したら、[Application Setup Details] をクリックし、表示されたダイアログ ウィンドウで、[Web] タブの config オブジェクトにある apiKeyauthDomain をコピーして、安全な場所に保存します。すべてをコピーしないのはなぜですか?このダイアログのスニペットはハードコードされており、日付も古いため、最も重要な部分だけを保存し、Firebase Auth の同時使用を増やすコードで使用してください。値をコピーして安全な場所に保存したら、[閉じる] ボタンをクリックして、必要な設定をすべて完了します。bbb09dcdd9be538e.png

4. 構成を更新する

構成の更新には、さまざまな構成ファイルの変更と、App Engine と同等のものを Cloud Identity Platform エコシステム内に作成することが含まれます。

appengine_config.py

  • Python 3 にアップグレードする場合は、appengine_config.py を削除します。
  • Identity Platform に移行する予定だが、Python 2 を維持する場合は、ファイルを削除しないでください。代わりに、Python 2 バックポート時に更新します。

requirements.txt

モジュール 20 の requirements.txt ファイルには Flask のみが記載されていました。モジュール 21 の場合は、次のパッケージを追加します。

requirements.txt の内容は次のようになります。

flask
google-auth
google-cloud-ndb
google-cloud-resource-manager
firebase-admin

app.yaml

  • Python 3 にアップグレードすると、app.yaml ファイルが簡素化されます。ランタイム ディレクティブ以外のすべてを削除し、現在サポートされている Python 3 のバージョンに設定します。この例では、現在バージョン 3.10 を使用しています。
  • Python 2 を使用し続ける場合は、ここでは何も操作しないでください。

変更前:

runtime: python27
threadsafe: yes
api_version: 1

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

モジュール 20 のサンプルアプリには、静的ファイル ハンドラがありません。自分のアプリがそうである場合は、そのままにします。必要に応じて、すべてのスクリプト ハンドラを削除できます。また、app.yaml 移行ガイドで説明されているように、ハンドルを auto に変更する限り、参照用に残しておくこともできます。これらの変更により、Python 3 の更新された app.yaml は次のように簡略化されます。

変更後:

runtime: python310

その他の構成の更新

Python 2 を使用し続ける場合でも、Python 3 に移行する場合でも、lib フォルダがある場合は削除します。

5. アプリケーション コードを変更する

このセクションでは、メイン アプリケーション ファイル main.py の更新について説明します。この更新では、App Engine Users サービスの使用が Cloud Identity Platform に置き換えられています。メイン アプリケーションを更新したら、ウェブ テンプレート templates/index.html を更新します。

インポートと初期化を更新する

インポートを更新してアプリケーション リソースを初期化する手順は次のとおりです。

  1. インポートについては、App Engine NDB を Cloud NDB に置き換えます。
  2. Cloud NDB とともに、Cloud Resource Manager もインポートします。
  3. Identity Platform は Firebase Auth に基づいているため、Firebase Admin SDK をインポートします。
  4. Cloud API では API クライアントを使用する必要があるため、Flask の初期化の直後に Cloud NDB 用に初期化します。

ここでは Cloud Resource Manager パッケージをインポートしますが、アプリの初期化の後半で使用します。以下は、モジュール 20 のインポートと初期化、および上記の変更を実装した後のセクションの様子です。

変更前:

from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb

app = Flask(__name__)

変更後:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

# initialize Flask and Cloud NDB API client
app = Flask(__name__)
ds_client = ndb.Client()

App Engine 管理ユーザーのサポート

管理者ユーザーの認識をサポートするアプリに追加するコンポーネントは 2 つあります。

  • _get_gae_admins() - 管理ユーザーのセットを照合します。1 回呼び出されて保存されます
  • is_admin() - ログインしたユーザーが管理者ユーザーかどうかを確認します。ユーザーがログインするたびに呼び出されます。

ユーティリティ関数 _get_gae_admins() は、Resource Manager API を呼び出して現在の Cloud IAM allow-policy を取得します。許可ポリシーは、どのプリンシパル(人間のユーザー、サービス アカウントなど)にどのロールを付与するかを定義し、適用します。設定には次のものが含まれます。

  • Cloud プロジェクト ID(PROJ_ID)を取得する
  • Resource Manager API クライアントの作成(rm_client
  • App Engine 管理者ロール(_TARGETS)の(読み取り専用)セットを作成する

Resource Manager には Cloud プロジェクト ID が必要です。google.auth.default() をインポートし、その関数を呼び出してプロジェクト ID を取得します。この呼び出しには、URL のように見えるが OAuth2 権限スコープであるパラメータが含まれています。クラウドでアプリを実行する場合(Compute Engine VM や App Engine アプリなど)、広範な権限を持つデフォルトのサービス アカウントが提供されます。最小権限のベスト プラクティスに従って、独自のユーザー管理のサービス アカウントを作成することをおすすめします。

API 呼び出しの場合、アプリのスコープを適切に機能するために必要な最小限のレベルまでさらに縮小することをおすすめします。実行する Resource Manager API 呼び出しは get_iam_policy() です。この呼び出しを実行するには、次のいずれかのスコープが必要です

  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/cloud-platform.read-only
  • https://www.googleapis.com/auth/cloudplatformprojects
  • https://www.googleapis.com/auth/cloudplatformprojects.readonly

サンプルアプリに必要なのは、allow-policy への読み取り専用アクセス権のみです。ポリシーを変更することも、プロジェクト全体へのアクセス権限を必要とすることもありません。つまり、アプリは最初の 3 つの権限を必要としません。最後の 1 つだけが必須です。サンプルアプリではこれを実装します。

関数のメイン部分では、管理者ユーザーの空のセットを作成し(admins)、get_iam_policy() を介して allow_policy を取得し、すべてのバインディングをループして、App Engine 管理者ロールを検索します。

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin

検出されたターゲット ロールごとに、そのロールに属するユーザーを収集し、管理者ユーザーの全体的なセットに追加します。最後に、検出されてキャッシュに保存されたすべての管理者ユーザーを、この App Engine インスタンスのライフサイクル全体にわたる定数(_ADMINS)として返します。この呼び出しについては、後ほど説明します。

次の _get_gae_admins() 関数定義を、Cloud NDB API クライアント(ds_client)のインスタンス化の直後の main.py に追加します。

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    _, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
    rm_client = resourcemanager.ProjectsClient()
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
    for b in allow_policy.bindings:     # bindings in IAM allow-policy
        if b.role in _TARGETS:          # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b.members)
    return admins

ユーザーがアプリにログインすると、次の処理が行われます。

  1. ユーザーが Firebase にログインすると、ウェブ テンプレートから簡単なチェックが行われます。
  2. テンプレートで認証状態が変更されると、Ajax スタイルの fetch() 呼び出しが /is_admin に対して行われます。このハンドラは次の関数 is_admin() です。
  3. Firebase ID トークンは POST 本文で is_admin() に渡されます。is_admin() はヘッダーからトークンを取得し、Firebase Admin SDK を呼び出して検証します。有効なユーザーの場合は、メールアドレスを抽出し、管理者ユーザーかどうかを確認します。
  4. ブール値の結果は、成功を示す 200 としてテンプレートに返されます。

_get_gae_admins() の直後の main.pyis_admin() を追加します。

@app.route('/is_admin', methods=['POST'])
def is_admin():
    'check if user (via their Firebase ID token) is GAE admin (POST) handler'
    id_token = request.headers.get('Authorization')
    email = auth.verify_id_token(id_token).get('email')
    return {'admin': email in _ADMINS}, 200

両方の関数のコードはすべて、ユーザー サービス(特に is_current_user_admin() 関数)から利用できる機能を複製するために必要です。モジュール 20 のこの関数呼び出しは、置き換えソリューションを実装するモジュール 21 とは異なり、すべての処理を行いました。このアプリは App Engine 専用のサービスに依存しなくなったため、アプリを Cloud Run などのサービスに移行できます。また、_TARGETS で目的のロールに切り替えるだけで、独自のアプリの「管理者ユーザー」の定義を変更することもできます。一方、Users サービスは App Engine 管理者ロール用にハードコードされています。

Firebase Auth を初期化して App Engine 管理ユーザーをキャッシュに保存する

Firebase Auth は、Flask アプリが初期化され、Cloud NDB API クライアントが作成された場所の近くで初期化することもできましたが、すべての管理コードが定義されるまで必要ありませんでした。同様に、_get_gae_admins() が定義されたので、それを呼び出して管理者ユーザーのリストをキャッシュに保存します。

is_admin() の関数本体のすぐ下に次の行を追加します。

# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()

データモデルの更新にアクセスする

Visit データモデルは変更されません。Datastore にアクセスするには、Cloud NDB API クライアント コンテキスト マネージャー ds_client.context() を明示的に使用する必要があります。コードでは、Python の with ブロック内の store_visit()fetch_visits() の両方で Datastore 呼び出しをラップします。このアップデートはモジュール 2 と同一です。次のように変更します。

変更前:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

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 Visit.query().order(-Visit.timestamp).fetch(limit)

変更後:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

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 Visit.query().order(-Visit.timestamp).fetch(limit)

ユーザー ログイン ロジックをウェブ テンプレートに移動

App Engine Users サービスはサーバーサイドですが、Firebase Auth と Cloud Identity Platform は主にクライアント サイドです。その結果、Module 20 アプリのユーザー管理コードの多くが Module 21 ウェブ テンプレートに移動します。

main.py では、ウェブ コンテキストが 5 つの重要なデータをテンプレートに渡します。最初の 4 つはユーザー管理に関連付けられており、ユーザーがログインしているかどうかによって異なります。

  • who - ログインしている場合はユーザーのメールアドレス、それ以外の場合は user
  • admin - ログインしているユーザーが管理者の場合は (管理者)バッジ
  • sign - [ログイン] ボタンまたは [ログアウト] ボタンを表示する
  • link - ボタンクリック時のログインまたはログアウトのリンク
  • visits - 最近のアクセス

変更前:

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

    # put together users context for web template
    user = users.get_current_user()
    context = {  # logged in
        'who':   user.nickname(),
        'admin': '(admin)' if users.is_current_user_admin() else '',
        'sign':  'Logout',
        'link':  '/_ah/logout?continue=%s://%s/' % (
                      request.environ['wsgi.url_scheme'],
                      request.environ['HTTP_HOST'],
                  ),  # alternative to users.create_logout_url()
    } if user else {  # not logged in
        'who':   'user',
        'admin': '',
        'sign':  'Login',
        'link':  users.create_login_url('/'),
    }

    # add visits to context and render template
    context['visits'] = visits  # display whether logged in or not
    return render_template('index.html', **context)

ユーザー管理はすべてウェブ テンプレートに移行するため、訪問のみが残ります。これにより、メイン ハンドラはモジュール 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)

ウェブ テンプレートを更新する

前のセクションのすべての更新は、テンプレートでどのように表示されますか?主に、ユーザー管理をアプリからテンプレートで実行される Firebase Auth に移行し、移行したコードの一部を JavaScript に移植しました。main.py はかなり縮小したため、templates/index.html も同様の成長が見込まれます。

変更前:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>
<p>
Welcome, {{ who }} <code>{{ admin }}</code>
<button id="logbtn">{{ sign }}</button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
document.getElementById("logbtn").onclick = () => {
    window.location.href = '{{ link }}';
};
</script>
</body>
</html>

ウェブ テンプレート全体を次の内容に置き換えます。

変更後:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

この HTML 本文には多くのコンポーネントが含まれているため、1 つずつ見ていきましょう。

Firebase のインポート

HTML ドキュメントのヘッダーで、ページ タイトルを過ぎたら、必要な Firebase コンポーネントをインポートします。Firebase コンポーネントは、効率性を高めるために複数のモジュールに分割されました。Firebase を初期化するコードはメインの Firebase アプリ モジュールからインポートされます。一方、Firebase 認証、認証プロバイダとしての Google、ログインとログアウト、認証状態の変更の「コールバック」を管理する関数はすべて Firebase Auth モジュールからインポートされます。

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

Firebase の構成

このチュートリアルの Identity Platform の設定パートで、[アプリケーション設定の詳細] ダイアログから apiKeyauthDomain を保存しました。これらの値を次のセクションの firebaseConfig 変数に追加します。詳細な手順へのリンクは、コメントに記載されています。

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

Firebase の初期化

次のセクションでは、この構成情報を使用して Firebase を初期化します。

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

これにより、認証プロバイダとして Google を使用する機能が設定されます。また、ブラウザ セッションに登録されている Google アカウントが 1 つしかない場合でもアカウント選択ツールを表示するためのコメントアウトされたオプションが提供されます。つまり、複数のアカウントがある場合は、次のようにアカウント選択ツールが表示されます。a38369389b7c4c7e.png ただし、セッションにユーザーが 1 人しかいない場合は、ユーザーの操作なしでログイン プロセスが自動的に完了します。(ポップアップが表示されてから消えます)。カスタム パラメータの行のコメントを解除すると、1 人のユーザーに対してアカウント選択ツール ダイアログを強制的に表示できます(アプリにすぐにログインするのではなく)。有効にすると、シングル ユーザー ログインでもアカウント選択ツールが表示されます。b75624cb68d94557.png

ログイン関数とログアウト関数

次のコード行は、ログイン ボタンまたはログアウト ボタンのクリックの関数を構成します。

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

ログインとログアウトのアクション

この <script> ブロックの最後の主要なセクションは、認証の変更(ログインまたはログアウト)ごとに呼び出される関数です。

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

モジュール 20 のコードで、ユーザーがログインしているかログアウトしているかに応じてテンプレート コンテキストを送信するかどうかが判断されます。ユーザーが正常にログインした場合、最上位の条件は true になり、次のアクションがトリガーされます。

  1. ユーザーのメールアドレスが表示されるように設定されています。
  2. [ログイン] ボタンが [ログアウト] に変わります。
  3. /is_admin への Ajax スタイルの呼び出しが行われ、(admin) 管理ユーザー バッジを表示するかどうかが判断されます。

ユーザーがログアウトすると、else 句が実行され、すべてのユーザー情報がリセットされます。

  1. ユーザー名が user に設定されている
  2. 管理者バッジが削除された
  3. [ログアウト] ボタンが [ログイン] に戻る

テンプレート変数

ヘッダー セクションの終了後、メインの本文は、必要に応じて変更される HTML 要素に置き換えられるテンプレート変数で始まります。

  1. 表示されるユーザー名
  2. (admin) 管理者バッジ(該当する場合)
  3. [ログイン] または [ログアウト] ボタン
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

最新の訪問と HTML 要素の変数

最近のアクセスコードは変更されません。最後の <script> ブロックでは、直前のログインとログアウトで変更される HTML 要素の変数を設定します。

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

これで、App Engine NDB API と Users API から Cloud NDB と Identity Platform に切り替え、Python 3 にアップグレードするためにアプリケーションとウェブ テンプレートに必要な変更は完了です。新しいモジュール 21 のサンプルアプリにたどり着きました。おめでとうございます。このバージョンは、Module 21b のリポジトリ フォルダで確認できます。

Codelab の次の部分はオプション(*)で、アプリを Python 2 に残す必要があるユーザーのみを対象としています。この部分では、動作する Python 2 モジュール 21 アプリを作成するために必要な手順を説明します。

6. *Python 2 バックポート

この省略可能なセクションは、Identity Platform の移行を実施するものの、Python 2 ランタイムで引き続き実行する必要があるデベロッパーを対象としています。この点が問題にならない場合は、このセクションをスキップしてください。

Module 21 アプリの動作する Python 2 バージョンを作成するには、次のものが必要です。

  1. ランタイム要件: Python 2 をサポートする構成ファイルと、Python 3 の非互換性を回避するためにメイン アプリケーションに必要な変更
  2. ライブラリのマイナーな変更: 必要な機能が Resource Manager クライアント ライブラリに追加される前に、Python 2 は非推奨になりました。そのため、欠落している機能にアクセスする別の方法が必要になります。

それでは、構成から始めましょう。

appengine_config.py を復元する

このチュートリアルの前半では、Python 3 App Engine ランタイムで使用されていないため、appengine_config.py を削除するように説明しました。Python 2 の場合は、保存する必要があるだけでなく、Module 20 appengine_config.py を更新して、組み込みのサードパーティ ライブラリgrpciosetuptools)の使用をサポートする必要があります。これらのパッケージは、App Engine アプリで Cloud NDB や Cloud Resource Manager などの Cloud クライアント ライブラリを使用する場合に必要です。

これらのパッケージはすぐに app.yaml に追加しますが、アプリがこれらのパッケージにアクセスするには、setuptoolspkg_resources.working_set.add_entry() 関数を呼び出す必要があります。これにより、lib フォルダにインストールされたコピーされた(セルフバンドルまたはベンダー提供の)サードパーティ ライブラリが、組み込みライブラリと通信できるようになります。

これらの変更を反映するには、appengine_config.py ファイルに次の更新を実装します。

変更前:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

このコードだけでは、setuptoolsgrpcio の使用をサポートするのに十分ではありません。さらに数行必要なので、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)

Cloud クライアント ライブラリをサポートするために必要な変更の詳細については、バンドル サービスの移行に関するドキュメントをご覧ください。

app.yaml

appengine_config.py と同様に、app.yaml ファイルも Python 2 をサポートするものに戻す必要があります。まず、元のモジュール 20 の app.yaml から始めましょう。

変更前:

runtime: python27
threadsafe: yes
api_version: 1

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

前述の setuptoolsgrpcio に加えて、Cloud Storage クライアント ライブラリの使用を必要とする依存関係(Identity Platform の移行に明示的に関連するものではありません)があり、ssl という別の組み込みのサードパーティ パッケージが必要です。新しい libraries セクションに 3 つすべてを追加し、それらのパッケージの利用可能な「最新」バージョンを選択して app.yaml にします。

変更後:

runtime: python27
threadsafe: yes
api_version: 1

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

libraries:
- name: grpcio
  version: latest
- name: setuptools
  version: latest
- name: ssl
  version: latest

requirements.txt

モジュール 21 では、Python 3 requirements.txtGoogle AuthCloud NDBCloud Resource ManagerFirebase Admin SDK を追加しました。Python 2 の状況はより複雑です。

  • Resource Manager API は、サンプルアプリに必要な許可ポリシー機能を提供します。残念ながら、このサポートは Cloud Resource Manager クライアント ライブラリPython 2 の最終バージョンではまだ利用できませんでした。(Python 3 バージョンでのみ使用できます)。
  • そのため、API からこの機能にアクセスする別の方法が必要になります。この問題を解決するには、下位レベルの Google APIs クライアント ライブラリを使用して API と通信します。このクライアント ライブラリに切り替えるには、google-cloud-resource-manager を下位レベルの google-api-python-client パッケージに置き換えます。
  • Python 2 はサポートが終了しているため、モジュール 21 をサポートする依存関係グラフでは、特定のパッケージを特定のバージョンにロックする必要があります。一部のパッケージは、Python 3 の app.yaml で指定されていなくても、呼び出す必要があります。

変更前:

flask

モジュール 20 の requirements.txt から、モジュール 21 のアプリが動作するように次のように更新します。

変更後:

grpcio==1.0.0
protobuf<3.18.0
six>=1.13.0
flask
google-gax<0.13.0
google-api-core==1.31.1
google-api-python-client<=1.11.0
google-auth<2.0dev
google-cloud-datastore==1.15.3
google-cloud-firestore==1.9.0
google-cloud-ndb
google-cloud-pubsub==1.7.0
firebase-admin

パッケージとバージョン番号は、依存関係が変更されるとリポジトリで更新されますが、この app.yaml は、このドキュメントの作成時点では機能するアプリに十分です。

その他の構成の更新

この Codelab の前半で lib フォルダを削除していない場合は、ここで削除します。更新された requirements.txt で、次のコマンドを実行して、これらの要件を lib にインストールします。

pip install -t lib -r requirements.txt  # or pip2

開発システムに Python 2 と 3 の両方がインストールされている場合は、pip ではなく pip2 を使用する必要がある場合があります。

アプリケーション コードを変更する

幸いなことに、必要な変更のほとんどは構成ファイルにあります。アプリケーション コードで必要な変更は、API にアクセスするために Resource Manager クライアント ライブラリではなく下位レベルの Google API クライアント ライブラリを使用するようにマイナー アップデートすることだけです。templates/index.html ウェブ テンプレートの更新は必要ありません。

インポートと初期化を更新する

次の図に示すように、Resource Manager クライアント ライブラリ(google.cloud.resourcemanager)を Google API クライアント ライブラリ(googleapiclient.discovery)に置き換えます。

変更前:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

変更後:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb
from googleapiclient import discovery
from firebase_admin import auth, initialize_app

App Engine 管理ユーザーのサポート

下位レベルのクライアント ライブラリの使用をサポートするために、_get_gae_admins() でいくつかの変更が必要です。まず変更点について説明し、次に更新用のコードをすべて提供します。

Python 2 コードでは、google.auth.default() から返された認証情報とプロジェクト ID の両方を使用する必要があります。認証情報は Python 3 では使用されないため、汎用のアンダースコア(_)ダミー変数に割り当てられました。Python 2 バージョンで必要になるため、アンダースコアを CREDS に変更します。また、Resource Manager API クライアントを作成するのではなく、API クライアントと概念的に類似した API サービス エンドポイントを作成するため、同じ変数名(rm_client)を保持します。1 つの違いは、サービス エンドポイントのインスタンス化には認証情報(CREDS)が必要であることです。

これらの変更は、次のコードに反映されています。

変更前:

_, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()

変更後:

CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)

もう 1 つの違いは、Resource Manager クライアント ライブラリはドット属性表記を使用する許可ポリシー オブジェクトを返しますが、下位レベルのクライアント ライブラリは角かっこ([ ])が使用される Python 辞書を返します。たとえば、Resource Manager クライアント ライブラリでは binding.role を使用しますが、下位レベルのライブラリでは binding['role'] を使用します。前者は「underscore_separated」という名前を使用しますが、下位レベルのライブラリは「CamelCased」という名前を使用し、API パラメータの渡し方も若干異なります。

使用方法の違いは次のとおりです。

変更前:

allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings:     # bindings in IAM allow-policy
    if b.role in _TARGETS:          # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b.members)

変更後:

allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']:  # bindings in IAM allow-policy
    if b['role'] in _TARGETS:       # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b['members'])

これらの変更をすべてまとめると、Python 3 の _get_gae_admins() は、次の同等の Python 2 バージョンに置き換えられます。

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloud-platform'])
    rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
    for b in allow_policy['bindings']:  # bindings in IAM allow-policy
        if b['role'] in _TARGETS:       # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b['members'])
    return admins

is_admin() 関数は、すでに更新されている _get_gae_admins() に依存しているため、更新は必要ありません。

これで、Python 3 モジュール 21 アプリを Python 2 にバックポートするために必要な変更は完了です。モジュール 21 のサンプルアプリの更新が完了しました。すべてのコードは Module 21a リポジトリ フォルダにあります。

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

この Codelab の最後の手順では、このアプリを実行するプリンシパル(ユーザーまたはサービス アカウント)に適切な権限があることを確認し、アプリをデプロイして、意図したとおりに動作し、変更が出力に反映されることを確認します。

IAM 許可ポリシーを読み取る権限

以前、App Engine 管理ユーザーとして認識されるために必要な 4 つのロールをご紹介しましたが、新たに 5 つ目のロールが追加されました。

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin(IAM 許可ポリシーにアクセスするプリンシパル)

roles/resourcemanager.projectIamAdmin ロールを使用すると、プリンシパルはエンドユーザーが App Engine 管理者ロールのいずれかのメンバーであるかどうかを判断できます。roles/resourcemanager.projectIamAdmin のメンバーでない場合、Cloud Resource Manager API を呼び出して許可ポリシーを取得しようとすると失敗します。

アプリは App Engine のデフォルトのサービス アカウントで実行され、このロールのメンバーシップが自動的に付与されるため、ここで明示的な操作を行う必要はありません。開発フェーズでデフォルトのサービス アカウントを使用する場合でも、アプリが正しく動作するために必要な最小限の権限を持つユーザー管理のサービス アカウントを作成して使用することを強くおすすめします。このようなサービス アカウントにメンバーシップを付与するには、次のコマンドを実行します。

$ gcloud projects add-iam-policy-binding PROJ_ID --member="serviceAccount:USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com" --role=roles/resourcemanager.projectIamAdmin

PROJ_ID は Cloud プロジェクト ID で、USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com はアプリ用に作成するユーザー管理のサービス アカウントです。このコマンドは、プロジェクトの更新された IAM ポリシーを出力します。ここで、サービス アカウントが roles/resourcemanager.projectIamAdmin のメンバーであることを確認できます。詳細については、リファレンス ドキュメントをご覧ください。繰り返しますが、この Codelab ではこのコマンドを発行する必要はありません。ただし、このコマンドは、独自のアプリを最新化する際の参考として保存してください。

アプリケーションをデプロイして検証する

標準の gcloud app deploy コマンドを使用して、アプリをクラウドにアップロードします。デプロイが完了すると、Module 20 アプリとほぼ同じ機能が表示されます。ただし、ユーザー管理に App Engine Users サービスではなく Cloud Identity Platform(と Firebase Auth)が使用されている点が異なります。

3a83ae745121d70.png

モジュール 20 との大きな違いは、[ログイン] をクリックするとリダイレクトではなくポップアップが表示されることです。以下のスクリーンショットの一部で確認できます。ただし、モジュール 20 と同様に、ブラウザに登録されている Google アカウントの数によって動作が若干異なります。

ブラウザに登録されているユーザーがいない場合、またはまだログインしていないユーザーが 1 人だけの場合は、一般的な Google ログインのポップアップが表示されます。

8437f5f3d489a942.png

ブラウザに 1 人のユーザーが登録されているが、別の場所でログインしている場合、ダイアログは表示されません(または、ポップアップ表示されてすぐに閉じます)。アプリはログイン状態になり(ユーザーのメールアドレスと [ログアウト] ボタンが表示されます)。

デベロッパーによっては、単一のユーザーに対してもアカウント選択ツールを提供したい場合があります。

b75624cb68d94557.png

これを実装するには、前述のようにウェブ テンプレートで provider.setCustomParameters({prompt: 'select_account'}); 行のコメントを解除します。

ユーザーが複数いる場合は、アカウント選択ダイアログが表示されます(下記参照)。まだログインしていない場合は、ログインを求めるメッセージが表示されます。すでにログインしている場合は、ポップアップが消え、アプリがログイン状態になります。

c454455b6020d5e4.png

モジュール 21 のログイン状態は、モジュール 20 のユーザー インターフェースとまったく同じです。

49ebe4dcc1eff11f.png

管理者ユーザーがログインした場合も同様です。

44302f35b39856eb.png

モジュール 21 とは異なり、モジュール 20 は常にアプリ(サーバーサイド コード)からウェブ テンプレート コンテンツのロジックにアクセスします。モジュール 20 の欠点は、エンドユーザーがアプリに初めてアクセスしたときに 1 回のアクセスが登録され、ユーザーがログインしたときにもう 1 回のアクセスが登録されることです。

モジュール 21 では、ログイン ロジックはウェブ テンプレート(クライアントサイド コード)でのみ実行されます。表示するコンテンツを決定するために必要なサーバーサイドのトリップはありません。サーバーへの呼び出しは、エンドユーザーのログイン後に管理者ユーザーを確認する呼び出しのみです。つまり、ログインとログアウトは追加のアクセスとして登録されないため、ユーザー管理アクションの最新のアクセスリストは一定のままになります。上記のスクリーンショットでは、複数のユーザー ログインにわたって同じ 4 回の訪問が表示されています。

モジュール 20 のスクリーンショットは、この Codelab の冒頭で説明した「二重訪問バグ」を示しています。ログインまたはログアウトのアクションごとに、個別のアクセスログが表示されます。各スクリーンショットの最新のアクセス日時のタイムスタンプを確認して、時系列順に並んでいることを確認します。

クリーンアップ

全般

現時点で完了した場合は、課金が発生しないように App Engine アプリを無効にすることをおすすめします。ただし、さらにテストや実験を行う場合は、App Engine プラットフォームに無料割り当てがあります。この使用量枠を超えない限り、課金されることはありません。これはコンピューティングの料金ですが、関連する App Engine サービスの料金も発生する可能性があります。詳細については、料金ページをご覧ください。この移行に他の Cloud サービスが含まれる場合は、それらのサービスは別途請求されます。どちらの場合も、該当する場合は、以下の「この Codelab に固有」のセクションをご覧ください。

完全に開示すると、App Engine などの Google Cloud サーバーレス コンピューティング プラットフォームにデプロイすると、ビルドとストレージの費用がわずかに発生しますCloud Build には独自の無料割り当てがあり、Cloud Storage にも独自の無料割り当てがあります。そのイメージの保存には、その割り当ての一部が使用されます。ただし、お住まいの地域でこのような無料枠が提供されていない可能性もあります。そのため、保存容量の使用状況を把握して、費用を最小限に抑えるようにしてください。確認する必要がある特定の Cloud Storage の「フォルダ」は次のとおりです。

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • 上記のストレージ リンクは、PROJECT_IDLOCーションによって異なります。たとえば、アプリが米国でホストされている場合は「us」になります。

ただし、このアプリケーションや関連する移行 Codelab を続行せず、すべてを完全に削除する場合は、プロジェクトをシャットダウンしてください。

この Codelab に固有

以下のサービスは、この Codelab 固有のものです。詳細については、各プロダクトのドキュメントをご覧ください。

  • App Engine Datastore サービスは Cloud Datastore(Datastore モードの Cloud Firestore)によって提供されます。これにも無料枠があります。詳細については、料金ページをご覧ください。
  • Cloud Identity Platform の使用には、使用するサービスに応じてある程度の「無料」が含まれています。詳細については、料金ページをご覧ください。
  • Cloud Resource Manager API の使用は、料金ページに記載されているように、ほとんどの場合無料です。

次のステップ

このチュートリアル以外にも、以前のバンドル サービスからの移行に焦点を当てた移行モジュールがあります。

App Engine は、Google Cloud の唯一のサーバーレス プラットフォームではなくなりました。小規模な App Engine アプリや、機能が制限されているアプリをスタンドアロンのマイクロサービスに変換したい場合や、モノリシック アプリを複数の再利用可能なコンポーネントに分割したい場合は、Cloud Functions への移行を検討する理由として十分です。コンテナ化がアプリケーション開発ワークフローの一部になっている場合(特に CI/CD(継続的インテグレーション/継続的デリバリーまたはデプロイ)パイプラインで構成されている場合)は、Cloud Run への移行を検討してください。これらのシナリオは、次のモジュールで取り上げます。

  • App Engine から Cloud Functions に移行する: モジュール 11 をご覧ください。
  • App Engine から Cloud Run に移行する: モジュール 4 で Docker を使用してアプリをコンテナ化するか、モジュール 5 でコンテナ、Docker の知識、Dockerfile を使用せずにコンテナ化します。

別のサーバーレス プラットフォームへの切り替えは任意です。変更を行う前に、アプリとユースケースに最適なオプションを検討することをおすすめします。

次にどの移行モジュールを検討する場合でも、すべてのサーバーレス移行ステーションのコンテンツ(Codelab、動画、ソースコード [利用可能な場合])は、オープンソース リポジトリからアクセスできます。リポジトリの README には、考慮すべき移行と、移行モジュールの関連する「順序」に関するガイダンスも記載されています。

8. 参考情報

以下に、この移行モジュールまたは関連する移行モジュールをさらに詳しく調べるデベロッパー向けの追加リソースを示します。以下では、このコンテンツに関するフィードバックを送信したり、コードへのリンクや、役立つ可能性のあるさまざまなドキュメントを見つけたりできます。

Codelab に関する問題/フィードバック

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

移行に関するリソース

モジュール 20(開始)とモジュール 21(終了)のリポジトリ フォルダへのリンクを以下の表に示します。

Codelab

Python 2

Python 3

モジュール 20

コード

該当なし

モジュール 21(この Codelab)

コード

コード

オンライン リファレンス

このチュートリアルに関連するリソースは次のとおりです。

Cloud Identity Platform と Cloud Marketplace

Cloud Resource Manager、Cloud IAM、Firebase Admin SDK

App Engine Users、App Engine NDB、Cloud NDB、Cloud Datastore

その他の Migration Module のリファレンス

App Engine の移行

App Engine プラットフォーム

Cloud SDK

その他のクラウド情報

動画

ライセンス

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