HTTP Cloud Functions ב-Python

1. מבוא

b158ce75c3cccd6d.png

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

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

יש שני סוגים של 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 שמקבלת ארגומנטים.
  • איך בודקים פונקציה של Cloud Functions מסוג HTTP.
  • איך מריצים שרת HTTP מקומי של Python כדי לנסות את הפונקציה.
  • איך כותבים פונקציה של Cloud Functions מסוג HTTP שמחזירה תמונה.

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

הגדרת סביבה בקצב עצמי

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

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • שם הפרויקט הוא השם המוצג של הפרויקט הזה למשתתפים. זו מחרוזת תווים שלא נמצאת בשימוש ב-Google APIs. תמיד אפשר לעדכן את המיקום.
  • מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud, והוא קבוע (אי אפשר לשנות אותו אחרי שהוא מוגדר). מסוף Cloud יוצר באופן אוטומטי מחרוזת ייחודית, ובדרך כלל לא צריך לדעת מה היא. ברוב ה-Codelabs, תצטרכו להפנות למזהה הפרויקט (בדרך כלל מסומן כ-PROJECT_ID). אם אתם לא אוהבים את המזהה שנוצר, אתם יכולים ליצור מזהה אקראי אחר. אפשר גם לנסות שם משתמש משלכם ולבדוק אם הוא זמין. אי אפשר לשנות את ההגדרה הזו אחרי השלב הזה, והיא תישאר לאורך הפרויקט.
  • לידיעתכם, יש ערך שלישי, מספר פרויקט, שחלק מממשקי ה-API משתמשים בו. במאמרי העזרה מפורט מידע נוסף על שלושת הערכים האלה.
  1. בשלב הבא, תצטרכו להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבי Cloud או בממשקי API של Cloud. השלמת ה-codelab הזה לא תעלה לכם הרבה, אם בכלל. כדי להשבית את המשאבים ולמנוע חיובים נוספים אחרי שתסיימו את המדריך הזה, תוכלו למחוק את המשאבים שיצרתם או למחוק את הפרויקט. משתמשים חדשים ב-Google Cloud זכאים לתוכנית תקופת ניסיון בחינם בשווי 300$.

הפעלת Cloud Shell

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

הפעלת Cloud Shell

  1. ב-Cloud Console, לוחצים על 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].

מוודאים ש-Cloud Functions API ו-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

פונקציות 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 .

נפרוס את הפונקציה הזו כפונקציה של Cloud Functions ב-HTTP באמצעות הפקודה 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: הפונקציה תקבל נקודת קצה. בקשות HTTP ‏ (POST,‏ PUT,‏ GET,‏ DELETE ו-OPTIONS) לנקודת הקצה יפעילו את ביצוע הפונקציה.
  • --allow-unauthenticated: הפונקציה תהיה ציבורית, כך שכל המתקשרים יוכלו להשתמש בה בלי שייבדק אימות.
  • מידע נוסף זמין במאמר בנושא gcloud functions deploy.

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

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

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

Hello World! 👋

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

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

בואו ניצור פונקציה חדשה שתהיה קצת יותר משעשעת, ונחזיר את התמונה '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 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 framework מותקן בסביבת הפיתוח:

pip install flask

הפלט של התקנת Flask אמור להיראות כך:

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

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

python web_app.py

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

6c9ff9e5c692c58e.gif

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