מבוא לתזמור ללא שרת (serverless) עם Workflows

1. מבוא

c9b0cc839df0bb8f.png

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

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

ב-codelab הזה תלמדו איך לקשר בין שירותים שונים של Google Cloud וממשקי HTTP API חיצוניים לבין Workflows. באופן ספציפי, תחברו לתהליך עבודה שני שירותים ציבוריים של Cloud Functions, שירות פרטי של Cloud Run ו-API חיצוני ציבורי מבוסס-HTTP.

מה תלמדו

  • היכרות עם תהליכי עבודה.
  • איך מקשרים Cloud Functions ציבורי ל-Workflows.
  • איך מחברים שירותים פרטיים של Cloud Run ל-Workflows.
  • איך מחברים ממשקי HTTP API חיצוניים ל-Workflows.

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

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

  1. נכנסים אל Cloud Console ויוצרים פרויקט חדש או משתמשים בפרויקט קיים. (אם עדיין אין לכם חשבון Gmail או G Suite, אתם צריכים ליצור חשבון).

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

חשוב לזכור את מזהה הפרויקט, שהוא שם ייחודי בכל הפרויקטים ב-Google Cloud (השם שלמעלה כבר תפוס ולא יתאים לכם, מצטערים!). בהמשך ה-codelab הזה נתייחס אליו כאל PROJECT_ID.

  1. לאחר מכן, תצטרכו להפעיל את החיוב ב-Cloud Console כדי להשתמש במשאבים של Google Cloud.

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

מפעילים את Cloud Shell

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

ב-GCP Console, לוחצים על סמל Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

יחלפו כמה רגעים עד שההקצאה והחיבור לסביבת העבודה יושלמו. בסיום התהליך, אמור להופיע משהו כזה:

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

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

3. סקירה כללית על Workflows

היסודות

תהליך עבודה מורכב מסדרה של שלבים שמתוארים באמצעות תחביר מבוסס-YAML של Workflows. זו ההגדרה של תהליך העבודה. הסבר מפורט על תחביר ה-YAML של Workflows זמין בדף הפניה לתחביר.

כשיוצרים תהליך עבודה, הוא נפרס, כך שהוא מוכן להרצה. הרצה היא הפעלה יחידה של הלוגיקה שמופיעה בהגדרה של תהליך עבודה. כל ההרצות של תהליכי העבודה הן עצמאיות, והמוצר תומך במספר גבוה של הרצות בו-זמניות.

הפעלת שירותים

ב-codelab הזה תלמדו איך לחבר בין Cloud Functions, שירותי Cloud Run ו-Workflows. במהלך בניית השירותים, תשתמשו גם ב-Cloud Build וב-Cloud Storage.

מפעילים את כל השירותים הנדרשים:

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

בשלב הבא, תקשרו בין שתי פונקציות Cloud Functions בתהליך עבודה.

4. פריסת הפונקציה הראשונה של Cloud Functions

הפונקציה הראשונה היא מחולל מספרים אקראיים ב-Python.

יוצרים ספרייה לקוד הפונקציה ועוברים אליה:

mkdir ~/randomgen
cd ~/randomgen

יוצרים קובץ main.py בספרייה עם התוכן הבא:

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

כשהפונקציה מקבלת בקשת HTTP, היא יוצרת מספר אקראי בין 1 ל-100 ומחזירה אותו בפורמט JSON למתקשר.

הפונקציה מסתמכת על Flask לעיבוד HTTP, ולכן צריך להוסיף אותה כתלות. יחסי תלות ב-Python מנוהלים באמצעות pip ומפורטים בקובץ מטא-נתונים בשם requirements.txt.

יוצרים קובץ requirements.txt באותה תיקייה עם התוכן הבא:

flask>=1.0.2

פורסים את הפונקציה עם טריגר HTTP ועם הרשאה לבקשות לא מאומתות באמצעות הפקודה הבאה:

gcloud functions deploy randomgen \
    --runtime python312 \
    --trigger-http \
    --allow-unauthenticated

אחרי פריסת הפונקציה, אפשר לראות את כתובת ה-URL שלה בנכס url שמוצג במסוף או באמצעות הפקודה gcloud functions describe.

אפשר גם להיכנס לכתובת ה-URL של הפונקציה באמצעות הפקודה curl הבאה:

curl $(gcloud functions describe randomgen --format='value(url)')

הפונקציה מוכנה לתהליך העבודה.

5. פריסת הפונקציה השנייה של Cloud Functions

הפונקציה השנייה היא מכפיל. הוא מכפיל את הקלט שהתקבל ב-2.

יוצרים ספרייה לקוד הפונקציה ועוברים אליה:

mkdir ~/multiply
cd ~/multiply

יוצרים קובץ main.py בספרייה עם התוכן הבא:

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

כשהפונקציה מקבלת בקשת HTTP, היא מחלצת את input מגוף ה-JSON, מכפילה אותו ב-2 ומחזירה אותו בפורמט JSON למתקשר.

יוצרים את אותו קובץ requirements.txt באותה תיקייה עם התוכן הבא:

flask>=1.0.2

פורסים את הפונקציה עם טריגר HTTP ועם הרשאה לבקשות לא מאומתות באמצעות הפקודה הבאה:

gcloud functions deploy multiply \
    --runtime python312 \
    --trigger-http \
    --allow-unauthenticated

אחרי שפורסים את הפונקציה, אפשר גם להיכנס לכתובת ה-URL של הפונקציה באמצעות הפקודה curl הבאה:

curl $(gcloud functions describe multiply --format='value(url)') \
-X POST \
-H "content-type: application/json" \
-d '{"input": 5}'

הפונקציה מוכנה לתהליך העבודה.

6. חיבור בין שתי פונקציות Cloud

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

יוצרים קובץ workflow.yaml עם התוכן הבא.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

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

פורסים את תהליך העבודה הראשון:

gcloud workflows deploy workflow --source=workflow.yaml

מריצים את תהליך העבודה הראשון:

gcloud workflows execute workflow

אחרי שמריצים את תהליך העבודה, אפשר לראות את התוצאה על ידי העברת מזהה ההרצה שמופיע בשלב הקודם:

gcloud workflows executions describe <your-execution-id> --workflow workflow

הפלט יכלול את הערכים result ו-state:

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. חיבור של ממשק API חיצוני ל-HTTP

בשלב הבא, מחברים את math.js כשירות חיצוני בתהליך העבודה.

ב-math.js, אפשר להעריך ביטויים מתמטיים כמו:

curl https://api.mathjs.org/v4/?'expr=log(56)'

הפעם, תשתמשו במסוף Cloud כדי לעדכן את תהליך העבודה. מחפשים את Workflows במסוף Google Cloud:

7608a7991b33bbb0.png

מאתרים את תהליך העבודה ולוחצים על הכרטיסייה Definition:

f3c8c4d3ffa49b1b.png

עורכים את הגדרת תהליך העבודה ומוסיפים קריאה ל-math.js.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

עכשיו, תהליך העבודה מעביר את הפלט של פונקציית הכפל לבקשה להפעלת פונקציית יומן ב-math.js.

ממשק המשתמש ינחה אתכם לערוך ולפרוס את תהליך העבודה. אחרי הפריסה, לוחצים על Execute כדי להפעיל את תהליך העבודה. יוצגו פרטי ההפעלה:

b40c76ee43a1ce65.png

שימו לב לקוד הסטטוס 200 ול-body עם הפלט של פונקציית היומן.

הרגע שילבת שירות חיצוני בתהליך העבודה שלנו, זה ממש מגניב!

8. פריסת שירות Cloud Run

בחלק האחרון, משלימים את תהליך העבודה באמצעות קריאה לשירות פרטי של Cloud Run. המשמעות היא שצריך לאמת את תהליך העבודה כדי לקרוא לשירות Cloud Run.

שירות Cloud Run מחזיר את math.floor של המספר שהועבר.

יוצרים ספרייה לקוד השירות ועוברים אליה:

mkdir ~/floor
cd ~/floor

יוצרים קובץ app.py בספרייה עם התוכן הבא:

import json
import logging
import os
import math

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_post():
    content = json.loads(request.data)
    input = float(content['input'])
    return f"{math.floor(input)}", 200

if __name__ != '__main__':
    # Redirect Flask logs to Gunicorn logs
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
    app.logger.info('Service started...')
else:
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

‫Cloud Run פורס קונטיינרים, ולכן צריך Dockerfile והקונטיינר צריך להיות קשור ל-0.0.0.0 ולמשתנה הסביבה PORT, ומכאן הקוד שלמעלה.

כשהפונקציה מקבלת בקשת HTTP, היא מחלצת את input מגוף ה-JSON, מפעילה את הפונקציה math.floor ומחזירה את התוצאה למשתמש ששלח את הבקשה.

באותה ספרייה, יוצרים את הקובץ Dockerfile הבא:

# Use an official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# Install production dependencies.
RUN pip install Flask gunicorn

# Copy local code to the container image.
WORKDIR /app
COPY . .

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 1 --threads 8 app:app

בונים את מאגר התגים:

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

אחרי שיוצרים את הקונטיינר, פורסים אותו ב-Cloud Run. שימו לב לדגל no-allow-unauthenticated. כך מוודאים שהשירות יקבל רק שיחות מאומתות:

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

אחרי הפריסה, השירות מוכן לשימוש בתהליך העבודה.

9. חיבור שירות Cloud Run

כדי להגדיר את Workflows כך שיקרא לשירות הפרטי של Cloud Run, צריך ליצור חשבון שירות לשימוש ב-Workflows:

export SERVICE_ACCOUNT=workflows-sa
gcloud iam service-accounts create ${SERVICE_ACCOUNT}

מקצים לחשבון השירות את התפקיד run.invoker. כך חשבון השירות יוכל לקרוא לשירותי Cloud Run מאומתים:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

מעדכנים את הגדרת תהליך העבודה ב-workflow.yaml כך שתכלול את שירות Cloud Run. שימו לב שאתם כוללים גם את השדה auth כדי לוודא ש-Workflows מעביר את אסימון האימות בקריאות שלו לשירות Cloud Run:

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

מעדכנים את תהליך העבודה. הפעם מעבירים את חשבון השירות:

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

מריצים את תהליך העבודה:

gcloud workflows execute workflow

אחרי כמה שניות, אפשר לבדוק את ההרצה של תהליך העבודה כדי לראות את התוצאה:

gcloud workflows executions describe <your-execution-id> --workflow workflow

הפלט יכלול מספר שלם result ו-state:

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. מעולה!

כל הכבוד, סיימתם את ה-Codelab.

מה נכלל

  • היכרות עם תהליכי עבודה.
  • איך מקשרים Cloud Functions ציבורי ל-Workflows.
  • איך מחברים שירותים פרטיים של Cloud Run ל-Workflows.
  • איך מחברים ממשקי HTTP API חיצוניים ל-Workflows.