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

1. 概要

Serverless Migration Station の Codelab シリーズ(セルフペース型のハンズオン チュートリアル)と関連動画は、Google Cloud サーバーレス デベロッパーが主にレガシー サービスからの移行を 1 つ以上の移行を通じてガイドし、アプリをモダナイズできるようにすることを目的としています。これにより、アプリのポータビリティが高まり、選択肢と柔軟性が増し、より広範な Cloud プロダクトと統合してアクセスできるようになり、より新しい言語リリースに簡単にアップグレードできるようになります。当初は初期の Cloud ユーザー、主に App Engine(スタンダード環境)のデベロッパーを対象としていますが、Cloud FunctionsCloud Run など、その他のサーバーレス プラットフォーム(該当する場合)まで幅広くカバーしています。

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

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

GCP コンソールの

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

必要なもの

アンケート

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

<ph type="x-smartling-placeholder"></ph> 最後まで読んでください 内容を読んで演習を済ませる をご覧ください。

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

初心者 中級者 上級者

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

<ph type="x-smartling-placeholder"></ph> 初心者 中級 上達 をご覧ください。

2. 背景情報

App Engine ユーザー サービスは、App Engine アプリで使用するユーザー認証システムです。ID プロバイダとして Google ログインを提供し、アプリで使用する便利なログイン リンクとログアウト リンクを提供し、管理者ユーザーと管理者専用機能のコンセプトをサポートしています。Google Cloud では、アプリケーションのポータビリティを向上させるために、以前の App Engine バンドル サービスから Cloud スタンドアロン サービスに移行すること(ユーザー サービスから Cloud Identity Platform などなど)を推奨しています。

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

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

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

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

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

3. 設定/事前作業

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

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

この手順により、コードをスタンドアロンのクラウド サービスに移行できる実用的なコードから始めることができます。

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

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

2. ベースラインのサンプルアプリを取得する

前提条件の一つは、モジュール 20 の App Engine アプリを実行することです。Codelab を完了するか(推奨、上記のリンク)、リポジトリからモジュール 20 のコードをコピーします。お客様のものか弊社によるものかを問わず、ここから始まります(「開始」)。この Codelab では移行手順について説明します。最後に、モジュール 21 のリポジトリ フォルダ(「FINISH」)にあるコードに似たコードを使用します。

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

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

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

次の手順で Module 20 アプリをデプロイします。

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

907e64c19ef964f8.png

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

ad7b59916b69a035.png

管理者ユーザーとしてログインすると、ユーザーのメールアドレスが「(管理者)」と一緒に表示される次の操作を行います。

867bcb3334149e4.png

4. 新しい Google Cloud APIs/サービスの有効化

はじめに

モジュール 20 アプリでは、App Engine NDB と 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 の使用は、1 か月のアクティブ ユーザー(MAU)数または認証確認の数に応じて課金されます。「無料」の利用モデルごとに異なります詳しくは料金ページをご覧ください。さらに、App Engine と Cloud Datastore には課金が必要ですが、GCIP を使用するだけでは、計測を必要としない 1 日あたりの割り当てを超えない限り課金を有効にする必要はありません。そのため、課金が必要な Cloud APIs/サービスを必要としない Cloud プロジェクトの場合は、課金を有効にすることを検討してください。
  • Cloud Resource Manager API は料金ページのとおり、ほとんどの部分で無料でご利用いただけます。

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

Cloud Console から

Cloud コンソールで 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.

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

「URL」上記のコマンドで使用されている各 API は、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 認証プロバイダを有効にします。プロバイダが設定されていない場合は、[プロバイダを追加] をクリックして [Google] を選択します。この画面に戻ると、[Google] エントリが有効になっているはずです。このチュートリアルでは、App Engine ユーザー サービスを軽量の 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 ユーザー サービスを 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 APIs では API クライアントを使用する必要があるため、Flask を初期化するすぐ下で Cloud NDB に対して API クライアントを開始します。

ここでは 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() - 管理者ユーザーのセットを照合します。保存されたデータは
  • 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 つの権限のいずれも不要です。最後の要素は必要なだけです。サンプルアプリではこれを実装します。

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

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

検出されたロールごとに、そのロールに属するユーザーが照合され、管理者ユーザーのセット全体に追加されます。この App Engine インスタンスが存在する間、検出されてキャッシュに保存されたすべての管理ユーザーを定数(_ADMINS)として返すことで終了します。間もなくお電話を差し上げます。

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

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. テンプレートの認証状態が変更されると、次の関数 is_admin() がハンドラとなる /is_admin に対して Ajax スタイルの fetch() 呼び出しが行われます。
  3. Firebase ID トークンが POST 本文で 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

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

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

Flask アプリが初期化され、Cloud NDB API クライアントが作成されたのと同じ位置の近くに Firebase Auth を初期化できましたが、すべての管理コードが定義されるまでその必要はありませんでした。今のところです。同様に、_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() を明示的に使用する必要があります。つまり、コードでは、Datastore の呼び出しを Python with ブロック内の store_visit()fetch_visits() の両方でラップすることになります。この更新はモジュール 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 のユーザー サービスはサーバーサイドで、Firebase Auth と Cloud Identity Platform は主にクライアントサイドです。その結果、モジュール 20 アプリのユーザー管理コードの多くは、モジュール 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 人のユーザーにアカウント選択ダイアログを強制的に表示できます。有効にした場合、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. [Logout] ボタンが [Login] に戻る

テンプレート変数

ヘッダー セクションが終了すると、メインの本文はテンプレート変数から始まります。変数は、必要に応じて変更される 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 と Users API から Cloud NDB と Identity Platform に切り替え、Python 3 にアップグレードするために必要なアプリケーションとウェブ テンプレートの変更は完了です。モジュール 21 の新しいサンプルアプリをご利用いただきありがとうございます。このバージョンは、モジュール 21b のリポジトリ フォルダで確認できます。

Codelab の次の部分はオプション(*)であり、アプリを Python 2 のままにしておく必要があるユーザーのみを対象としており、Python 2 Module 21 アプリを正しく動作させるために必要な手順を説明します。

6. *Python 2 バックポート

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

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

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

それでは、構成から手順に進みましょう。

appengine_config.py を復元する

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

これらのパッケージはすぐに app.yaml に追加しますが、アプリからアクセスするには、setuptools から pkg_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 つすべてを追加し、[latest] を選択します。これらのパッケージの利用可能なバージョンを 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 では、Google AuthCloud NDBCloud Resource ManagerFirebase Admin SDK を Python 3 requirements.txt に追加しました。Python 2 の状況はより複雑です。

  • Resource Manager API は、サンプルアプリに必要な許可ポリシー機能を提供します。残念ながら、Cloud Resource Manager クライアント ライブラリPython 2 の最終バージョンでは、まだこのサポートが提供されていませんでした。(Python 3 バージョンでのみ使用できます)。
  • そのため、API からこの機能にアクセスするには、別の方法が必要になります。この問題を解決するには、下位レベルの Google API クライアント ライブラリを使用して 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 と Python 3 の両方がインストールされている場合は、pip ではなく pip2 を使用する必要があります。

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

幸いなことに、必要な変更のほとんどは構成ファイルにあります。アプリケーション コードで必要な変更は、Resource Manager クライアント ライブラリではなく、下位レベルの Google API クライアント ライブラリを使用して 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)

もう一つの違いは、Resource Manager クライアント ライブラリはドット属性表記を使用する allow-policy オブジェクトを返すのに対し、下位レベルのクライアント ライブラリは角かっこ([ ])を使用した Python 辞書を返すことです。たとえば、Resource Manager クライアント ライブラリでは binding.role を使用し、下位レベル ライブラリでは binding['role'] を使用します。前者でも「underscore_sensitive」を使用します。「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 のサンプル アプリを更新できました。モジュール 21a リポジトリ フォルダにすべてのコードがあります。

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

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

IAM allow-policy の読み取り権限

先ほど、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 コマンドを使用して、アプリをクラウドにアップロードします。デプロイが完了すると、App Engine ユーザー サービスが Cloud Identity Platform(および Firebase Auth)に正常に置き換えられ、ユーザー管理が行える点を除き、モジュール 20 アプリとほぼ同じ機能が表示されます。

3a83ae745121d70.png

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

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

8437f5f3d489a942.png

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

デベロッパーによっては、1 人のユーザーであっても、アカウント選択ツールを用意したい場合があります。

b75624cb68d94557.png

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

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

c454455b6020d5e4.png

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

49ebe4dcc1eff11f.png

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

44302f35b39856eb.png

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

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

モジュール 20 のスクリーンショットは、「二重訪問バグ」を示しています。見てみましょう。ログインやログアウトの操作ごとに別々のアクセスログが表示されます。時系列順を示す各スクリーンショットについて、直近のアクセスのタイムスタンプを確認します。

クリーンアップ

全般

現時点で完了したら、課金が発生しないように App Engine アプリを無効にすることをおすすめします。さらにテストや実験を行う場合は、App Engine プラットフォームに無料の割り当てが用意されています。この使用量ティアを超えない限り、料金は発生しません。これはコンピューティングに関するものですが、関連する App Engine サービスに対して料金が発生する場合もあります。詳細については、料金ページをご覧ください。この移行に他のクラウド サービスが含まれる場合、それらは別途請求されます。いずれの場合も、該当する場合は「この 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 を使用せずに

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

次に検討する移行モジュールに関係なく、Serverless Migration Station のすべてのコンテンツ(Codelab、動画、ソースコード(利用可能な場合))には、オープンソース リポジトリからアクセスできます。リポジトリの README には、検討すべき移行や関連する「順序」に関するガイダンスも用意されています。概要をまとめたものです

8. 参考情報

以下に、今回の移行モジュールまたは関連する移行モジュールをさらに詳しく学習するデベロッパー向けの追加リソースを示します。以下から、このコンテンツに関するフィードバック、コードへのリンク、役立つさまざまなドキュメントをご覧いただけます。

Codelab の問題/フィードバック

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

移行に関するリソース

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

Codelab

Python 2

Python 3

モジュール 20

コード

該当なし

モジュール 21(この Codelab)

コード

コード

オンライン リファレンス

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

Cloud Identity Platform と Cloud Marketplace

Cloud Resource Manager、Cloud IAM、Firebase Admin SDK

App Engine ユーザー、App Engine NDB、Cloud NDB、Cloud Datastore

移行モジュールに関するその他のリファレンス

App Engine の移行

App Engine プラットフォーム

Cloud SDK

Cloud のその他の情報

動画

ライセンス

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