1. 概要
Serverless Migration Station の Codelab シリーズ(セルフペース型のハンズオン チュートリアル)と関連動画は、Google Cloud サーバーレス デベロッパーが主にレガシー サービスからの移行を 1 つ以上の移行を通じてガイドし、アプリをモダナイズできるようにすることを目的としています。これにより、アプリのポータビリティが高まり、選択肢と柔軟性が増し、より広範な Cloud プロダクトと統合してアクセスできるようになり、より新しい言語リリースに簡単にアップグレードできるようになります。当初は初期の Cloud ユーザー、主に App Engine(スタンダード環境)のデベロッパーを対象としていますが、Cloud Functions や Cloud Run など、その他のサーバーレス プラットフォーム(該当する場合)まで幅広くカバーしています。
この Codelab では、Python 2 の App Engine デベロッパーに対し、App Engine タスクキューの pull タスクから Cloud Pub/Sub に移行する方法を紹介します。また、Datastore アクセスのための App Engine NDB から Cloud NDB への暗黙的な移行(主にモジュール 2 で説明)や、Python 3 へのアップグレードもあります。
モジュール 18 では、アプリに pull タスクを追加する方法について説明します。このモジュールでは、モジュール 18 で完成したアプリを Cloud Pub/Sub に移行します。push タスクにタスクキューを使用している場合、Cloud Tasks に移行する代わりに、モジュール 7 ~ 9 を参照してください。
GCP コンソールの
- App Engine タスクキュー(pull タスク)の代わりに Cloud Pub/Sub を使用します。
- App Engine NDB の使用を Cloud NDB に置き換えます(モジュール 2 も参照してください)。
- アプリを Python 3 に移植する
必要なもの
- 有効な GCP 請求先アカウントを持つ Google Cloud Platform プロジェクト
- 基本的な Python スキル
- 一般的な Linux コマンドに関する実践的な知識
- App Engine アプリの開発とデプロイに関する基本的な知識
- 機能するモジュール 18 の App Engine サンプルアプリ
アンケート
このチュートリアルの利用方法をお選びください。
<ph type="x-smartling-placeholder">Python のご利用経験はどの程度ありますか?
Google Cloud サービスの使用経験はどの程度ありますか?
<ph type="x-smartling-placeholder">2. 背景情報
App Engine タスクキューは、push タスクと pull タスクの両方をサポートしています。Google Cloud では、アプリケーションのポータビリティを向上させるために、タスクキューなどの以前のバンドル サービスから、他の Cloud スタンドアロン サービスまたは同等のサードパーティ サービスに移行することをおすすめします。
- タスクキューの push タスクのユーザーは Cloud Tasks に移行する必要があります。
- タスクキューの pull タスクのユーザーは、Cloud Pub/Sub に移行する必要があります。
移行モジュール 7 ~ 9 では push タスクの移行について説明し、モジュール 18 ~ 19 では pull タスクの移行に焦点を当てています。Cloud Tasks はタスクキューの push タスクに厳密には一致しますが、Pub/Sub はタスクキューの pull タスクに類似していません。
Pub/Sub には、タスクキューが提供する pull 機能よりも多くの機能があります。たとえば、Pub/Sub には push 機能もありますが、Cloud Tasks はタスクキューの push タスクに近いため、Pub/Sub push はどの移行モジュールでも適用されません。モジュール 19 の Codelab では、キューイング メカニズムをタスクキューの pull キューから Pub/Sub に切り替え、Datastore アクセスのために App Engine NDB から Cloud NDB に移行し、モジュール 2 の移行を繰り返します。
モジュール 18 のコードはPython 2 サンプルアプリとして、ソース自体は Python 2 と Python 3 と互換性があります。モジュール 19 で Cloud Pub/Sub(および Cloud NDB)に移行した後も、ソース自体は Python 2 と Python 3 と互換性があります。
このチュートリアルでは、次の手順について説明します。
- セットアップ / 事前作業
- 構成の更新
- アプリケーション コードを変更する
3. 設定/事前作業
このセクションでは、次の方法を説明します。
- Cloud プロジェクトを設定する
- ベースラインのサンプルアプリを取得する
- (再)ベースライン アプリをデプロイして検証する
- 新しい Google Cloud サービス/API を有効にする
この手順により、コードを実際に使ってみて、クラウド サービスに移行する準備が整っていることを確認できます。
1. プロジェクトのセットアップ
モジュール 18 の Codelab を完了している場合は、同じプロジェクト(およびコード)を再利用してください。あるいは、新しいプロジェクトを作成するか、別の既存のプロジェクトを再利用します。プロジェクトに有効な請求先アカウントがあり、App Engine アプリが有効になっていることを確認します。この Codelab で必要になるため、プロジェクト ID を検索します。PROJECT_ID
変数が見つかるたびに、プロジェクト ID を使用します。
2. ベースラインのサンプルアプリを取得する
前提条件の一つは、モジュール 18 の App Engine アプリを動作させることです。そのため、Codelab を完了するか(推奨、上記のリンク)、リポジトリからモジュール 18 のコードをコピーします。お客様のものか弊社によるものかを問わず、ここから始まります(「開始」)。この Codelab では移行手順について説明します。最後に、モジュール 19 のリポジトリ フォルダ(「FINISH」)にあるコードと同様のコードを使用して終了します。
- 開始: モジュール 18 フォルダ(Python 2)
- FINISH: モジュール 19 フォルダ(Python 2 および 3)
- リポジトリ全体(ZIP ファイルのクローンを作成するかダウンロードするため)
使用するモジュール 18 のアプリに関係なく、フォルダは次のようになります。lib
フォルダも含まれている可能性があります。
$ ls README.md appengine_config.py queue.yaml templates app.yaml main.py requirements.txt
3. (再)ベースライン アプリをデプロイして検証する
次の手順で Module 18 アプリをデプロイします。
lib
フォルダがある場合は削除し、pip install -t lib -r requirements.txt
を実行してlib
を再設定します。開発マシンに Python 2 と Python 3 の両方がインストールされている場合は、代わりにpip2
を使用する必要があります。gcloud
コマンドライン ツールをインストールして初期化し、その使用方法を確認していることを確認します。- (省略可)発行する
gcloud
コマンドごとにPROJECT_ID
を入力しない場合は、Cloud プロジェクトにgcloud config set project
PROJECT_ID
を設定します。 gcloud app deploy
を使用してサンプルアプリをデプロイする- アプリが問題なく動作することを確認します。モジュール 18 の Codelab を完了すると、上位の訪問者と最近の訪問(下図)がアプリに表示されます。表示されていない場合は、訪問者数が表示されていない可能性があります。
モジュール 18 のサンプルアプリを移行する前に、まず、変更したアプリで使用するクラウド サービスを有効にする必要があります。
4. 新しい Google Cloud サービス/API の有効化
古いアプリでは App Engine バンドル サービスが使用されていましたが、追加のセットアップは必要ないのに対し、スタンドアロンの Cloud サービスでは必要となっています。アップデート後のアプリでは、Cloud Pub/Sub と Cloud Datastore(Cloud NDB クライアント ライブラリ経由)の両方を使用します。App Engine と Cloud APIs の両方とも「無料枠」 割り当てられており、上限内であれば、このチュートリアルを完了しても料金は発生しません。Cloud APIs は、必要に応じて Cloud コンソールまたはコマンドラインから有効にできます。
Cloud Console から
Cloud コンソールで API Manager の [ライブラリ] ページ(正しいプロジェクト)に移動し、ページ中央の検索バーを使って Cloud Datastore API と Cloud Pub/Sub API を検索します。
API ごとに [有効にする] ボタンをクリックします。お支払い情報の入力を求められる場合があります。たとえば、これは Cloud Pub/Sub API ライブラリのページです。
コマンドラインから
コンソールから API を有効にする方法は視覚的にわかりやすくなりますが、コマンドラインを使う方がよい場合もあります。gcloud services enable pubsub.googleapis.com datastore.googleapis.com
コマンドを発行して、両方の API を同時に有効にします。
$ gcloud services enable pubsub.googleapis.com datastore.googleapis.com Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.
お支払い情報の入力を求められる場合があります。他の Cloud APIs を有効にし、その URI を確認したい場合は、各 API のライブラリ ページの下部で確認できます。たとえば、「サービス名」として pubsub.googleapis.com
を確認します。最上部に配置できます
手順を完了すると、プロジェクトで API にアクセスできるようになります。次に、これらの API を使用するようにアプリケーションを更新します。
4. Pub/Sub リソースを作成する
モジュール 18 のタスクキュー ワークフローの順序をまとめます。
- モジュール 18 では、
queue.yaml
ファイルを使用してpullq
という名前の pull キューを作成しました。 - アプリは、訪問者を追跡するためにタスクを pull キューに追加します。
- タスクは最終的にワーカーによって処理され、有限時間(1 時間)リースされます。
- 最近の訪問者数を集計するためにタスクが実行されます。
- 完了すると、タスクはキューから削除されます。
Pub/Sub を使用して同様のワークフローを複製します。次のセクションでは、Pub/Sub の基本的な用語を紹介し、必要な Pub/Sub リソースを作成する 3 つの方法を紹介します。
App Engine タスクキュー(pull)と Cloud Pub/Sub の用語
Pub/Sub に切り替える場合は、語彙を少し調整する必要があります。以下に、両サービスの主要カテゴリと関連する用語を示します。また、同様の比較が記載されている移行ガイドもご覧ください。
- キューイングのデータ構造: タスクキューでは、データは pull キューに入れられます。Pub/Sub では、データはトピックに入ります。
- キューに格納されたデータの単位: タスクキューでの pull タスクは、Pub/Sub ではメッセージと呼ばれます。
- データ処理者: タスクキューでは、ワーカーが pull タスクにアクセスします。Pub/Sub でメッセージを受信するにはサブスクリプション/サブスクライバーが必要
- データの抽出: pull タスクをリースすることは、トピックから(サブスクリプションを介して)メッセージを pull することと同じです。
- クリーンアップ/完了: 完了時にタスクキューのタスクを pull キューから削除することは、Pub/Sub メッセージの確認応答に似ています。
キューに入れるプロダクトは異なりますが、ワークフローは比較的似ています。
- pull キューではなく、アプリは
pullq
という名前のトピックを使用します。 - タスクを pull キューに追加するのではなく、アプリはトピック(
pullq
)にメッセージを送信します。 - ワーカーが pull キューからタスクをリースするのではなく、
worker
という名前のサブスクライバーがpullq
トピックからメッセージを pull します。 - このアプリはメッセージ ペイロードを処理し、Datastore で訪問者数をインクリメントします。
- アプリは pull キューからタスクを削除せずに、処理されたメッセージに確認応答します。
タスクキューを使用する場合、pull キューを作成します。Pub/Sub を使用して設定するには、トピックとサブスクリプションの両方を作成する必要があります。モジュール 18 では、アプリ実行以外で queue.yaml
を処理しました。Pub/Sub でも同じことをする必要があります。
トピックとサブスクリプションを作成するには、次の 3 つの方法があります。
- Cloud コンソール
- コマンドライン
- コードから(短い Python スクリプト)
以下のいずれかのオプションを選択し、対応する手順に沿って Pub/Sub リソースを作成します。
Cloud コンソール
Cloud コンソールからトピックを作成する手順は次のとおりです。
- Cloud コンソールの Pub/Sub の [トピック] ページに移動します。
- 上部にある [トピックを作成] をクリックします。新しいダイアログ ウィンドウが開きます(下の画像を参照)。
- [トピック ID] フィールドに「
pullq
」と入力します。 - オンになっているすべてのオプションの選択を解除し、[Google-managed encryption key] を選択します。
- [トピックを作成] ボタンをクリックします。
トピック作成ダイアログは次のようになります。
トピックを作成したので、そのトピックのサブスクリプションを作成する必要があります。
- Cloud コンソールの Pub/Sub の [サブスクリプション] ページに移動します。
- 上部にある [Create subscription] をクリックします(下の画像を参照)。
- [Subscription ID] フィールドに「
worker
」と入力します。 - [Cloud Pub/Sub トピックを選択してください] プルダウンから
pullq
を選択し、「完全修飾パス名」に注目してください。例:projects/PROJECT_ID/topics/pullq
- [配信タイプ] で [pull] を選択します。
- その他のオプションはすべてそのままにして、[作成] ボタンをクリックします。
サブスクリプションの作成画面は次のようになります。
[トピック] ページからサブスクリプションを作成することもできます。この「ショートカット」トピックをサブスクリプションに関連付けるのに便利です。サブスクリプションの作成について詳しくは、こちらのドキュメントをご覧ください。
コマンドラインから
Pub/Sub のユーザーは、gcloud pubsub topics create
TOPIC_ID
コマンドと gcloud pubsub subscriptions create
SUBSCRIPTION_ID
--topic=
TOPIC_ID
コマンドを使用して、トピックとサブスクリプションを作成できます。pullq
の TOPIC_ID
と worker
の SUBSCRIPTION_ID
でこれらを実行すると、プロジェクト PROJECT_ID
の出力は次のようになります。
$ gcloud pubsub topics create pullq Created topic [projects/PROJECT_ID/topics/pullq]. $ gcloud pubsub subscriptions create worker --topic=pullq Created subscription [projects/PROJECT_ID/subscriptions/worker].
クイックスタート ドキュメントのこちらのページもご覧ください。コマンドラインを使用すると、トピックやサブスクリプションが定期的に作成されるワークフローが簡素化され、このようなコマンドをシェル スクリプトでこの目的で使用できます。
コードから(短い Python スクリプト)
トピックとサブスクリプションの作成を自動化するもう一つの方法は、ソースコードで Pub/Sub API を使用することです。以下は、モジュール 19 リポジトリ フォルダにある maker.py
スクリプトのコードです。
from __future__ import print_function
import google.auth
from google.api_core import exceptions
from google.cloud import pubsub
_, PROJECT_ID = google.auth.default()
TOPIC = 'pullq'
SBSCR = 'worker'
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)
def make_top():
try:
top = ppc_client.create_topic(name=TOP_PATH)
print('Created topic %r (%s)' % (TOPIC, top.name))
except exceptions.AlreadyExists:
print('Topic %r already exists at %r' % (TOPIC, TOP_PATH))
def make_sub():
try:
sub = psc_client.create_subscription(name=SUB_PATH, topic=TOP_PATH)
print('Subscription created %r (%s)' % (SBSCR, sub.name))
except exceptions.AlreadyExists:
print('Subscription %r already exists at %r' % (SBSCR, SUB_PATH))
try:
psc_client.close()
except AttributeError: # special Py2 handler for grpcio<1.12.0
pass
make_top()
make_sub()
このスクリプトを実行すると、次のような出力が表示されます(エラーがない場合)。
$ python3 maker.py Created topic 'pullq' (projects/PROJECT_ID/topics/pullq) Subscription created 'worker' (projects/PROJECT_ID/subscriptions/worker)
API を呼び出して既存のリソースを作成すると、クライアント ライブラリから google.api_core.exceptions.AlreadyExists
例外がスローされ、スクリプトによって適切に処理されます。
$ python3 maker.py Topic 'pullq' already exists at 'projects/PROJECT_ID/topics/pullq' Subscription 'worker' already exists at 'projects/PROJECT_ID/subscriptions/worker'
Pub/Sub を初めて使用する場合は、Pub/Sub アーキテクチャに関するホワイト ペーパーで詳細をご覧ください。
5. 構成の更新
構成の更新には、さまざまな構成ファイルを変更することや、Cloud Pub/Sub エコシステム内で App Engine の pull キューに相当するものを作成することが含まれます。
queue.yaml を削除する
タスクキューから完全に移行します。Pub/Sub はこのファイルを使用しないため、queue.yaml
を削除します。pull キューを作成する代わりに、Pub/Sub トピック(およびサブスクリプション)を作成します。
requirements.txt
google-cloud-ndb
と google-cloud-pubsub
の両方を requirements.txt
に追加して、モジュール 18 の flask
を結合します。更新したモジュール 19 requirements.txt
は次のようになります。
flask
google-cloud-ndb
google-cloud-pubsub
この requirements.txt
ファイルにはバージョン番号がありません。つまり、最新バージョンが選択されます。互換性の問題がある場合は、バージョン番号を使用してアプリの動作バージョンをロックインする標準的な方法に従います。
app.yaml
app.yaml
への変更は、Python 2 を引き続き使用するか、Python 3 にアップグレードするかによって異なります。
Python 2
上記の requirements.txt
の更新により、Google Cloud クライアント ライブラリの使用が追加されます。これらのライブラリを使用するには、App Engine からの追加サポート、つまり組み込みライブラリの setuptools
と grpcio
が必要です。組み込みライブラリを使用するには、app.yaml
の libraries
セクションとライブラリのバージョン番号または「latest」が必要をご覧ください。モジュール 18 app.yaml
には、まだこれらのセクションのいずれもありません。
変更前:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries
セクションを app.yaml
に追加して、setuptools
と grpcio
の両方のエントリとともに最新バージョンを選択します。また、Python 3 のプレースホルダの runtime
エントリを追加します。これは、この記事の執筆時点で現在の 3.x リリース(3.10 など)とともにコメントアウトされています。この変更により、app.yaml
は次のようになります。
変更後:
#runtime: python310
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: setuptools
version: latest
- name: grpcio
version: latest
Python 3
Python 3 ユーザーと app.yaml
にとって、これはすべてを削除することを意味します。このセクションでは、handlers
セクション、threadsafe
および api_version
ディレクティブを削除します。libraries
セクションは作成しません。
第 2 世代のランタイムは組み込みサードパーティ ライブラリを提供していないため、app.yaml
に libraries
セクションは必要ありません。さらに、組み込みされていないサードパーティ パッケージをコピー(ベンダリングや自己バンドルと呼ばれることもあります)する必要はなくなりました。アプリで使用するサードパーティ ライブラリを requirements.txt
にリストするだけで済みます。
app.yaml
の handlers
セクションは、アプリケーション(script)と静的ファイル ハンドラを指定するためのものです。Python 3 ランタイムでは、ウェブ フレームワークが独自のルーティングを行う必要があるため、すべてのスクリプト ハンドラを auto
に変更する必要があります。アプリ(モジュール 18 のような)が静的ファイルを提供しない場合、すべてのルートが auto
になり、ルートは無関係になります。そのため、handlers
セクションも不要なので、削除します。
最後に、Python 3 では threadsafe
ディレクティブも api_version
ディレクティブも使用されていないため、それらも削除します。結論として、runtime
ディレクティブのみが残るように app.yaml
のすべてのセクションを削除し、Python 3 の最新バージョン(3.10 など)を指定します。更新前と更新後の app.yaml
の外観は次のとおりです。
変更前:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
変更後:
runtime: python310
Python 3 用の app.yaml
からすべてを削除する準備ができていない方のために、モジュール 19 のリポジトリ フォルダに app3.yaml
の代替ファイルを用意しています。これをデプロイに使用する場合は、コマンドの末尾に gcloud app deploy app3.yaml
というファイル名を追加します(そうしないと、デフォルトで Python 2 の app.yaml
ファイルを変更せずにデプロイします)。
appengine_config.py
Python 3 にアップグレードする場合、appengine_config.py
は不要なため、削除します。サードパーティ ライブラリのサポートでは、requirements.txt
で指定するだけでよいからです。Python 2 ユーザー向けです。
モジュール 18 appengine_config.py
には、サードパーティ ライブラリをサポートする適切なコードが含まれています。たとえば、Flask や、先ほど requirements.txt
に追加された Cloud クライアント ライブラリなどです。
変更前:
from google.appengine.ext import vendor
# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
ただし、このコードだけでは、追加した組み込みライブラリ(setuptools
、grpcio
)をサポートするには不十分です。さらに数行が必要になるため、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 クライアント ライブラリのサポートに必要な変更について詳しくは、バンドル サービスの移行に関するドキュメントをご覧ください。
その他の構成の更新
lib
フォルダがある場合は削除します。Python 2 を使用している場合は、次のコマンドを実行して lib
フォルダを補充します。
pip install -t lib -r requirements.txt # or pip2
開発システムに Python 2 と Python 3 の両方がインストールされている場合は、pip
ではなく pip2
を使用する必要があります。
6. アプリケーション コードを変更する
このセクションでは、App Engine タスクキューの pull キューの使用が Cloud Pub/Sub に置き換えられた、メイン アプリケーション ファイル main.py
の更新について説明します。ウェブ テンプレート templates/index.html
に変更はありません。どちらのアプリも同じように動作し、同じデータを表示する必要があります。
インポートと初期化を更新する
インポートと初期化が更新されています。
- インポートについては、App Engine NDB とタスクキューを Cloud NDB と Pub/Sub に置き換えます。
pullq
の名前をQUEUE
からTOPIC
の名前に変更しました。- pull タスクではワーカーが 1 時間リースしたのに対し、Pub/Sub ではタイムアウトがメッセージ単位で測定されるため、
HOUR
定数は削除します。 - Cloud APIs では API クライアントを使用する必要があるため、Cloud NDB と Cloud Pub/Sub で API クライアントを開始します。後者はトピックとサブスクリプションの両方のクライアントを提供します。
- Pub/Sub には Cloud プロジェクト ID が必要なため、
google.auth.default()
からインポートして取得します。 - Pub/Sub には「完全修飾パス名」が必要そのため、
*_path()
コンビニエンス関数を使用して作成してください。
以下にモジュール 18 のインポートと初期化を示します。その後に、上記の変更を実装した後のセクションの内容を示します。新しいコードのほとんどは、さまざまな Pub/Sub リソースです。
変更前:
from flask import Flask, render_template, request
from google.appengine.api import taskqueue
from google.appengine.ext import ndb
HOUR = 3600
LIMIT = 10
TASKS = 1000
QNAME = 'pullq'
QUEUE = taskqueue.Queue(QNAME)
app = Flask(__name__)
変更後:
from flask import Flask, render_template, request
import google.auth
from google.cloud import ndb, pubsub
LIMIT = 10
TASKS = 1000
TOPIC = 'pullq'
SBSCR = 'worker'
app = Flask(__name__)
ds_client = ndb.Client()
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
_, PROJECT_ID = google.auth.default()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)
データモデルの更新内容にアクセスする
Visit
データモデルは変更されません。Datastore にアクセスするには、Cloud NDB API クライアント コンテキスト マネージャー ds_client.context()
を明示的に使用する必要があります。つまり、コードでは、Datastore の呼び出しを Python with
ブロック内の store_visit()
と fetch_visits()
の両方でラップすることになります。この更新はモジュール 2 で取り上げる内容と同じです。
Pub/Sub にとって最も重要な変更は、タスクキューの pull タスクのキュー登録を、pullq
トピックへの Pub/Sub メッセージのパブリッシュに置き換えることです。上記の更新の前後のコードを次に示します。
変更前:
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 in Datastore and queue request to bump visitor count'
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
QUEUE.add(taskqueue.Task(payload=remote_addr, method='PULL'))
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 in Datastore and queue request to bump visitor count'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
ppc_client.publish(TOP_PATH, remote_addr.encode('utf-8'))
def fetch_visits(limit):
'get most recent visits'
with ds_client.context():
return Visit.query().order(-Visit.timestamp).fetch(limit)
VisitorCount データモデルの更新
VisitorCount
データモデルは変更されず、fetch_counts()
を実行します。ただし、Datastore クエリを with
ブロックでラップする点が異なります。
変更前:
class VisitorCount(ndb.Model):
visitor = ndb.StringProperty(repeated=False, required=True)
counter = ndb.IntegerProperty()
def fetch_counts(limit):
'get top visitors'
return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)
変更後:
class VisitorCount(ndb.Model):
visitor = ndb.StringProperty(repeated=False, required=True)
counter = ndb.IntegerProperty()
def fetch_counts(limit):
'get top visitors'
with ds_client.context():
return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)
ワーカーコードを更新する
NDB を Cloud NDB に、タスクキューを Pub/Sub に置き換える限り、ワーカーコードが更新されますが、ワークフローは変わりません。
- Cloud NDB コンテキスト マネージャーの
with
ブロックで Datastore 呼び出しをラップする。 - タスクキューのクリーンアップでは、pull キューからすべてのタスクを削除します。Pub/Sub では「確認応答 ID」が
acks
で収集され、最後に削除/確認応答されます。 - タスクキューの pull タスクは、Pub/Sub メッセージが pull されるのと同様の方法でリースされます。pull タスクの削除はタスク オブジェクト自体で行われますが、Pub/Sub メッセージは確認応答 ID を介して削除されます。
- Pub/Sub メッセージ ペイロードには(Python 文字列ではなく)バイトが必要なため、トピックにメッセージをパブリッシュし、トピックからメッセージを pull するときには、それぞれ UTF-8 のエンコードとデコードがあります。
log_visitors()
を、上述の変更を実装する以下の更新したコードに置き換えます。
変更前:
@app.route('/log')
def log_visitors():
'worker processes recent visitor counts and updates them in Datastore'
# tally recent visitor counts from queue then delete those tasks
tallies = {}
tasks = QUEUE.lease_tasks(HOUR, TASKS)
for task in tasks:
visitor = task.payload
tallies[visitor] = tallies.get(visitor, 0) + 1
if tasks:
QUEUE.delete_tasks(tasks)
# increment those counts in Datastore and return
for visitor in tallies:
counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
if not counter:
counter = VisitorCount(visitor=visitor, counter=0)
counter.put()
counter.counter += tallies[visitor]
counter.put()
return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
len(tasks), len(tallies))
変更後:
@app.route('/log')
def log_visitors():
'worker processes recent visitor counts and updates them in Datastore'
# tally recent visitor counts from queue then delete those tasks
tallies = {}
acks = set()
rsp = psc_client.pull(subscription=SUB_PATH, max_messages=TASKS)
msgs = rsp.received_messages
for rcvd_msg in msgs:
acks.add(rcvd_msg.ack_id)
visitor = rcvd_msg.message.data.decode('utf-8')
tallies[visitor] = tallies.get(visitor, 0) + 1
if acks:
psc_client.acknowledge(subscription=SUB_PATH, ack_ids=acks)
try:
psc_client.close()
except AttributeError: # special handler for grpcio<1.12.0
pass
# increment those counts in Datastore and return
if tallies:
with ds_client.context():
for visitor in tallies:
counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
if not counter:
counter = VisitorCount(visitor=visitor, counter=0)
counter.put()
counter.counter += tallies[visitor]
counter.put()
return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
len(msgs), len(tallies))
メインのアプリケーション ハンドラ root()
に変更はありません。HTML テンプレート ファイル templates/index.html
も変更する必要がないため、必要な更新はすべてラップされます。Cloud Pub/Sub を使用した新しいモジュール 19 アプリケーションへようこそ。
7. 概要/クリーンアップ
アプリをデプロイして、意図したとおりに動作し、出力に反映されていることを確認します。また、ワーカーを実行して訪問者数を処理します。アプリの検証後、クリーンアップ手順を実施し、次のステップを検討します。
アプリケーションのデプロイと検証
pullq
トピックと worker
サブスクリプションがすでに作成されていることを確認します。それが完了し、サンプルアプリを使用する準備ができたら、gcloud app deploy
でアプリをデプロイします。基になるキューイング メカニズム全体を正常に置き換えた点を除き、出力はモジュール 18 のアプリと同じになるはずです。
アプリのウェブ フロントエンドが、アプリケーションのこの部分が機能することを確認します。アプリのこの部分は、上位の訪問者と最近の訪問を正常にクエリして表示しますが、この訪問者を合計数に追加する pull タスクを作成しながら、アプリ側でこの訪問を登録し、そのタスクは、処理を待機中のキューに追加されます。
これは、App Engine バックエンド サービス、cron
ジョブ、/log
の閲覧、コマンドラインの HTTP リクエストの発行で実行できます。curl
でワーカーコードを呼び出している実行例を次に示します(実際の PROJECT_ID
に置き換えます)。
$ curl https://PROJECT_ID.appspot.com/log DONE (with 1 task[s] logging 1 visitor[s])
更新されたカウントは、次回のウェブサイト訪問時に反映されます。これで作業は完了です。
クリーンアップ
全般
現時点で完了したら、課金が発生しないように 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_ID
と *LOC
*に依存します(例: 「us
」)アプリが米国でホストされている場合は、
一方、このアプリケーションや他の関連する移行 Codelab を続行せず、すべてを完全に削除したい場合は、プロジェクトをシャットダウンします。
この Codelab のみ
以下に示すサービスは、この Codelab に固有のものです。詳細については、各プロダクトのドキュメントをご覧ください。
- Cloud Pub/Sub のさまざまなコンポーネントには無料枠があります。全体的な使用量を判断することで、費用への影響を把握できます。詳しくは、料金ページをご覧ください。
- App Engine Datastore サービスは Cloud Datastore(Datastore モードの Cloud Firestore)で提供しており、これにも無料枠があります。詳しくは料金ページをご覧ください。
次のステップ
このチュートリアル以外にも、以前のバンドル サービスからの移行に重点を置いた他の移行モジュールを以下に示します。
- モジュール 2: App Engine
ndb
から Cloud NDB に移行する - モジュール 7 ~ 9: App Engine タスクキュー(push タスク)から Cloud Tasks への移行
- モジュール 12 ~ 13: App Engine Memcache から Cloud Memorystore への移行
- モジュール 15 ~ 16: App Engine Blobstore から Cloud Storage への移行
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 に問題が見つかった場合は、提出する前にまず問題を検索してください。新しい問題の検索と登録を行うためのリンク:
移行に関するリソース
以下の表に、モジュール 18(START)とモジュール 19(FINISH)のリポジトリ フォルダへのリンクを示します。
Codelab | Python 2 | Python 3 |
該当なし | ||
モジュール 19(この Codelab) | (上記のように app.yaml を更新した場合を除き、app3.yaml を使用する以外は Python 2 と同じです) |
オンライン リファレンス
このチュートリアルに関連するリソースは次のとおりです。
App Engine のタスクキュー
- App Engine タスクキューの概要
- App Engine タスクキューの pull キューの概要
- App Engine タスクキューの pull キューの完全なサンプルアプリ
- タスクキューの pull キューの作成
- Google I/O 2011 の pull キューのリリース動画(Votelator サンプルアプリ)
queue.yaml
リファレンスqueue.yaml
と Cloud Tasks の比較- pull キューから Pub/Sub への移行ガイド
Cloud Pub/Sub
- Cloud Pub/Sub のプロダクト ページ
- Pub/Sub クライアント ライブラリの使用
- Pub/Sub Python クライアント ライブラリのサンプル
- Pub/Sub Python クライアント ライブラリのドキュメント
- 作成してPub/Sub トピックの管理
- Pub/Sub トピックの命名ガイドライン
- 作成してPub/Sub サブスクリプションの管理
- App Engine(フレキシブル)サンプルアプリ(Standard にもデプロイ可能、Python 3)
- 上記のサンプルアプリのリポジトリ
- Pub/Sub pull サブスクリプション
- Pub/Sub push サブスクリプション
- App Engine Pub/Sub push サンプルアプリ(Python 3)
- App Engine Pub/Sub push サンプルアプリ リポジトリ
- Pub/Sub の料金情報
- Cloud Tasks か Cloud Pub/Sub か(push または pull)
App Engine NDB と Cloud NDB(Datastore)
- App Engine NDB ドキュメント
- App Engine NDB リポジトリ
- Google Cloud NDB のドキュメント
- Google Cloud NDB リポジトリ
- Cloud Datastore の料金情報
App Engine プラットフォーム
- App Engine ドキュメント
- Python 2 App Engine(スタンダード環境)ランタイム
- App Engine の Python 2 組み込みライブラリで Python 2 を使用する
- Python 3 App Engine(スタンダード環境)ランタイム
- Python 2 と3 つの App Engine(スタンダード環境)ランタイム
- Python 2 から 3 への App Engine(スタンダード環境)の移行ガイド
- App Engine の料金と割り当てに関する情報
- 第 2 世代の App Engine プラットフォームのリリース(2018 年)
- 1 番目と 2 番目の比較をプラットフォームに
- レガシー ランタイムの長期サポート
- ドキュメントの移行サンプル
- コミュニティ提供の移行サンプル
Cloud のその他の情報
- Google Cloud Platform での Python
- Google Cloud Python クライアント ライブラリ
- Google Cloud「Always Free」ティア
- Google Cloud SDK(
gcloud
コマンドライン ツール) - Google Cloud に関するすべてのドキュメント
動画
- サーバーレス移行ステーション
- Serverless Expeditions(サーバーレス探索)
- Google Cloud Tech に登録
- Google Developers に登録します。
ライセンス
この作業はクリエイティブ・コモンズの表示 2.0 汎用ライセンスにより使用許諾されています。