基本的な「Google 翻訳」をデプロイするPython 2 App Engine 上のアプリ

1. 概要

この一連の Codelab(ご自分のペースで進められる実践型のチュートリアル)は、デベロッパーがアプリケーションのデプロイ時に利用できるさまざまなオプションを理解できるよう支援することを目的としています。この Codelab では、Python で Google Cloud Translation API を使用して、ローカルで実行するか、Cloud サーバーレス コンピューティング プラットフォーム(App Engine、Cloud Functions、Cloud Run)にデプロイする方法を学習します。このチュートリアルのリポジトリにあるサンプルアプリは、構成を少し変更するだけで(少なくとも)8 通りの方法でデプロイできます。

  1. ローカル Flask サーバー(Python 2)
  2. ローカル Flask サーバー(Python 3)
  3. App Engine(Python 2)
  4. App Engine(Python 3)
  5. Cloud Functions(Python 3)
  6. Cloud Run(Docker 経由の Python 2)
  7. Cloud Run(Docker 経由の Python 3)
  8. Cloud Run(Cloud Buildpack を介した Python 3)

この Codelab では、上記の太字のプラットフォームにこのアプリをデプロイすることに焦点を当てます。

方法を学ぶ対象

必要なもの

  • 有効な Cloud 請求先アカウントを持つ Google Cloud プロジェクト
  • ローカル実行用にインストールされた Flask、またはクラウドベースのデプロイ用に有効になっている Cloud サーバーレス コンピューティング プラットフォーム
  • 基本的な Python スキル
  • 基本的なオペレーティング システム コマンドに関する実践的な知識

アンケート

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

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

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

初心者 中級者 上級者

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

初心者 中級者 上級者

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列で、いつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud Console により一意の文字列が自動生成されます(通常は内容を意識する必要はありません)。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常、プロジェクト ID は「PROJECT_ID」の形式です)。好みの文字列でない場合は、別のランダムな ID を生成するか、独自の ID を試用して利用可能であるかどうかを確認することができます。プロジェクトの作成後、ID は「フリーズ」されます。
  • 3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud Console で課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルを終了した後に課金が発生しないようにリソースをシャットダウンするには、Codelab の最後にある「クリーンアップ」の手順を行います。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

3. Translation API を有効にする

サンプルアプリでは、後述の同様の手順で Cloud Translation APIApp Engine サービスを有効にします。

Cloud APIs の有効化

はじめに

アプリケーションで使用する Google API にかかわらず、API は有効にする必要があります。次の例は、Cloud Vision API を有効にする 2 つの方法を示しています。1 つの Cloud API を有効にする方法を学んだら、他の API も有効にできます。手順は同じです。

オプション 1: Cloud Shell またはコマンドライン インターフェースから

Cloud コンソールから API を有効にするのが一般的ですが、コマンドラインからすべてを行うことを好むデベロッパーもいます。これを行うには、API の「サービス名」を検索する必要があります。URL のように見えます(SERVICE_NAME.googleapis.com)。サポートされているプロダクトについては、サポートされているプロダクトの表をご覧ください。また、Google Discovery API を使用してプログラムでクエリすることもできます。

この情報を確認したら、Cloud Shell(または gcloud コマンドライン ツールがインストールされているローカル開発環境)を使用して、次のように API を有効にします。

gcloud services enable SERVICE_NAME.googleapis.com

たとえば、次のコマンドは Cloud Vision API を有効にします。

gcloud services enable vision.googleapis.com

このコマンドは、App Engine を有効にします。

gcloud services enable appengine.googleapis.com

1 つのリクエストで複数の API を有効にすることもできます。たとえば、次のコマンドラインでは、Cloud Run、Cloud Artifact Registry、Cloud Translation API が有効になります。

gcloud services enable artifactregistry.googleapis.com run.googleapis.com translate.googleapis.com

オプション 2: Cloud コンソールから

API Manager で Vision API を有効にすることもできます。Cloud コンソールから API Manager に移動し、[ライブラリ] を選択します。

fb0f1d315f122d4a.png

Cloud Vision API を有効にするには、検索バーに「vision」と入力します。入力した内容に一致する項目がすべて表示されます。

2275786a24f8f204.png

有効にする API を選択し、[有効にする] をクリックします。

2556f923b628e31.png

費用

多くの Google API は無料でご利用いただけますが、Google Cloud のプロダクトと API の使用は無料ではありません。Cloud APIs を有効にすると、有効な請求先アカウントの入力を求められる場合があります。ただし、一部の Google Cloud プロダクトには、超過した場合のみに料金が発生する Always Free 枠(1 日または 1 か月)が用意されています。この枠を超えないと、クレジット カード(または指定されたお支払い方法)に請求は発生しません。

有効にする前に、API の料金情報を確認してください。特に、無料の利用枠があるかどうか、ある場合はその内容を確認してください。Cloud Vision API を有効にする場合は、料金情報ページを確認します。Cloud Vision には無料の割り当てがあります。使用量の合計が上限(月ごと)を超えない限り、料金は発生しません。

料金と無料の階層は Google API によって異なります。例:

Google プロダクトごとに課金方法が異なるため、API ドキュメントで該当する情報を確認してください

概要

Google API の一般的な有効化方法を理解できたところで、API マネージャーに移動し、Cloud Translation API と App Engine サービスの両方を有効にします(まだ有効になっていない場合)。前者はアプリケーションで使用するため、後者は App Engine アプリをデプロイするためです。コマンドラインから行う場合は、代わりに次のコマンドを実行します。

gcloud services enable appengine.googleapis.com translate.googleapis.com

月間割り当ては [Always Free] の概要ページには記載されていませんが、Translation API の料金ページには、すべてのユーザーが毎月一定量の翻訳文字を取得できると記載されています。このしきい値を下回っている場合、API からの請求は発生しません。Google Cloud に関連するその他の料金がある場合は、最後に「クリーンアップ」セクションで説明します。

4. サンプルアプリのコードを取得する

ローカルまたは Cloud Shell(git clone コマンドを使用)でリポジトリのコード クローンを作成するか、次のスクリーンショットに示すように、緑色の [Code] ボタンから ZIP ファイルをダウンロードします。

5cd6110c4414cf65.png

準備が整ったので、このチュートリアルで使用するフォルダの完全なコピーを作成します。このチュートリアルでは、ファイルの削除や変更が必要になる可能性があります。別のデプロイを行う場合は、元のものをコピーして最初からやり直すことができます。これにより、クローンを作成したりダウンロードしたりする必要がなくなります。

5. サンプルアプリの概要

このサンプルアプリは、ユーザーに英語のテキストを入力して、そのテキストのスペイン語の翻訳を受け取るよう求める、シンプルな Google 翻訳 派生アプリです。main.py ファイルを開いて、仕組みを確認しましょう。ライセンスに関するコメント付きの行を除くと、上部と下部は次のようになります。

from flask import Flask, render_template, request
import google.auth
from google.cloud import translate

app = Flask(__name__)
_, PROJECT_ID = google.auth.default()
TRANSLATE = translate.TranslationServiceClient()
PARENT = 'projects/{}'.format(PROJECT_ID)
SOURCE, TARGET = ('en', 'English'), ('es', 'Spanish')

# . . . [translate() function definition] . . .

if __name__ == '__main__':
    import os
    app.run(debug=True, threaded=True, host='0.0.0.0',
            port=int(os.environ.get('PORT', 8080)))
  1. インポートにより、Flask 機能、google.auth モジュール、Cloud Translation API クライアント ライブラリが導入されます。
  2. グローバル変数は、Flask アプリ、Cloud プロジェクト ID、Translation API クライアント、Translation API 呼び出しの親「ロケーションパス」、ソース言語とターゲット言語を表します。この場合は英語(en)とスペイン語(es)ですが、これらの値は Cloud Translation API でサポートされている他の言語コードに変更できます。
  3. 下部にある大きな if ブロックは、このアプリをローカルで実行するチュートリアルで使用されます。このブロックは、Flask 開発用サーバーを使用してアプリを提供します。このセクションは、ウェブサーバーがコンテナにバンドルされていない場合に備えて、Cloud Run のデプロイのチュートリアルでも使用されます。コンテナ内でサーバーのバンドルを有効にするよう求められますが、この手順を忘れた場合、アプリコードは Flask 開発環境サーバーを使用するようにフォールバックします。(App Engine や Cloud Functions では問題になりません。これらのプラットフォームはソースベースのプラットフォームであるため、Google Cloud がデフォルトのウェブサーバーを用意して実行します)。

最後に、main.py の中央に、アプリケーションの核となる translate() 関数があります。

@app.route('/', methods=['GET', 'POST'])
def translate(gcf_request=None):
    """
    main handler - show form and possibly previous translation
    """

    # Flask Request object passed in for Cloud Functions
    # (use gcf_request for GCF but flask.request otherwise)
    local_request = gcf_request if gcf_request else request

    # reset all variables (GET)
    text = translated = None

    # if there is data to process (POST)
    if local_request.method == 'POST':
        text = local_request.form['text']
        data = {
            'contents': [text],
            'parent': PARENT,
            'target_language_code': TARGET[0],
        }
        # handle older call for backwards-compatibility
        try:
            rsp = TRANSLATE.translate_text(request=data)
        except TypeError:
            rsp = TRANSLATE.translate_text(**data)
        translated = rsp.translations[0].translated_text

    # create context & render template
    context = {
        'orig':  {'text': text, 'lc': SOURCE},
        'trans': {'text': translated, 'lc': TARGET},
    }
    return render_template('index.html', **context)

メインの関数は、ユーザー入力を受け取り、Translation API を呼び出して処理を行います。詳しく見ていきましょう。

  1. local_request 変数を使用して、リクエストが Cloud Functions から送信されているかどうかを確認します。Cloud Functions は独自の Flask リクエスト オブジェクトを送信しますが、他のすべての場合(ローカルで実行する場合、または App Engine または Cloud Run にデプロイする場合)は、Flask からリクエスト オブジェクトを直接取得します。
  2. フォームの基本変数をリセットします。これは主に GET リクエストを対象としています。POST リクエストには、これらの値に代わるデータが含まれます。
  3. POST の場合は、翻訳するテキストを取得し、API メタデータ要件を表す JSON 構造を作成します。次に、API を呼び出し、ユーザーが古いライブラリを使用している場合は、以前のバージョンの API にフォールバックします。
  4. いずれの場合も、実際の結果(POST)またはデータなし(GET)をテンプレート コンテキストにフォーマットしてレンダリングします。

アプリケーションのビジュアル部分は、テンプレート index.html ファイルにあります。以前に翻訳された結果(空白の場合)が表示され、その後に翻訳対象のフォームが表示されます。

<!doctype html>
<html><head><title>My Google Translate 1990s</title><body>
<h2>My Google Translate (1990s edition)</h2>

{% if trans['text'] %}
    <h4>Previous translation</h4>
    <li><b>Original</b>:   {{ orig['text'] }}  (<i>{{ orig['lc'][0] }}</i>)</li>
    <li><b>Translated</b>: {{ trans['text'] }} (<i>{{ trans['lc'][0] }}</i>)</li>
{% endif %}

<h4>Enter <i>{{ orig['lc'][1] }}</i> text to translate to <i>{{ trans['lc'][1] }}</i>:</h4>
<form method="POST"><input name="text"><input type="submit"></form>
</body></html>

6. ローカル パッケージ/依存関係をインストールする(lib に)

前述のように、サンプルアプリは Flask マイクロウェブ フレームワークと Python 用 Google Cloud Translation API クライアント ライブラリを使用します。pip とこの 2 つのパッケージをインストールして更新するには、次の pip(または pip3)コマンドを使用します。

pip install -t lib -r requirements.txt

上記のコマンドを実行すると、次のようなインストール出力が表示されます。

$ pip install -t lib -r requirements.txt
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Collecting flask>=1.1.2
  Using cached Flask-1.1.4-py2.py3-none-any.whl (94 kB)
Collecting google-cloud-translate>=2.0.1
  Using cached google_cloud_translate-2.0.2-py2.py3-none-any.whl (91 kB)
Collecting click<8.0,>=5.1
  Using cached click-7.1.2-py2.py3-none-any.whl (82 kB)
Collecting Jinja2<3.0,>=2.10.1
  Using cached Jinja2-2.11.3-py2.py3-none-any.whl (125 kB)
Collecting Werkzeug<2.0,>=0.15
  Using cached Werkzeug-1.0.1-py2.py3-none-any.whl (298 kB)
Collecting itsdangerous<2.0,>=0.24
  Using cached itsdangerous-1.1.0-py2.py3-none-any.whl (16 kB)
Collecting google-api-core[grpc]<2.0.0dev,>=1.15.0
  Downloading google_api_core-1.29.0-py2.py3-none-any.whl (93 kB)
     |████████████████████████████████| 93 kB 2.1 MB/s
Collecting google-cloud-core<2.0dev,>=1.1.0
  Using cached google_cloud_core-1.6.0-py2.py3-none-any.whl (28 kB)
Collecting MarkupSafe>=0.23
  Using cached MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl (17 kB)
Collecting protobuf>=3.12.0
  Downloading protobuf-3.17.2-cp27-cp27m-macosx_10_9_x86_64.whl (958 kB)
     |████████████████████████████████| 958 kB 21.6 MB/s
Collecting futures>=3.2.0; python_version < "3.2"
  Using cached futures-3.3.0-py2-none-any.whl (16 kB)
Collecting six>=1.13.0
  Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting packaging>=14.3
  Using cached packaging-20.9-py2.py3-none-any.whl (40 kB)
Collecting googleapis-common-protos<2.0dev,>=1.6.0
  Using cached googleapis_common_protos-1.52.0-py2.py3-none-any.whl (100 kB)
Collecting requests<3.0.0dev,>=2.18.0
  Using cached requests-2.25.1-py2.py3-none-any.whl (61 kB)
Collecting google-auth<2.0dev,>=1.25.0
  Using cached google_auth-1.30.1-py2.py3-none-any.whl (146 kB)
Collecting pytz
  Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting setuptools>=40.3.0
  Using cached setuptools-44.1.1-py2.py3-none-any.whl (583 kB)
Collecting grpcio<2.0dev,>=1.29.0; extra == "grpc"
  Using cached grpcio-1.38.0-cp27-cp27m-macosx_10_10_x86_64.whl (3.8 MB)
Collecting pyparsing>=2.0.2
  Using cached pyparsing-2.4.7-py2.py3-none-any.whl (67 kB)
Collecting chardet<5,>=3.0.2
  Using cached chardet-4.0.0-py2.py3-none-any.whl (178 kB)
Collecting urllib3<1.27,>=1.21.1
  Using cached urllib3-1.26.5-py2.py3-none-any.whl (138 kB)
Collecting idna<3,>=2.5
  Using cached idna-2.10-py2.py3-none-any.whl (58 kB)
Collecting certifi>=2017.4.17
  Downloading certifi-2021.5.30-py2.py3-none-any.whl (145 kB)
     |████████████████████████████████| 145 kB 61.1 MB/s
Collecting pyasn1-modules>=0.2.1
  Using cached pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB)
Collecting rsa<4.6; python_version < "3.6"
  Using cached rsa-4.5-py2.py3-none-any.whl (36 kB)
Collecting cachetools<5.0,>=2.0.0
  Using cached cachetools-3.1.1-py2.py3-none-any.whl (11 kB)
Collecting enum34>=1.0.4; python_version < "3.4"
  Using cached enum34-1.1.10-py2-none-any.whl (11 kB)
Collecting pyasn1<0.5.0,>=0.4.6
  Using cached pyasn1-0.4.8-py2.py3-none-any.whl (77 kB)
Installing collected packages: click, MarkupSafe, Jinja2, Werkzeug, itsdangerous, flask, six, protobuf, futures, pyparsing, packaging, googleapis-common-protos, chardet, urllib3, idna, certifi, requests, pyasn1, pyasn1-modules, rsa, cachetools, setuptools, google-auth, pytz, enum34, grpcio, google-api-core, google-cloud-core, google-cloud-translate
ERROR: pip's legacy dependency resolver does not consider dependency conflicts when selecting packages. This behaviour is the source of the following dependency conflicts.
matplotlib 1.3.1 requires nose, which is not installed.
matplotlib 1.3.1 requires tornado, which is not installed.
Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 Werkzeug-1.0.1 cachetools-3.1.1 certifi-2021.5.30 chardet-4.0.0 click-7.1.2 enum34-1.1.10 flask-1.1.4 futures-3.3.0 google-api-core-1.29.0 google-auth-1.30.1 google-cloud-core-1.6.0 google-cloud-translate-2.0.2 googleapis-common-protos-1.52.0 grpcio-1.38.0 idna-2.10 itsdangerous-1.1.0 packaging-20.9 protobuf-3.17.2 pyasn1-0.4.8 pyasn1-modules-0.2.8 pyparsing-2.4.7 pytz-2021.1 requests-2.25.1 rsa-4.5 setuptools-44.1.1 six-1.16.0 urllib3-1.26.5

7. サービスをデプロイします。

翻訳サービスを Python 2 App Engine にデプロイするには、次のコマンドを実行します。

gcloud app deploy

出力は次のようになります。また、次のステップに関するプロンプトも表示されます。

$ gcloud app deploy
Services to deploy:

descriptor:      [/private/tmp/nebulous-serverless-python/app.yaml]
source:          [/private/tmp/nebulous-serverless-python]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20210422t161025]
target url:      [https://PROJECT_ID.appspot.com]


Do you want to continue (Y/n)?

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 1290 files 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.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

これで、アプリは世界中で利用可能になりました。デプロイの出力に表示された URL(プロジェクト ID を含む)でアプリにアクセスできるはずです。

da28f951c33a2c3d.png

何か翻訳して、その仕組みをご確認ください。

d911984d15dd5ef9.png

8. おわりに

これで、Cloud Translation API を有効にして必要な認証情報を取得し、シンプルなウェブアプリを Python 2 App Engine にデプロイする方法を学びました。このデプロイの詳細については、リポジトリのこの表をご覧ください。

クリーンアップ

Cloud Translation API では、一定数の翻訳文字を毎月無料で実行できます。App Engine には無料の割り当てもあります。これは、Cloud FunctionsCloud Run にも当てはまります。いずれかの上限を超えると、料金が発生します。次の Codelab に進む場合は、アプリをシャットダウンする必要はありません。

次のチュートリアルに進む準備がまだ完了していない場合や、デプロイしたばかりのアプリがインターネットで検出されることについて懸念がある場合は、App Engine アプリを無効にするCloud Functions を削除するCloud Run サービスを無効にすることで、課金されないようにしてください。次の Codelab に進む準備ができた時点で、再度有効にできます。ただし、このアプリケーションや他の Codelab を続行せず、すべてを完全に削除する場合は、プロジェクトをシャットダウンしてください。

また、Google Cloud サーバーレス コンピューティング プラットフォームにデプロイすると、ビルドとストレージの費用が少額かかります。Cloud Build には、Cloud Storage と同様に独自の無料の割り当て枠があります。透明性を高めるために、Cloud Build はアプリケーション イメージをビルドし、Cloud Container Registry またはその後継である Artifact Registry に保存します。そのイメージのストレージは、その割り当ての一部を消費します。また、そのイメージをサービスに転送する際の下り(外向き)ネットワークも消費します。ただし、お住まいの地域でこのような無料枠が提供されていない場合があります。ストレージの使用量に注意して、発生する費用を最小限に抑えてください。

9. 参考情報

以降のセクションでは、このチュートリアルで学んだ知識を補完するための追加の参考資料と推奨される演習について説明します。

追加の調査

Translation API の使用方法を理解できたので、さらにスキルを磨くために、いくつかの演習を行いましょう。学習パスを続行するには、サンプルアプリを変更して次の操作を行います。

  1. ローカルで実行するか、Google Cloud サーバーレス コンピューティング プラットフォームにデプロイするには、この Codelab の他のエディションをすべて完了してください(repo README をご覧ください)。
  2. 別のプログラミング言語を使用して、このチュートリアルを完了します。
  3. 別の原文の言語または訳文の言語をサポートするように、このアプリを変更します。
  4. このアプリケーションをアップグレードして、テキストを複数の言語に翻訳できるようにします。テンプレート ファイルを変更して、サポートされているターゲット言語のプルダウンを表示します。

詳細

Google Apps Engine

Google Cloud Functions

Google Cloud Run

Google Cloud Buildpacks、Container Registry、Artifact Registry

Google Cloud Translation と Google ML Kit

その他の Google Cloud プロダクト/ページ

Python と Flask

ライセンス

このチュートリアルはクリエイティブ コモンズ表示 2.0 汎用ライセンスに基づいて使用許諾されていますが、リポジトリ内のソースコードは Apache 2 に基づいて使用許諾されています。