Python での HTTP Cloud Functions

1. はじめに

b158ce75c3cccd6d.png

Python は、データ サイエンティスト、ウェブ アプリケーション デベロッパー、システム管理者などが使用する一般的なオープンソース プログラミング言語です。

Cloud Functions は、イベント ドリブンのサーバーレス コンピューティング プラットフォームです。Cloud Functions を使用すると、リソースのプロビジョニングや、変化する要件に対応するためのスケーリングを気にすることなく、コードを記述できます。

Cloud Functions の関数には次の 2 つのタイプがあります。

  • HTTP 関数は HTTP リクエストに応答します。この Codelab ではいくつかの機能を作成します。
  • バックグラウンド関数は、Cloud Pub/Sub へのメッセージの公開や Cloud Storage へのファイルのアップロードなどのイベントによってトリガーされます。このラボでは説明しませんが、詳細についてはドキュメントをご覧ください。

efb3268e3b74ed4f.png

この Codelab では、Python で独自の Cloud Functions の関数を作成する方法について説明します。

作成するアプリの概要

この Codelab では Cloud Functions の関数を公開します。この関数を HTTP 経由で呼び出すと、関数 ロゴ:

a7aaf656b78050fd.png

学習内容

  • Cloud Functions の HTTP 関数を作成する方法。
  • 引数を受け取る HTTP Cloud Functions の関数を記述する方法。
  • HTTP Cloud Functions の関数をテストする方法。
  • ローカルの Python HTTP サーバーを実行して関数を試す方法。
  • 画像を返す Cloud Functions の HTTP 関数を記述する方法。

2. 設定と要件

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

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

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

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

Cloud Shell の起動

Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では Cloud 上で動作するコマンドライン環境である Cloud Shell を使用します。

Cloud Shell をアクティブにする

  1. Cloud Console で、[Cloud Shell をアクティブにする] 853e55310c205094.png をクリックします。

3c1dabeca90e44e5.png

Cloud Shell を初めて起動する場合は、内容を説明する中間画面が表示されます。中間画面が表示されたら、[続行] をクリックします。

9c92662c6a846a5c.png

Cloud Shell のプロビジョニングと接続に少し時間がかかる程度です。

9f0e51b578fecce5.png

この仮想マシンには、必要なすべての開発ツールが読み込まれます。5 GB の永続的なホーム ディレクトリが用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。この Codelab での作業のほとんどはブラウザを使って行うことができます。

Cloud Shell に接続すると、認証が完了し、プロジェクトに各自のプロジェクト ID が設定されていることがわかります。

  1. Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list

コマンド出力

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

上記のようになっていない場合は、次のコマンドで設定できます。

gcloud config set project <PROJECT_ID>

コマンド出力

Updated property [core/project].

Cloud Functions と Cloud Build API が有効になっていることを確認する

Cloud Shell から次のコマンドを実行して、Cloud Functions と Cloud Build API が有効になっていることを確認します。

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

注: gcloud functions deploy コマンドによって Cloud Build が呼び出され、コードが自動的にコンテナ イメージにビルドされます。

ソースコードをダウンロードする

Cloud Shell ターミナルから、次のコマンドを実行します。

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

ソース ディレクトリの内容を確認します。

ls

次のファイルが保存されています。

main.py  python-powered.png  test_main.py  web_app.py

3. HTTP Cloud Functions の概要

Python の HTTP Cloud Functions は、通常の Python 関数として記述します。この関数は、単一の flask.Request 引数(通常は request という名前)を受け入れる必要があります。

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

このファイルは、お好みのコマンドライン エディタ(nano、vim、emacs)で開くことができます。ソース ディレクトリをワークスペースとして設定した後、Cloud Shell エディタで開くこともできます。

cloudshell open-workspace .

gcloud functions deploy コマンドを使用して、この関数を HTTP Cloud Functions の関数としてデプロイします。

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

コマンド出力:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

gcloud functions deploy オプションに関する注意事項:

  • --runtime: 言語ランタイムを指定します。Python の場合、現時点では python37python38python39python310python312 のいずれかになります。ランタイムをご覧ください。
  • --trigger-http: 関数にエンドポイントが割り当てられます。エンドポイントへの HTTP リクエスト(POST、PUT、GET、DELETE、OPTIONS)は、関数の実行をトリガーします。
  • --allow-unauthenticated: 関数は公開され、認証を確認せずにすべての呼び出し元を許可します。
  • 詳細については、gcloud functions deploy をご覧ください。

関数をテストするには、上記のコマンド出力に表示されている httpsTrigger.url URL をクリックします。次のコマンドを使用して、プログラムで URL を取得し、関数を呼び出すこともできます。

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

次の結果が表示されます。

Hello World! 👋

4. 引数を受け取る HTTP Cloud Functions の関数を記述する

関数は、引数を受け入れると汎用性が高くなります。name パラメータをサポートする新しい関数 hello_name を定義しましょう。

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

この新しい関数をデプロイします。

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

コマンド出力:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

関数をテストするには、上記のコマンド出力に表示されている httpsTrigger.url URL をクリックします。次のコマンドを使用して、プログラムで URL を取得し、関数を呼び出すこともできます。

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

デフォルトの結果が表示されます。

Hello World! 🚀

name 引数が設定されていないため、デフォルトの結果が表示されます。URL にパラメータを追加します。

curl -w "\n" $URL?name=YOUR%20NAME

今回は、次のようなカスタム レスポンスが返されます。

Hello YOUR NAME! 🚀

次のステップでは、単体テストを追加して、ソースコードが更新されたときに関数が意図したとおりに動作し続けることを確認します。

5. テストの作成

Python の HTTP Cloud Functions は、標準ライブラリの unittest モジュールを使用してテストされます。関数をテストするためにエミュレータやその他のシミュレーションを実行する必要はありません。通常の Python コードだけです。

hello_world 関数と hello_name 関数のテストは次のようになります。

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Python テストは、他の Python ファイルと同じ方法で記述します。一連のインポートから始めて、クラスと関数を定義します。
  2. テスト宣言の形式は class TestHello(TestCase) です。unittest.TestCase から継承したクラスにする必要があります。
  3. テストクラスにはメソッドがあり、各メソッドは個々のテストケースを表す test_ で始める必要があります。
  4. 各テストケースでは、request パラメータをモックして(つまり、テストに必要な特定のデータを持つ架空のオブジェクトに置き換えて)いずれかの関数をテストします。
  5. 各関数が呼び出された後、テストでは HTTP レスポンスが期待どおりであるかを確認します。

main.pyflask に依存しているため、テスト環境に Flask フレームワークがインストールされていることを確認します。

pip install flask

Flask をインストールすると、次のような結果が出力されます。

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

以下のテストをローカルで実行します。

python -m unittest

3 つの単体テストに合格する必要があります。

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

次に、「Python Powered」のレスポンスを返す新しい関数を作成します。ロゴ。

6. 「Python Powered」のCloud Functions の HTTP 関数

「Python Powered」の構文を返すことによって、新しい関数をより面白くしましょう。構成します。

a7aaf656b78050fd.png

これを実現するためのコードを以下に示します。

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

新しい python_powered 関数をデプロイします。

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

コマンド出力:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

関数をテストするには、上記のコマンド出力に表示されている httpsTrigger.url URL をクリックします。すべてが正常に機能する場合は、「Python Powered」のメッセージが表示されます。新しいブラウザタブに表示されます。

次に、デプロイ前に関数をローカルで実行して試せるようにアプリを作成します。

7. 関数をローカルで実行する

HTTP 関数をローカルで実行するには、ウェブアプリを作成し、ルートで関数を呼び出します。関数と同じディレクトリに追加できます。web_app.py という名前のファイルの内容は次のとおりです。

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. このファイルにより Flask アプリケーションが作成されます。
  2. ベース URL にルートを登録します。これは、index() という名前の関数で処理されます。
  3. 次に、index() 関数は python_powered 関数を呼び出して、現在のリクエストを渡します。

開発環境に Flask フレームワークがインストールされていることを確認します。

pip install flask

Flask をインストールすると、次のような結果が出力されます。

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

このアプリケーションをローカルで実行するには、次のコマンドを実行します。

python web_app.py

次に、Cloud Shell ウェブ プレビューを使用して、ブラウザでウェブアプリをテストします。Cloud Shell で [ウェブでプレビュー] ボタンを[ポート 8080 でプレビュー] を選択します。

6c9ff9e5c692c58e.gif

プロキシ サービスのプレビュー用 URL が新しいブラウザ ウィンドウで開きます。ウェブ プレビューでは、HTTPS 経由でのアクセスはユーザーのアカウントのみに制限されます。すべてが正常に機能していれば、「Python Powered」のメッセージが表示されます。ロゴ!

8e5c3ead11cfd103.png

8. 完了

b158ce75c3cccd6d.png

Flask フレームワークでウェブ リクエストを処理する慣用的な関数を使用して、HTTP Cloud Functions をデプロイしました。

Cloud Functions の料金は、関数の呼び出し頻度に基づきます。これには、頻繁に実行されない関数の無料枠も含まれます。Cloud Functions の関数のテストが完了したら、gcloud を使用して削除できます。

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Google Cloud コンソールから関数を削除することもできます。

Python で Cloud Functions をぜひご活用ください。