HTTP Cloud Functions ב-Python

1. מבוא

b158ce75c3cccd6d.png

Python היא שפת תכנות פופולרית בקוד פתוח, שמשמשת מדעני נתונים, מפתחי אפליקציות אינטרנט, מנהלי מערכות ועוד.

Cloud Functions היא פלטפורמת מחשוב ללא שרת (serverless) שמבוססת על אירועים. בעזרת Cloud Functions תוכלו לכתוב את הקוד בלי לדאוג לגבי הקצאת משאבים או התאמה לעומס (scaling) כדי להתמודד עם הדרישות המשתנות.

יש שני סוגים של Cloud Functions:

  • פונקציות HTTP מגיבות לבקשות HTTP. תבנה כמה זוגות ב-Codelab הזה.
  • פונקציות ברקע מופעלות על ידי אירועים, כמו הודעה שמתפרסמת ב-Cloud Pub/Sub או קובץ שמעלים ל-Cloud Storage. אנחנו לא מטפלים בעניין הזה בשיעור ה-Lab הזה, אבל תוכלו לקרוא מידע נוסף במסמכי התיעוד.

efb3268e3b74ed4f.png

ה-Codelab הזה ידריך אותך איך ליצור פונקציות Cloud Functions משלך ב-Python.

מה תפַתחו

ב-Codelab הזה תפרסמו פונקציה של Cloud Functions, שכאשר מפעילים אותה דרך HTTP, תוצג ההודעה "Python Powered" לוגו:

a7aaf656b78050fd.png

מה תלמדו

  • איך כותבים פונקציית HTTP של Cloud Functions.
  • איך כותבים פונקציית HTTP של Cloud Functions שלוקחת ארגומנטים.
  • איך בודקים פונקציית HTTP של Cloud Functions.
  • איך להריץ שרת Python HTTP מקומי כדי לנסות את הפונקציה.
  • איך כותבים פונקציה של Cloud Functions שמחזירה תמונה.

2. הגדרה ודרישות

הגדרת סביבה בקצב אישי

  1. נכנסים למסוף Google Cloud ויוצרים פרויקט חדש או עושים שימוש חוזר בפרויקט קיים. אם אין לכם עדיין חשבון Gmail או חשבון Google Workspace, עליכם ליצור חשבון.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Project name הוא השם המוצג של המשתתפים בפרויקט. זו מחרוזת תווים שלא משמשת את Google APIs. תמיד אפשר לעדכן.
  • Project ID הוא ייחודי בכל הפרויקטים ב-Google Cloud ואי אפשר לשנות אותו (אי אפשר לשנות אותו אחרי שמגדירים אותו). מסוף Cloud יוצר מחרוזת ייחודית באופן אוטומטי; בדרך כלל לא מעניין אותך מה זה. ברוב ה-codelabs תצטרכו להפנות למזהה הפרויקט שלכם (בדרך כלל מזוהה כ-PROJECT_ID). אם המזהה שנוצר לא מוצא חן בעיניכם, אתם יכולים ליצור מזהה אקראי אחר. לחלופין, אפשר לנסות שם משלך ולראות אם הוא זמין. לא ניתן לשנות אותו אחרי השלב הזה, והוא נשאר למשך הפרויקט.
  • לידיעתך, יש ערך שלישי, Project Number, שבו משתמשים בחלק מממשקי ה-API. מידע נוסף על כל שלושת הערכים האלה זמין במסמכי התיעוד.
  1. בשלב הבא צריך להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבים או בממשקי API של Cloud. מעבר ב-Codelab הזה לא יעלה הרבה כסף, אם בכלל. כדי להשבית משאבים ולא לצבור חיובים מעבר למדריך הזה, אתם יכולים למחוק את המשאבים שיצרתם או למחוק את הפרויקט. משתמשים חדשים ב-Google Cloud זכאים להשתתף בתוכנית תקופת ניסיון בחינם בשווי 1,200 ש"ח.

הפעלת Cloud Shell

אומנם אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה משתמשים ב-Cloud Shell, סביבת שורת הפקודה שפועלת ב-Cloud.

הפעלת Cloud Shell

  1. במסוף Cloud, לוחצים על Activate Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

אם זו הפעם הראשונה שאתם מפעילים את Cloud Shell, יוצג לכם מסך ביניים שמתוארת בו. אם הוצג לכם מסך ביניים, לוחצים על המשך.

9c92662c6a846a5c.png

ההקצאה וההתחברות ל-Cloud Shell נמשכת כמה דקות.

9f0e51b578fecce5.png

במכונה הווירטואלית הזו נמצאים כל כלי הפיתוח הדרושים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר משמעותית את ביצועי הרשת והאימות. אם לא את כולן, ניתן לבצע חלק גדול מהעבודה ב-Codelab הזה באמצעות דפדפן.

אחרי ההתחברות ל-Cloud Shell, אתם אמורים לראות שהפרויקט מאומת ושהפרויקט מוגדר לפי מזהה הפרויקט שלכם.

  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].

איך מוודאים שממשקי ה-API של Cloud Functions ו-Cloud Build מופעלים

מריצים את הפקודה הבאה מ-Cloud Shell כדי לוודא שממשקי ה-API של Cloud Functions ו-Cloud Build מופעלים:

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

פונקציות HTTP Cloud Functions ב-Python נכתבות כפונקציות 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 Editor אחרי שמגדירים את ספריית המקור כסביבת עבודה:

cloudshell open-workspace .

נפרוס את הפונקציה הזו כפונקציית HTTP של Cloud Functions באמצעות הפקודה gcloud functions deploy:

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, התקציב יכול להיות כרגע python37, python38, python39, python310 או python312. ראו זמני ריצה.
  • --trigger-http: לפונקציה תוקצה נקודת קצה (endpoint). בקשות HTTP (POST, PUT, GET, DELETE ו-OPTIONS) לנקודת הקצה יפעילו הרצת פונקציה.
  • --allow-unauthenticated: הפונקציה תהיה ציבורית ותאפשר לכל המתקשרים, ללא בדיקת אימות.
  • למידע נוסף, קראו את המאמר פריסה של פונקציות של gcloud.

כדי לבדוק את הפונקציה, אפשר ללחוץ על כתובת ה-URL httpsTrigger.url שמוצגת בפלט הפקודה שלמעלה. אפשר גם לאחזר את כתובת ה-URL באופן פרוגרמטי ולהפעיל את הפונקציה באמצעות הפקודות הבאות:

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

אמורה להתקבל התוצאה הבאה:

Hello World! 👋

4. כתיבת פונקציה של Cloud Functions של HTTP שלוקחת ארגומנטים

פונקציות מגוונות יותר כשהן מקבלות ארגומנטים. נגדיר פונקציה חדשה hello_name שתומכת בפרמטר 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
...

כדי לבדוק את הפונקציה, אפשר ללחוץ על כתובת ה-URL httpsTrigger.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. מבחנים על כתיבה

הפונקציות של HTTP Cloud Functions ב-Python נבדקות באמצעות המודול 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.py תלוי ב-flask, צריך לוודא ש-Flask framework מותקנת בסביבת הבדיקה:

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 Powered פונקציית HTTP של Cloud Functions

הגיע הזמן להפוך פונקציה חדשה ליותר מבדרת באמצעות החזרת "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
...

כדי לבדוק את הפונקציה, צריך ללחוץ על כתובת ה-URL httpsTrigger.url שמוצגת בפלט הפקודה שלמעלה. אם הכול פועל כמו שצריך, יופיע הכיתוב 'מופעל על ידי Python'. הלוגו בכרטיסייה חדשה בדפדפן!

בשלב הבא תיצרו אפליקציה שתאפשר לכם להריץ את הפונקציה שלה ולנסות אותה באופן מקומי לפני הפריסה.

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 framework מותקנת בסביבת הפיתוח:

pip install flask

התקנת Flask מפיקה תוצאה שדומה לזו:

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

כדי להריץ את האפליקציה הזו באופן מקומי, מריצים את הפקודה הבאה:

python web_app.py

עכשיו אפשר להשתמש ב-Cloud Shell Web Preview כדי לבדוק את אפליקציית האינטרנט בדפדפן. ב-Cloud Shell, לוחצים על Web Preview. ואז בוחרים באפשרות 'תצוגה מקדימה ביציאה 8080':

6c9ff9e5c692c58e.gif

Cloud Shell פותח את כתובת ה-URL של התצוגה המקדימה בשירות ה-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.

אנחנו מקווים שתיהנו להשתמש ב-Cloud Functions ב-Python!