Flask アプリで App Engine タスクキュー(push タスク)を使用する方法(モジュール 7)

1. 概要

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

この Codelab では、モジュール 1 CodelabサンプルアプリApp Engine タスクキューの push タスクを使用する方法について説明します。モジュール 7 のブログ投稿と動画では、このチュートリアルを補完し、このチュートリアルの内容の概要を説明します。

このモジュールでは、push タスクの使用法を追加し、モジュール 8 では Cloud Tasks で使用されていたタスクを、モジュール 9 で Python 3 と Cloud Datastore に移行します。pull タスクにタスクキューを使用している場合、Cloud Pub/Sub に移行してモジュール 18 ~ 19 を参照してください。

GCP コンソールの

  • App Engine Task Queue API/バンドル サービスを使用する
  • 基本的な Python 2 Flask App Engine NDB アプリに push タスクの使用を追加する

必要なもの

アンケート

このチュートリアルをどのように使用されますか?

通読のみ 通読して演習を行う

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

初心者 中級者 上級者

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

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

2. 背景情報

App Engine タスクキューは push タスクと pull タスクの両方をサポートしています。アプリケーションのポータビリティを向上させるために、Google Cloud チームは、タスクキューなどの以前のバンドル サービスから、他の Cloud スタンドアロン サービスまたは同等のサードパーティ サービスに移行することを推奨しています。

pull タスクの移行については移行モジュール 18 ~ 19 で説明し、モジュール 7 ~ 9 では push タスクの移行に焦点を当てます。App Engine タスクキューの push タスクから移行するには、モジュール 1 の Codelab で得られた既存の Flask と App Engine NDB アプリにその使用法を追加します。このアプリでは、新しいページビューによって新しい訪問が登録され、最新の訪問がユーザーに表示されます。過去の訪問が二度と表示されることはなく、Datastore の容量を使用するため、最も古い訪問を自動的に削除する push タスクを作成します。モジュール 8 では、そのアプリをタスクキューから Cloud Tasks に移行します。

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

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

3. 設定/事前作業

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

  1. Cloud プロジェクトを設定する
  2. ベースラインのサンプルアプリを取得する
  3. (再)ベースライン アプリをデプロイして検証する

この手順により、コードを確実に稼働させることができます。

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

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

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

この Codelab の前提条件の 1 つは、モジュール 1 の App Engine アプリを準備しておくことです。モジュール 1 の Codelab を完了するか(推奨)、リポジトリからモジュール 1 アプリをコピーします。皆さんのコードであれ私たちのものであれ、モジュール 1 のコードが「開始」となります。この Codelab では各ステップについて説明します。最後に、モジュール 7 のリポジトリ フォルダ「FINISH」にあるコードに似たコードを使用して締めくくります。

使用するモジュール 1 アプリに関係なく、フォルダは次のようになります。lib フォルダもある可能性があります。

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

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

次の手順でモジュール 1 アプリを(再)デプロイします。

  1. lib フォルダがある場合は削除し、pip install -t lib -r requirements.txt を実行して lib を再設定します。Python 2 と Python 3 の両方がインストールされている場合は、代わりに pip2 コマンドを使用する必要があります。
  2. gcloud コマンドライン ツールをインストールして初期化し、使用方法を確認したことを確認します。
  3. gcloud コマンドを発行するたびに PROJECT_ID を入力しない場合は、Cloud プロジェクトに gcloud config set project PROJECT_ID を設定します。
  4. gcloud app deploy を使用してサンプルアプリをデプロイする
  5. モジュール 1 のアプリが想定どおりに動作し、最近のアクセスの表示に問題がないことを確認します(以下の図を参照)。

a7a9d2b80d706a2b.png

4. 構成の更新

標準の App Engine 構成ファイル(app.yamlrequirements.txtappengine_config.py)を変更する必要はありません。

5. アプリケーション ファイルを変更する

メインのアプリケーション ファイルは main.py で、このセクションのすべての更新はこのファイルに関連します。また、ウェブ テンプレート templates/index.html もマイナー アップデートされました。このセクションで実装する変更は次のとおりです。

  1. インポートを更新する
  2. push タスクを追加
  3. タスクハンドラを追加
  4. ウェブ テンプレートの更新

1. インポートを更新する

google.appengine.api.taskqueue をインポートすると、タスクキューの機能が組み込まれます。いくつかの Python 標準ライブラリ パッケージも必要です。

  • 最も古い訪問を削除するタスクを追加するため、アプリはタイムスタンプを扱う必要があります。つまり、timedatetime を使用します。
  • タスクの実行に関する有用な情報をログに記録するには、logging が必要です。

これらのインポートをすべて追加すると、変更前と変更後のコードは次のようになります。

変更前:

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

変更後:

from datetime import datetime
import logging
import time
from flask import Flask, render_template, request
from google.appengine.api import taskqueue
from google.appengine.ext import ndb

2. push タスクを追加(タスクのデータを照合し、新しいタスクをキューに入れる)

push キューのドキュメントには、「タスクを処理するには、タスクを push キューに追加する必要があります。App Engine には、default という名前のデフォルトの push キューが用意されています。これはデフォルトの構成ですぐに使用できます。必要に応じて、すべてのタスクをこのデフォルト キューに追加するだけで済みます。他のキューを作成して構成する必要はありません。」この Codelab では、わかりやすくするために default キューを使用しています。同じ特性または異なる特性を持つ独自の push キューの定義について詳しくは、push キューの作成に関するドキュメントをご覧ください。

この Codelab の主な目的は、表示されなくなった古いアクセスを Datastore から削除するタスクを(default push キューに追加する)ことです。ベースライン アプリは、新しい Visit エンティティを作成して各訪問(/ への GET リクエスト)を登録し、最新の訪問を取得して表示します。最も古い訪問が再び表示または使用されることはないため、プッシュタスクでは、表示されている最も古い訪問履歴はすべて削除されます。そのためには、アプリの動作を少し変更する必要があります。

  1. 直近の訪問を照会するときは、その訪問をすぐに返すのではなく、最後に表示された最も古い Visit のタイムスタンプを保存するようにアプリを修正します。これより古い訪問はすべて削除しても安全です。
  2. このタイムスタンプをペイロードとして push タスクを作成し、タスクハンドラに送信します。タスクハンドラは、/trim に HTTP POST を介してアクセスできます。具体的には、標準の Python ユーティリティを使用して Datastore のタイムスタンプを変換して(浮動小数点数として)タスクに送信します。また、その文字列を(文字列として)ログに記録し、ユーザーに表示する標識値としてその文字列を返します。

これらはすべて fetch_visits() で行われ、更新の前後は次のようになります。

変更前:

def fetch_visits(limit):
    return (v.to_dict() for v in Visit.query().order(
            -Visit.timestamp).fetch(limit))

変更後:

def fetch_visits(limit):
    'get most recent visits and add task to delete older visits'
    data = Visit.query().order(-Visit.timestamp).fetch(limit)
    oldest = time.mktime(data[-1].timestamp.timetuple())
    oldest_str = time.ctime(oldest)
    logging.info('Delete entities older than %s' % oldest_str)
    taskqueue.add(url='/trim', params={'oldest': oldest})
    return (v.to_dict() for v in data), oldest_str

3. タスクハンドラを追加する(タスクの実行時に呼び出されるコード)

過去のアクセスの削除は fetch_visits() で簡単に行えますが、この機能はエンドユーザーとはあまり関係がないことを覚えておいてください。これは補助的な機能であり、標準のアプリ リクエストの外部で非同期的に処理することをおすすめします。Datastore に格納される情報量が少なくなるため、エンドユーザーはクエリを高速化できるというメリットを享受できます。タスクキューの POST リクエストを介して /trim に対して呼び出される新しい関数 trim() を作成します。これにより、次の処理が行われます。

  1. 「最も古い訪問」を抽出するタイムスタンプ ペイロード
  2. Datastore クエリを発行して、そのタイムスタンプより古いすべてのエンティティを検索します。
  3. より高速な「キーのみ」を選択している実際のユーザーデータが必要ないためです
  4. 削除するエンティティの数(ゼロを含む)を記録します。
  5. ndb.delete_multi() を呼び出してエンティティを削除します(それ以外の場合はスキップされます)。
  6. 空の文字列を(暗黙的な HTTP 200 戻りコードとともに)返します。

これらはすべて、以下の trim() で確認できます。fetch_visits() の直後の main.py に追加します。

@app.route('/trim', methods=['POST'])
def trim():
    '(push) task queue handler to delete oldest visits'
    oldest = request.form.get('oldest', type=float)
    keys = Visit.query(
            Visit.timestamp < datetime.fromtimestamp(oldest)
    ).fetch(keys_only=True)
    nkeys = len(keys)
    if nkeys:
        logging.info('Deleting %d entities: %s' % (
                nkeys, ', '.join(str(k.id()) for k in keys)))
        ndb.delete_multi(keys)
    else:
        logging.info('No entities older than: %s' % time.ctime(oldest))
    return ''   # need to return SOME string w/200

4. ウェブ テンプレートの更新

ウェブ テンプレート templates/index.html を更新し、変数が存在する場合は最も古いタイムスタンプを表示するという条件をこの Jinja2 で設定します。

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}

表示された訪問リストの後、本文を閉じる前に次のスニペットを追加します。テンプレートは次のようになります。

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

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

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}
</body>
</html>

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

このセクションでは、この Codelab の締めくくりとして、アプリをデプロイし、意図したとおりに動作することを確認し、反映された出力を確認します。アプリの検証後、クリーンアップを行い、次のステップを検討します。

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

gcloud app deploy でアプリをデプロイします。どの訪問が削除されるかを示す新しい行が下部に表示されることを除き、出力はモジュール 1 のアプリの場合と同じです。

4aa8a2cb5f527079.png

これでこの Codelab は完了です。コードが Module 7 Repo フォルダの内容と一致するはずです。これで、モジュール 8 の Cloud Tasks への移行の準備が整いました。

クリーンアップ

全般

現時点で完了したら、課金が発生しないように 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 に固有のものです。詳細については、各プロダクトのドキュメントをご覧ください。

次のステップ

この「移行」ではタスクキューの push キューの使用方法をモジュール 1 のサンプルアプリに追加し、訪問者の追跡のサポートを追加しました。その結果、モジュール 7 のサンプル アプリが作成されました。次の移行では、App Engine の push タスクから Cloud Tasks にアップグレードする方法について学習します。2021 年秋から、Python 3 にアップグレードする際に Cloud Tasks に移行する必要がなくなりました。詳しくは次のセクションで説明します。

Cloud Tasks に移行する場合は、次にモジュール 8 の Codelab に進んでください。これ以外にも、Cloud Datastore、Cloud Memorystore、Cloud Storage、Cloud Pub/Sub(pull キュー)などの移行を検討する必要があります。また、Cloud Run と Cloud Functions へのプロダクト間の移行もあります。Serverless Migration Station のすべてのコンテンツ(Codelab、動画、ソースコード(利用可能な場合))には、オープンソース リポジトリからアクセスできます。

7. Python 3 への移行

2021 年秋、App Engine チームは、バンドル サービスの多くのサポートを第 2 世代ランタイムに拡張しました(当初は第 1 世代ランタイムでのみ利用可能でした)。つまり、アプリを Python 3 に移植する際に、App Engine Task Queue などのバンドル サービスからスタンドアロンの Cloud または同等のサードパーティ サービス(Cloud Tasks など)に移行する必要がなくなりました。つまり、次世代ランタイムからバンドル サービスにアクセスするようにコードを改良すれば、Python 3 の App Engine アプリでタスクキューを引き続き使用できます。

バンドル サービスの使用を Python 3 に移行する方法については、モジュール 17 の Codelab と対応する動画をご覧ください。このトピックはモジュール 7 の対象外ですが、Python 3 に移植され、引き続き App Engine NDB とタスクキューを使用しているモジュール 1 と 7 の両方のアプリの Python 3 バージョンを以下に示します。

8. 参考情報

以下に、本モジュールや関連する移行モジュールと関連プロダクトについて詳しく学ぶデベロッパー向けの追加リソースを示します。これには、このコンテンツに関するフィードバックを提供する場所、コードへのリンク、役立つさまざまなドキュメントが含まれます。

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

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

移行に関するリソース

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

Codelab

Python 2

Python 3

モジュール 1

コード

code(このチュートリアルでは取り上げません)

モジュール 7(この Codelab)

コード

code(このチュートリアルでは取り上げません)

オンライン リソース

このチュートリアルに関連する可能性のあるオンライン リソースは次のとおりです。

App Engine のタスクキュー

App Engine プラットフォーム

Cloud のその他の情報

動画

ライセンス

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