モジュール 1: App Engine webapp2 から Flask に移行する

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

この最初のチュートリアルでは、App Engine アプリでウェブ フレームワークをモダナイズするための最初の移行手順(webapp2 から Flask への移行)を示します。アプリでは、ルーティングを処理するウェブ フレームワークを自由に使用できますが、このチュートリアルでは、コミュニティで広く使われている Flask を使用します。

方法を学ぶ対象

  • サードパーティ ライブラリの使用(組み込みまたはそれ以外)
  • 設定ファイルを更新する
  • シンプルなアプリの webapp2 から Flask への移行

必要なもの

アンケート

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

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

webapp フレームワークは、2008 年に App Engine が初めてPython 2.5 上でリリースされたときバンドルされました。数年後の 2013 年、2.7 ランタイムによって 2.5 のサポートが終了した ときに後継の webapp2に置き換えられました。

webapp2ドキュメントを参照)は引き続き存在し、WSGI 準拠のウェブ フレームワークとして App Engine の外部で使用できますが、ユーザー リクエストをアプリケーションの適切なコードに独自に転送することは行いません。代わりに、App Engine、構成ファイル、デベロッパーを利用して、ウェブ トラフィックを対応する「ハンドラ」に転送します。さらに、webapp2 の主なメリットは、App Engine のバンドル サービスに密接に関係しており、Python 3 で機能するとしても実際には非推奨です(関連する問題もご覧ください)。

このモジュールにより、App Engine と Google Cloud 以外の多くのサービスでサポートされているフレームワークの Flask にシンプルな webapp2 アプリを移行する実戦経験が利用者に与えられ、アプリの移植性がさらに向上します。Flask が、アプリケーションの移行先として望ましいフレームワークでない場合は、独自の転送を行う限り別のフレームワークを選択できます。この Codelab では、情報技術の意思決定者(ITDM)とデベロッパーに対して移行手順を説明するため、実際に移行する先のフレームワークに関係なく、このプロセスを理解できます。

この移行の主な手順は次のとおりです。

  1. セットアップ / 事前作業
  2. Flask サードパーティ ライブラリの追加
  3. アプリケーション ファイルの更新
  4. HTML テンプレート ファイルの更新

チュートリアルの主要部分に進む前に、まずプロジェクトをセットアップし、コードを取得します。つづいて、gcloud コマンドについて(再度)理解を深め、ベースライン アプリをデプロイします。そうすることで、作業コードを使って作業を開始できます。

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

現役のデベロッパーの方の場合、おそらく App Engine ダッシュボードには、運用しているサービスがすでに表示されていると思われます。このチュートリアルでは、新しいプロジェクトを作成するか、このチュートリアルのために既存のプロジェクトを再利用することをおすすめします。プロジェクトにアクティブな請求先アカウントがあり、App Engine(アプリ)が有効になっていることを確認します。

2. ベースライン サンプルアプリのダウンロード

GAE 移行リポジトリには、必要なコードがすべて用意されています。そのクローンを作成するか、ZIP ファイルをダウンロードします。このチュートリアルは、モジュール 0 フォルダ(START)のコードから始め、チュートリアルが完了すると、コードはモジュール 1 フォルダ(FINISH)のものと一致することになります。そうならない場合は、違いを確認してから次のラボに進んでください。

モジュール 0 フォルダには、POSIX の ls コマンドで示されるように、次のファイルがあります。

$ ls
app.yaml        index.html      main.py

3. gcloud コマンドの(再)理解

マシンに gcloud コマンドがまだない場合は、Google Cloud SDK をインストールして、gcloud が実行パスの一部として使用可能なことと、次の gcloud コマンドの使用方法を確認します。

  1. gcloud components update - Google Cloud SDK の更新
  2. gcloud auth login - 認証されたアカウントへのログイン
  3. gcloud config list - GCP プロジェクト構成設定のリスト出力
  4. gcloud config set project PROJECT_ID - GCP プロジェクト ID の設定
  5. gcloud app deploy - App Engine アプリケーションのデプロイ

最近 gcloud を使用して App Engine 開発を行っていない場合は、次の手順に進む前に、最初の 4 つのコマンド(#1~#4)を実行する必要があります。これらのコマンドについて簡単に説明します。

まず、gcloud components update は、Cloud SDK の最新バージョンを使用していることを確認します。このコマンドを実行すると、次のような出力が表示されます。

$ gcloud components update

Your current Cloud SDK version is: 317.0.0
You will be upgraded to version: 318.0.0

┌──────────────────────────────────────────────────┐
│        These components will be updated.         │
├──────────────────────────┬────────────┬──────────┤
│           Name           │  Version   │   Size   │
├──────────────────────────┼────────────┼──────────┤
│ Cloud SDK Core Libraries │ 2020.11.06 │ 15.5 MiB │
│ gcloud cli dependencies  │ 2020.11.06 │ 10.6 MiB │
└──────────────────────────┴────────────┴──────────┘

The following release notes are new in this upgrade.
Please read carefully for information about new features, breaking changes,
and bugs fixed.  The latest full release notes can be viewed at:
  https://cloud.google.com/sdk/release_notes

318.0.0 (2020-11-10)

      . . .
      (release notes)
      . . .

    Subscribe to these release notes at
    https://groups.google.com/forum/#!forum/google-cloud-sdk-announce.

Do you want to continue (Y/n)?

╔════════════════════════════════════════════════════════════╗
╠═ Creating update staging area                             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: Cloud SDK Core Libraries                   ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: gcloud cli dependencies                    ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud SDK Core Libraries                     ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: gcloud cli dependencies                      ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Creating backup and activating new installation          ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.

Update done!

To revert your SDK to the previously installed version, you may run:
  $ gcloud components update --version 317.0.0

次に、gcloud auth login を使用して、以降 gcloud コマンドを発行するための認証を行います。

$ gcloud auth login
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?response_type=code&client_id= . . .

You are now logged in as [YOUR_EMAIL].
Your current project is [PROJECT_ID].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID

gcloud config list を使用して、現在のプロジェクト設定を確認します。

$ gcloud config list
[core]
account = YOUR_EMAIL
disable_usage_reporting = False
project = PROJECT_ID

Your active configuration is: [default]

上記のコマンドにより、新しいプロジェクトを作成するか、既存のプロジェクトを選択するかを判断できます。gcloud config list の出力がこのチュートリアルで使用するプロジェクトと一致していない場合は、gcloud config set project PROJECT_ID を実行してプロジェクト ID を設定します。つづいて、gcloud config list を再度実行して、正しいプロジェクト ID が設定されていることを確認します。

$ gcloud config set project PROJECT_ID
Updated property [core/project].

代わりに Cloud Console を使用したいときは、ユーザー インターフェースを使用して新しいプロジェクトを作成することも、既存のプロジェクトを使用することもできます。プロジェクトのダッシュボードに、ID を示すプロジェクト情報カード(プロジェクト名、番号とともに)が表示されます。

プロジェクト情報カード

最後のコマンド(#5)の gcloud app deploy では、アプリを App Engine にデプロイします。まだ始めたばかりのため、すぐに実行する必要はありませんが、モジュール 0 コードをデプロイして動作を確認することは妨げません。実行時に、アプリを実行する地理的なリージョン(通常は現在地)を選択します。いったん設定すると変更できません。つづいて、残りのデプロイ情報を確認します。完了すると、アプリの URL が通知されます。簡略化した出力例を次に示します。

$ gcloud app deploy
Services to deploy:

descriptor:      [/private/tmp/mod0-baseline/app.yaml]
source:          [/private/tmp/mod0-baseline]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20201116t220827]
target url:      [https://PROJECT_ID.REG_ABBR.r.appspot.com]

Do you want to continue (Y/n)?

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 1 file to Google Cloud Storage                 ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT_ID.REG_ABBR.r.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

しばらく App Engine を使用していなかった場合は、元のデプロイの appcfg.py updateコマンドgcloud app deploy で置き換えられていることに気づくかもしれません。gcloud app deploy の詳細については、ドキュメント ページをご覧ください。

最近のもう 1 つの変更点は、デプロイ済みアプリの URL が、http://PROJECT_ID.appspot.com から http://PROJECT_ID.REG_ABBR.r.appspot.com に変わっていることです。ほとんどのアプリは、最終的に新しい形式に変換されます。URL 形式の詳細については、リクエストとルーティングのドキュメントをご覧ください。

アプリのデプロイ後、ブラウザの表示を何度か更新して最新のアクセスを確認します。

visitme アプリ

新規アプリの場合、訪問記録は、ほんの数件しか表示されません。

Python 2 App Engine ランタイムには、組み込み組み込みサードパーティ ライブラリ一式が備わっており、使用するために必要な作業は、app.yaml ファイルで指定することだけです。この移行ではそれを使用する必要はありませんが、次の移行チュートリアル(モジュール 2)では使用します。

組み込みでないサードパーティ ライブラリは、requirements.txt というファイルで指定し、App Engine にすべてがアップロードされるアプリケーション コードと同じディレクトリにある lib フォルダにローカルにインストールする必要があります。詳細については、サードパーティ ライブラリのバンドルに関するドキュメントをご覧ください。

Flask のようにコピーされたライブラリでは、appengine_config.py 構成ファイルを使用して lib フォルダ内の検索を App Engine に指示する必要があります。appengine_config.py 構成ファイルは、requirements.txtlib と同じ最上位のアプリケーション フォルダに配置されます。チュートリアルのこのパートでは、次のことを行います。

  • requirements.txt の作成(コピーされた [非組み込み] サードパーティ ライブラリを指定)
  • appengine_config.py の作成(サードパーティ ライブラリを認識)
  • (サードパーティ)パッケージと依存関係のインストール

1. requirements.txt を作成する

requirements.txt ファイルを作成してパッケージを指定します。この例では、Flask が必要とされる第三者ライブラリです。このドキュメントの作成時点では、最新バージョンは 1.1.2 です。requirements.txt は、次のように 1 行で作成します。

Flask==1.1.2

使用できる形式についての詳細は、requirements.txt のドキュメントをご覧ください。

2. appengine_config.py を作成する

次の手順では、外部サードパーティ ライブラリを App Engine に認識させます。次の内容のファイルを 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)

このコードは、前に指定したとおりの処理を行います。つまり、App Engine にコピーしたライブラリの lib フォルダを示します。

3. パッケージと依存関係をインストールする

ここでは、pip install コマンドを実行して lib フォルダを作成し、そこに Flask とその依存関係をインストールします。

$ pip install -t lib -r requirements.txt

pip または pip2 のどちらを使用しても、パッケージのインストールが完了すると、次のような内容の lib フォルダが作成されます。

$ ls lib
bin/
click/
click-7.1.2.dist-info/
flask/
Flask-1.1.2.dist-info/
itsdangerous/
itsdangerous-1.1.0.dist-info/
jinja2/
Jinja2-2.11.2.dist-info/
markupsafe/
MarkupSafe-1.1.1.dist-info/
werkzeug/
Werkzeug-1.0.1.dist-info/

次に、アプリケーション ファイル main.py を更新します。

1. インポート

インポートは、すべての Python ファイルと同様、最初に行います。webapp2 フレームワークのインポートの後に ndb Datastore ライブラリがあり、最後に Django フレーバーのテンプレートを処理する App Engine 拡張機能が続きます。次の結果が表示されます。

  • 移行前:
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

Flask に移行すると、Flask とテンプレート レンダラーの両方を同時にインポートします。webapp2 関連のインポートの 2 行を削除して、次のように置き換えます(ndb のインポートはそのまま残します)。

  • 移行後:
from flask import Flask, render_template, request
from google.appengine.ext import ndb

2. 起動

webapp2 を使用するアプリには、任意の Python ファイル内(他の場合もあります)のすべてのルートとハンドラを列挙する単一の配列(Python リスト)が必要です。

  • 移行前:
app = webapp2.WSGIApplication([
    ('/', MainHandler),
], debug=True)

app.yaml は上位レベルのルーティングを実行し、異なるハンドラを呼び出す場合があるので注意してください。サンプルアプリは非常に単純なもので、すべてのルートが main.py ハンドラに向かいます。

Flask ではこのようなルーティング テーブルを使用しないため、main.pyこれらの行は削除します。また、Flask にも初期化が必要なため、main.py の一番上のインポートの直後に 次の行を追加します。

  • 移行後:
app = Flask(__name__)

Flask でフレームワークを初期化し、デコレータを使用してルートを定義します。また、ルートは、クラスやメソッドではなく、関数とペアになります。

この Codelab に Flask のチュートリアルを含めることは対象外のため、別途 Flask のチュートリアルに目を通して、Flask のドキュメントを確認してください。

3. データモデル

ここでは変更はありません。Datastore は、次の Codelab で扱われます。

4. ハンドラ

アプリケーションは、使用するフレームワーク(webapp2 または Flask)に関係なく、次の 3 つのことを行います。

  1. ルートパス(/)GET リクエストを処理する
  2. ウェブページの「訪問」を登録する(Visit オブジェクトの作成 / 保存)
  3. 直近 10 件のアクセスを表示する(定義済みのテンプレートの index.html を使用)

webapp2 フレームワークでは、サポートされている HTTP メソッドごとにハンドラを作成するクラスベースの実行モデルが使用されます。ここでのシンプルなケースでは、GET だけのため、get() メソッドが定義されています。

  • 移行前:
class MainHandler(webapp2.RequestHandler):
    def get(self):
        store_visit(self.request.remote_addr, self.request.user_agent)
        visits = fetch_visits(10) or ()  # empty sequence if None
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(tmpl, {'visits': visits}))

前述のとおり、Flask では独自のルーティングを行います。ハンドラクラスではなく、関数を記述して、呼び出し先のルートで修飾します。ユーザーはデコレータ呼び出しで処理される HTTP メソッドを指定できます。たとえば、次のようにします。@app.route('/app/', methods=['GET', 'POST'])をクリックします。デフォルトは GET(暗黙的に HEAD)だけのため、そのままにしておくことができます。

Flask への移行では、MainHandler クラスとその get() メソッドは、次の Flask ルーティング関数に置き換えます。

  • 移行後:
@app.route('/')
def root():
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10) or ()  # empty sequence if None
    return render_template('index.html', visits=visits)

もちろん、これは、このサンプルよりも複雑なアプリを代表するものではありません。このチュートリアルの主な目的は、作業を開始し、ある程度まで「体で覚え」、App Engine 固有のコードの変更場所を理解することです。この変更が正しく行えたかどうかを確認するには、変更したモジュールをモジュール 1 の main.py と比較してみてください。

5. 補助ファイル

.gcloudignore ファイルの変更はありません。その目的は、App Engine にデプロイしないファイルを指定することです。それには、アプリケーションのデプロイや実行に必要とされない、補助 Python、ソース管理、リポジトリのボイラープレートなどがあります。.gcloudignore は、次のような形になります(簡潔にするため、コメントは削除しています)。

.gcloudignore
.git
.gitignore
.hgignore
.hg/
*.pyc
*.pyo
__pycache__/
/setup.cfg
README.md

1. テンプレート ファイルの移動

ベースライン リポジトリのフォルダ(モジュール 0)では、index.html テンプレート ファイルがアプリケーション ファイルと同じフォルダにあります。Flask では、templates フォルダに置かれた HTML ファイルを必要とするため、そのフォルダを作成(mkdir templates)して、そこに index.html を移動させる必要があります。Linux や Mac OS X などの POSIX 準拠のシステムでは、コマンドは次のようになります。

mkdir templates
mv index.html templates

2. テンプレート ファイルの更新

index.htmltemplates に移動したら、若干編集します。まず、元のテンプレート ファイル全体を次に示します。

<!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>

</body>
</html>

webapp2 は Django テンプレートを使用しており、括弧 ( ) なしで visit.timestamp.ctime のように呼び出せますが、Jinja2 では明示的に括弧が必要です。この調整は、小さいことのように思えるかもしれませんが、Jinja テンプレートは、呼び出しに引数を渡すことができるため、より強力です。

Django では、「テンプレート タグ」を作成するか、フィルタを記述する必要があります。これを理解したうえで、visit.timestamp.ctime の呼び出しにかっこのペアを追加して、index.html を更新します。

  • 移行前:
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
  • 移行後:
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>

これが唯一の必要な変更になります。残りの移行の Codelab では、index.html に追加の変更を加える必要はありません。

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

このチュートリアルでのすべての変更が完了したら、アプリケーション フォルダ内のファイルは、モジュール 1 リポジトリ フォルダ内のファイルと同じ(またはほぼ同じ)となる必要があります。ここでは、モジュール 1 Flask アプリケーションがモジュール 0 webapp2 バージョンと同じ動作をすることをデプロイして確認します。

元のモジュール 0 のコードをデプロイする前の手順で行ったように、gcloud app deploy コマンドを使用します。ウェブブラウザか、curl または wget コマンドで PROJECT_ID.appspot.com のアプリにアクセスして、アプリが期待どおりに動作することを確認します。

なんらかのサーバーエラーが発生した場合、通常は Python コードに入力ミスなどがあることを意味します。アプリケーション ログを調べて調査します。また、前記のモジュール 1 リポジトリにあるファイルと比較することもできます。

オプション: クリーンアップ

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

オプション: アプリを無効にする

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

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

次のステップ

完了したモジュール 1 のコードから始まる移行モジュールには、モジュール 2 とモジュール 7 があります。

  • モジュール 2(Datastore を使用する場合は必須)
    • App Engine ndb から Cloud NDB への移行
    • Cloud NDB に切り替えると、多くの選択肢が利用できます。
      • アプリをコンテナ化して Cloud Run で実行する
      • アプリをさらに Cloud Datastore クライアント ライブラリへ移行する
      • アプリを Cloud Firestore に移行して Firebase の機能にアクセスする
  • モジュール 7([push] タスクキューを使用する場合に必要)
    • App Engine の(push)taskqueue 使用量を追加する
    • モジュール 8 で Cloud Tasks へ移行するモジュール 1 アプリを準備する

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

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

移行に関するリソース

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

Codelab

Python 2

Python 3

モジュール 0

コード

該当なし

モジュール 1

コード

該当なし

App Engine リソース

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