Python 中的 HTTP Cloud Functions

1. 簡介

b158ce75c3cccd6d.png

Python 是熱門的開放原始碼程式設計語言,可供數據資料學家、網頁應用程式開發人員、系統管理員等人使用。

Cloud Functions 是事件導向的無伺服器運算平台。Cloud Functions 可讓您編寫程式碼,無須擔心佈建資源或調度資源來因應不斷變化的需求。

Cloud Functions 有兩種類型:

  • HTTP 函式會回應 HTTP 要求。您將在本程式碼研究室中建構幾個項目。
  • 背景函式是由事件觸發,例如發布至 Cloud Pub/Sub 的訊息或上傳至 Cloud Storage 的檔案。本研究室在這個研究室中不會探討這個問題,但您可以參閱說明文件來瞭解詳情。

efb3268e3b74ed4f.png

本程式碼研究室將逐步引導您使用 Python 建立自己的 Cloud Functions。

建構項目

在本程式碼研究室中,您將發布透過 HTTP 叫用的 Cloud 函式,該函式會顯示「Python Powered」標誌

a7aaf656b78050fd.png

課程內容

  • 如何編寫 HTTP Cloud 函式。
  • 如何編寫使用引數的 HTTP Cloud 函式。
  • 如何測試 HTTP Cloud 函式。
  • 如何執行本機 Python HTTP 伺服器以嘗試此函式。
  • 如何編寫會傳回圖片的 HTTP Cloud 函式。

2. 設定和需求

自修環境設定

  1. 登入 Google Cloud 控制台,建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • 「專案名稱」是這項專案參與者的顯示名稱。這是 Google API 未使用的字元字串。您可以隨時更新付款方式。
  • 所有 Google Cloud 專案的專案 ID 均不得重複,而且設定後即無法變更。Cloud 控制台會自動產生一個不重複的字串。但通常是在乎它何在在大部分的程式碼研究室中,您必須參照專案 ID (通常為 PROJECT_ID)。如果您對產生的 ID 不滿意,可以隨機產生一個 ID。或者,您也可以自行嘗試,看看是否支援。在這個步驟後,這個名稱即無法變更,而且在專案期間內仍會保持有效。
  • 資訊中的第三個值是專案編號,部分 API 會使用這個編號。如要進一步瞭解這三個值,請參閱說明文件
  1. 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Cloud 資源/API。執行本程式碼研究室不會產生任何費用 (如果有的話)。如要關閉資源,以免產生本教學課程結束後產生的費用,您可以刪除自己建立的資源或刪除專案。新使用者符合 $300 美元免費試用計畫的資格。

啟動 Cloud Shell

雖然 Google Cloud 可以從筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Cloud Shell,這是一種在 Cloud 中執行的指令列環境。

啟用 Cloud Shell

  1. 在 Cloud 控制台中,按一下「啟用 Cloud Shell」圖示 853e55310c205094.png

3c1dabeca90e44e5.png

如果您是第一次啟動 Cloud Shell,系統會顯示中繼畫面,說明這項服務的內容。如果系統顯示中繼畫面,請按一下「繼續」

9c92662c6a846a5c.png

佈建並連線至 Cloud Shell 只需幾分鐘的時間。

9f0e51b578fecce5.png

這個虛擬機器已載入所有必要的開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。在本程式碼研究室中,您的大部分作業都可透過瀏覽器完成。

連線至 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 函式:

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=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

畫面上應會顯示以下結果:

Hello World! 👋

4. 撰寫使用引數的 HTTP Cloud 函式

函式接受引數時,功能會更實用。讓我們定義支援 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=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

您應該會看到預設結果:

Hello World! 🚀

你尚未設定 name 引數,因此會收到預設結果。在網址中加入參數:

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

這次,您會收到自訂回覆:

Hello YOUR NAME! 🚀

下一步是新增單元測試,確保在原始碼更新時,函式仍可正常運作。

5. 撰寫測試

系統會使用標準程式庫的 unittest 模組測試 Python 中的 HTTP Cloud Functions。您不需要執行模擬器或其他模擬即可測試函式,只需使用一般的 Python 程式碼即可。

hello_worldhello_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.py 依附 flask,因此請確認已在測試環境中安裝 Flask 架構:

pip install flask

安裝 Flask 會輸出類似以下的結果:

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

在本機執行以下測試:

python -m unittest

這三個單元測試應通過:

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

OK

接下來,您將建立一個新函式,用來傳回「Python Powered」標誌。

6. 寫入「Python 技術提供」HTTP Cloud 函式

讓我們傳回「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 網址。如果一切運作正常,您會看到「Python power」標誌。

接下來,您將建立應用程式,以便在部署前在本機執行並試用函式。

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. 它會在基準網址上註冊路徑,並利用名為 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

Cloud Shell 會在新的瀏覽器視窗中,開啟相關 Proxy 服務的預覽網址。網頁預覽功能會限制只有您的使用者帳戶可透過 HTTPS 存取。如果一切運作正常,您應該會看到「Python Powered」標誌!

8e5c3ead11cfd103.png

8. 恭喜!

b158ce75c3cccd6d.png

您已部署 HTTP Cloud Functions,使用慣用的函式透過 Flask 架構處理網路要求。

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!