מדריך סדנה טכנית מעשית באמצעות Duet AI למפתחים Codelab

1. מטרות

מטרת הסדנה הזו היא לספק למשתמשים ולאנשי מקצוע הדרכה מעשית בנושא Duet AI.

ב-codelab הזה תלמדו:

  1. מפעילים את Duet AI בפרויקט GCP ומגדירים אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
  2. שימוש ב-Duet AI ליצירה, להשלמה ולהסבר של קוד.
  3. להשתמש ב-Duet AI כדי להסביר ולפתור בעיה באפליקציה.
  4. תכונות של Duet AI כמו צ'אט IDE ושיחה עם זיכרון, צ'אט לעומת יצירת קוד בשורה, פעולות חכמות כמו הסבר על קוד ואישור של הקראת קוד ועוד.

הסיפור

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

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

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

אחרי שהשירות מקודד, אדמין פלטפורמה משתמש ב-Duet AI (צ'אט) כדי ליצור את הארטיפקט (מאגר Docker) ואת המשאבים שנדרשים לפריסת הארטיפקט ב-GCP (לדוגמה, Artifact Registry, הרשאות IAM, מאגר קוד, תשתית מחשוב, כלומר GKE או CloudRun וכו').

אחרי שהאפליקציה נפרסת ב-GCP, מפעיל אפליקציה או SRE ישתמש ב-Duet AI (וב-Cloud Ops) כדי לפתור בעיה בשירות החדש.

פרסונה

הסדנה מתמקדת בדמות הבאה:

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

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

‫2. הכנת הסביבה

הפעלת Duet AI

אפשר להפעיל את Duet AI בפרויקט GCP באמצעות API (gcloud או כלים של IaC כמו Terraform) או באמצעות ממשק המשתמש של Cloud Console.

כדי להפעיל את Duet AI בפרויקט בענן ב-Google Cloud, צריך להפעיל את Cloud AI Companion API ולהקצות למשתמשים את תפקידי ה-IAM‏ Cloud AI Companion User ו-Service Usage Viewer.

באמצעות gcloud

מפעילים את Cloud Shell:

מגדירים את PROJECT_ID ואת USER ומפעילים את Cloud AI Companion API.

export PROJECT_ID=<YOUR PROJECT ID>
export USER=<YOUR USERNAME> # Use your full LDAP, e.g. name@example.com
gcloud config set project ${PROJECT_ID}
gcloud services enable cloudaicompanion.googleapis.com --project ${PROJECT_ID}

הפלט אמור להיראות כך:

Updated property [core/project].
Operation "operations/acat.p2-60565640195-f37dc7fe-b093-4451-9b12-934649e2a435" finished successfully.

מקצים לחשבון המשתמש את התפקידים 'משתמש ב-Cloud AI Companion' ו'צפייה בשימוש בשירות' בניהול הזהויות והרשאות הגישה (IAM). ה-API של Cloud Companion פועל ברקע של התכונות שבהן נשתמש ב-IDE ובמסוף. ההרשאה 'צפייה בשימוש בשירות' משמשת לבדיקה מהירה לפני הפעלת ממשק המשתמש במסוף (כדי שממשק המשתמש של Duet יופיע רק בפרויקטים שבהם מופעל ה-API).

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/cloudaicompanion.user

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/serviceusage.serviceUsageViewer

הפלט אמור להיראות כך:

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/cloudaicompanion.user

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/serviceusage.serviceUsageViewer

דרך Cloud Console

כדי להפעיל את ה-API, נכנסים לדף Cloud AI Companion API במסוף Google Cloud. ‫

בוחרים את הפרויקט מרשימת הפרויקטים.

לוחצים על הפעלה.

הדף יתעדכן ויופיע הסטטוס Enabled. ‫Duet AI זמין עכשיו בפרויקט Google Cloud שנבחר לכל המשתמשים שיש להם את תפקידי ה-IAM הנדרשים.

נכנסים לדף IAM כדי להקצות את התפקידים ב-IAM שנדרשים בשביל Duet AI. ‫

בעמודה Principal, מחפשים את המשתמש שרוצים לתת לו גישה ל-Duet AI, ולוחצים על סמל העיפרון ✏️ Edit principal בשורה הזו.

בחלונית Edit access, לוחצים על Add another role.

ברשימת התפקידים Select a role בוחרים את Cloud AI Companion User.

לוחצים על Add another role ובוחרים את התפקיד Service Usage Viewer.

לוחצים על שמירה.

הגדרת סביבת הפיתוח המשולבת

מפתחים יכולים לבחור מתוך מגוון סביבות פיתוח משולבות (IDE) את זו שהכי מתאימה לצרכים שלהם. התכונה Duet AI Code Assistance זמינה בסביבות פיתוח משולבות (IDE) רבות, כמו Visual Studio Code, סביבות פיתוח משולבות של JetBrains (IntelliJ,‏ PyCharm,‏ GoLand,‏ WebStorm ועוד), Cloud Workstations ו-Cloud Shell Editor.

בשיעור ה-Lab הזה אפשר להשתמש ב-Cloud Workstations או בכלי Cloud Shell Editor.

בסדנה הזו משתמשים ב-Cloud Shell Editor.

שימו לב: יכול להיות שיעברו 20-30 דקות עד שתהליך ההגדרה של Cloud Workstations יסתיים.

כדי להשתמש בו באופן מיידי, אפשר להשתמש ב-Cloud Shell Editor.

לוחצים על סמל העיפרון ✏️ בסרגל התפריטים העליון של Cloud Shell כדי לפתוח את Cloud Shell Editor.

ממשק המשתמש וחוויית המשתמש של Cloud Shell Editor דומים מאוד לאלה של VSCode.

d6a6565f83576063.png

לוחצים על CTRL (ב-Windows) או על CMD (ב-Mac) + , (פסיק) כדי להיכנס לחלונית ההגדרות.

בסרגל החיפוש, מקלידים 'duet ai'.

מוודאים שהאפשרויות Cloudcode › Duet AI: Enable ו-Cloudcode › Duet AI › Inline Suggestions: Enable Auto מופעלות.

111b8d587330ec74.png

בסרגל המצב התחתון, לוחצים על Cloud Code - Sign In ופועלים לפי תהליך הכניסה.

אם כבר נכנסתם לחשבון, בשורת המצב יופיע הכיתוב Cloud Code – No project (Cloud Code – אין פרויקט).

לוחצים על Cloud Code – No project (Cloud Code – אין פרויקט), ולוחצים על החלונית הנפתחת של הפעולה שמופיעה בחלק העליון. לוחצים על Select a Google Cloud project (בחירת פרויקט בענן של Google).

3241a59811e3c84a.png

מתחילים להקליד את מזהה הפרויקט והפרויקט אמור להופיע ברשימה.

c5358fc837588fe.png

בוחרים את PROJECT_ID מרשימת הפרויקטים.

שורת הסטטוס התחתונה מתעדכנת ומציגה את מזהה הפרויקט. אם הוא לא מופיע, יכול להיות שתצטרכו לרענן את הכרטיסייה של Cloud Shell Editor.

לוחצים על סמל Duet AI‏ d97fc4e7b594c3af.pngבסרגל התפריטים שמשמאל, וחלון הצ'אט של Duet AI יופיע. אם מופיעה ההודעה 'בחירת פרויקט ב-GCP'. לוחצים על הפרויקט ומסמנים אותו מחדש.

יוצג חלון הצ'אט של Duet AI

781f888360229ca6.png

‫3. הגדרת התשתית

d3234d237f00fdbb.png

כדי להפעיל את שירות המשלוחים החדש ב-GCP, אתם צריכים את משאבי GCP הבאים:

  1. מכונה של Cloud SQL, עם מסד נתונים.
  2. אשכול GKE להרצת השירות בקונטיינר.
  3. ‫Artifact Registry לאחסון קובץ האימג' של Docker.
  4. מאגר Cloud Source Repository לקוד.

בטרמינל של Cloud Shell, משכפלים את מאגר הנתונים הבא ומריצים את הפקודות הבאות כדי להגדיר את התשתית בפרויקט GCP.

# Set your project
export PROJECT_ID=<INSERT_YOUR_PROJECT_ID>
gcloud config set core/project ${PROJECT_ID}

# Enable Cloudbuild and grant Cloudbuild SA owner role 
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format 'value(projectNumber)')
gcloud services enable cloudbuild.googleapis.com
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com --role roles/owner

# Clone the repo
git clone https://github.com/duetailabs/dev.git ~/duetaidev
cd ~/duetaidev

# Run Cloudbuild to create the necessary resources
gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}

# To destroy all GCP resources, run the following
# gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID} --config=cloudbuild_destroy.yaml

4. פיתוח שירות Python Flask

9745ba5c70782e76.png

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

  1. package-service.yaml – מפרט Open API לשירות החבילות, שכולל נתונים כמו גובה, רוחב, משקל והוראות מיוחדות לטיפול.
  2. data_model.py – מודל נתונים למפרט של package-service API. יוצר גם את הטבלה packages במסד הנתונים product_details.
  3. connect_connector.py – חיבור ל-CloudSQL (הגדרת המנוע, Session ו-Base ORM)
  4. db_init.py – יוצר נתוני מדגם בטבלת packages.
  5. main.py – שירות Python Flask עם נקודת קצה GET לאחזור פרטי חבילה מנתוני packages על סמך product_id.
  6. test.py – בדיקת יחידה
  7. requirement.txt – דרישות Python
  8. Dockerfile – כדי להוסיף את האפליקציה הזו לקונטיינר

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

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

במסוף Cloud Shell, מריצים את הפקודה הבאה כדי לשכפל את המאגר.

cd ~
gcloud source repos clone shipping shipping
cd ~/shipping 

פותחים את סרגל הצד של הצ'אט עם Duet AI מהתפריט הימני של Cloud Shell Editor. הסמל נראה כך: 8b135a000b259175.png. עכשיו אפשר להשתמש ב-Duet AI כדי לקבל עזרה בכתיבת קוד.

package-service.yaml

כשלא פתוחים קבצים, מבקשים מ-Duet ליצור מפרט Open API לשירות המשלוחים.

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

ba12626f491a1204.png

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

אפשר COPY 71194556d8061dae.pngאת הקוד ולהדביק אותו בקובץ.

אפשר ADD df645de8c65607a.png את הקוד לקובץ שפתוח כרגע בכלי העריכה.

אפשר גם OPEN a4c7ed6d845df343.png את הקוד בקובץ חדש.

לוחצים על OPEN a4c7ed6d845df343.png הקוד בקובץ חדש.

לוחצים על CTRL/CMD + s כדי לשמור את הקובץ, ומאחסנים אותו בתיקיית האפליקציה עם שם הקובץ package-service.yaml. לחץ על 'אישור'.

f6ebd5b836949366.png

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את התשובות של Duet AI.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

data_model.py

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

פותחים את קובץ package-service.yaml ומזינים את ההנחיה הבאה.

הנחיה 1: באמצעות python sqlalchemy ORM, צור מודל נתונים לשירות ה-API הזה. צריך לכלול גם פונקציה נפרדת ונקודת כניסה ראשית שיוצרת את טבלאות מסד הנתונים.

b873a6a28bd28ca1.png

בואו נסתכל על כל חלק שנוצר. ‫Duet AI הוא עדיין עוזר, ולמרות שהוא יכול לעזור לכתוב קוד במהירות, עדיין כדאי לבדוק את התוכן שנוצר ולהבין אותו תוך כדי.

קודם כל, יש Class בשם Package מסוג kind Base שמגדיר את מודל הנתונים עבור מסד הנתונים packages, כמו בדוגמה הבאה:

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(String(255))
    height = Column(Float)
    width = Column(Float)
    depth = Column(Float)
    weight = Column(Float)
    special_handling_instructions = Column(String(255))

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

def create_tables(engine):
    Base.metadata.create_all(engine)

לבסוף, צריך פונקציה ראשית שמריצה את הפונקציה create_tables כדי ליצור את הטבלה במסד הנתונים של CloudSQL, כמו בדוגמה הבאה:

if __name__ == '__main__':
    from sqlalchemy import create_engine

    engine = create_engine('sqlite:///shipping.db')
    create_tables(engine)

    print('Tables created successfully.')

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

משתמשים ב-OPEN a4c7ed6d845df343.png בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם data_model.py (שימו לב לקו התחתון בשם, ולא למקף).

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

connect-connector.py

יוצרים את המחבר של Cloud SQL.

פותחים את הקובץ data_model.py ומזינים את ההנחיות הבאות.

הנחיה 1: באמצעות ספריית cloud-sql-python-connector, צור פונקציה שמאתחלת מאגר חיבורים למופע Cloud SQL של Postgres.

ed05cb6ff85d34c5.png

שימו לב שהתשובה לא משתמשת בספרייה cloud-sql-python-connector. כדי לתת ל-Duet כיוון, אתם יכולים לשפר את ההנחיות על ידי הוספת פרטים ספציפיים לאותו שרשור צ'אט.

אפשר להשתמש בהנחיה אחרת.

הנחיה 2: חובה להשתמש בספרייה cloud-sql-python-connector.

d09095b44dde35bf.png

מוודאים שהיא משתמשת בספרייה cloud-sql-python-connector.

משתמשים ב-OPEN a4c7ed6d845df343.png בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם connect_conector.py. יכול להיות שתצטרכו לייבא ידנית את הספרייה pg8000. פרטים נוספים מופיעים בקובץ שלמטה.

מנקים את היסטוריית הצ'אט עם Duet AI, ופותחים את הקובץ connect_connector.py כדי ליצור את ה-ORM‏ DB engine, sessionmaker ו-base לשימוש באפליקציה.

הנחיה 1: יצירת מנוע, מחלקה של sessionmaker ו-Base ORM באמצעות השיטה connect_with_connector

6e4214b72ab13a63.png

יכול להיות שהתשובה תצורף לקובץ connect_connector.py עם התוספים engine, Session ו-Base.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את הווריאציות האפשריות בתשובות של Duet AI.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

עדכון של data_model.py

כדי ליצור טבלה במסד הנתונים של CloudSQL, צריך להשתמש במנוע שיצרתם בשלב הקודם (בקובץ connect_connector.py).

לנקות את היסטוריית הצ'אט עם Duet AI. פותחים את הקובץ data_model.py. אפשר לנסות את ההנחיה הבאה.

הנחיה 1: בפונקציה הראשית, מייבאים את המנוע מ-connect_connector.py ומשתמשים בו

2e768c9b6c523b9a.png

אמורה להופיע התגובה 'ייבוא engine מ-connect_connector' (במקרה של CloudSQL). המאפיין create_table משתמש במנוע הזה (במקום במסד הנתונים המקומי sqlite שמוגדר כברירת מחדל).

מעדכנים את הקובץ data_model.py.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

requirements.txt

יוצרים קובץ requirements.txt לאפליקציה.

פותחים את connect_connector.py ואת הקובץ data_model.py ומזינים את ההנחיה הבאה.

הנחיה 1: יצירת קובץ דרישות pip למודל הנתונים ולשירות הזה

הנחיה 2: יצירת קובץ דרישות pip למודל הנתונים ולשירות הזה באמצעות הגרסאות העדכניות ביותר

69fae373bc5c6a18.png

מוודאים שהשמות והגרסאות נכונים. לדוגמה, בתגובה שלמעלה, השם והגרסה של google-cloud-sql-connecter שגויים. צריך לתקן את הגרסאות באופן ידני וליצור קובץ requirements.txt שנראה כך:

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0

בטרמינל הפקודות, מריצים את הפקודה הבאה:

pip3 install -r requirements.txt

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

יצירת טבלת חבילות ב-CloudSQL

מגדירים את משתני הסביבה עבור מחבר מסד הנתונים של CloudSQL.

export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export DB_USER=evolution
export DB_PASS=evolution
export DB_NAME=product_details

עכשיו מריצים את data_model.py.

python data_model.py

הפלט אמור להיראות כך (כדאי לבדוק את הקוד כדי לראות מה צפוי בפועל):

Tables created successfully.

מתחברים למכונה של Cloud SQL ובודקים שנוצר מסד הנתונים.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

אחרי שמזינים את הסיסמה (גם evolution), מקבלים את הטבלאות.

product_details=> \dt

הפלט אמור להיראות כך:

           List of relations
 Schema |   Name   | Type  |   Owner   
--------+----------+-------+-----------
 public | packages | table | evolution
(1 row)

אפשר גם לבדוק את מודל הנתונים ואת פרטי הטבלה.

product_details=> \d+ packages

הפלט אמור להיראות כך:

                                                                        Table "public.packages"
            Column             |       Type        | Collation | Nullable |               Default                | Storage  | Compression | Stats target | Description 
-------------------------------+-------------------+-----------+----------+--------------------------------------+----------+-------------+--------------+-------------
 id                            | integer           |           | not null | nextval('packages_id_seq'::regclass) | plain    |             |              | 
 product_id                    | integer           |           | not null |                                      | plain    |             |              | 
 height                        | double precision  |           | not null |                                      | plain    |             |              | 
 width                         | double precision  |           | not null |                                      | plain    |             |              | 
 depth                         | double precision  |           | not null |                                      | plain    |             |              | 
 weight                        | double precision  |           | not null |                                      | plain    |             |              | 
 special_handling_instructions | character varying |           |          |                                      | extended |             |              | 
Indexes:
    "packages_pkey" PRIMARY KEY, btree (id)
Access method: heap

מקלידים \q כדי לצאת מ-Cloud SQL.

db_init.py

בשלב הבא, נוסיף כמה נתונים לדוגמה לטבלה packages.

לנקות את היסטוריית הצ'אט עם Duet AI. אחרי שפותחים את הקובץ data_model.py, מנסים את ההנחיות הבאות.

הנחיה 1: יצירת פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומעבירה אותן לטבלת החבילות

הנחיה 2: באמצעות הסשן מ-connect_connector, צריך ליצור פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומבצעת commit שלהן לטבלת החבילות

34a9afc5f04ba5.png

משתמשים ב-OPEN a4c7ed6d845df343.png בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם db_init.py.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

יצירת נתונים לדוגמה של חבילות

מריצים את הפקודה db_init.py משורת הפקודה.

python db_init.py

הפלט אמור להיראות כך:

Packages created successfully.

מתחברים שוב למופע CloudSQL ומוודאים שנתוני הדוגמה נוספו לטבלה packages.

מתחברים למכונה של Cloud SQL ובודקים שנוצר מסד הנתונים.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

אחרי שמזינים את הסיסמה (גם evolution), מקבלים את כל הנתונים מהטבלה packages.

product_details=> SELECT * FROM packages;

הפלט אמור להיראות כך:

 id | product_id | height | width | depth | weight |   special_handling_instructions   
----+------------+--------+-------+-------+--------+-----------------------------------
  1 |          0 |     10 |    10 |    10 |     10 | No special handling instructions.
  2 |          1 |     10 |    10 |    10 |     10 | No special handling instructions.
  3 |          2 |     10 |    10 |    10 |     10 | No special handling instructions.
  4 |          3 |     10 |    10 |    10 |     10 | No special handling instructions.
  5 |          4 |     10 |    10 |    10 |     10 | No special handling instructions.
  6 |          5 |     10 |    10 |    10 |     10 | No special handling instructions.
  7 |          6 |     10 |    10 |    10 |     10 | No special handling instructions.
  8 |          7 |     10 |    10 |    10 |     10 | No special handling instructions.
  9 |          8 |     10 |    10 |    10 |     10 | No special handling instructions.
 10 |          9 |     10 |    10 |    10 |     10 | No special handling instructions.
(10 rows)

מקלידים \q כדי לצאת מ-Cloud SQL.

main.py

אחרי שפותחים את הקבצים data_model.py, package-service.yaml ו-connect_connector.py, יוצרים main.py לאפליקציה.

הנחיה 1: באמצעות ספריית Python Flask – צור הטמעה שמשתמשת בנקודות קצה של HTTP REST עבור השירות הזה

הנחיה 2: באמצעות ספריית ה-flask של Python – צור הטמעה שמשתמשת בנקודות קצה של http rest עבור השירות הזה. ייבא את SessionMaker מ-connect_conector.py והשתמש בו כדי לארוז נתונים.

הנחיה 3: באמצעות ספריית ה-flask של Python – יוצרים הטמעה שמשתמשת בנקודות קצה של http rest עבור השירות הזה. מייבאים ומשתמשים בחבילה מ-data_model.py וב-SessionMaker מ-connect_conector.py כדי לקבל נתוני חבילות.

הנחיה 4: באמצעות ספריית ה-flask של Python – יוצרים הטמעה שמשתמשת בנקודות קצה של HTTP REST עבור השירות הזה. מייבאים ומשתמשים בחבילה מ-data_model.py וב-SessionMaker מ-connect_conector.py לנתוני חבילות. שימוש בכתובת ה-IP של המארח 0.0.0.0 עבור app.run

6d794fc52a90e6ae.png

צריך לעדכן את הדרישות של main.py.

הנחיה: צור קובץ דרישות עבור main.py

1cc0b318d2d4ca2f.png

מוסיפים את הטקסט הזה לקובץ requirements.txt. חשוב להשתמש בגרסה 3.0.0 של Flask.

משתמשים בOPEN a4c7ed6d845df343.png הקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם main.py.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

5. בדיקה והרצה של האפליקציה

מתקינים את הדרישות.

pip3 install -r requirements.txt

מריצים את main.py.

python main.py

הפלט אמור להיראות כך:

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://10.88.0.3:5000
Press CTRL+C to quit

ממסוף שני, בודקים את נקודת הקצה /packages/<product_id>.

curl localhost:5000/packages/1

הפלט אמור להיראות כך:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

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

מזינים CTRL_C כדי לצאת מהקונטיינר הפעיל של Docker בטרמינל.

יצירת בדיקות יחידה

פותחים את הקובץ main.py ומפיקים בדיקות יחידה.

הנחיה 1: יצירת בדיקות יחידה.

e861e5b63e1b2657.png

משתמשים ב-OPEN a4c7ed6d845df343.png בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם test.py.

בפונקציה test_get_package, צריך להגדיר product_id. אפשר להוסיף אותו באופן ידני.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

הרצת בדיקות יחידה

מריצים את בדיקת היחידה.

python test.py

הפלט אמור להיראות כך:

.
----------------------------------------------------------------------
Ran 1 test in 1.061s

OK

סוגרים את כל הקבצים ב-Cloud Shell Editor ומנקים את היסטוריית הצ'אט על ידי לחיצה על סמל הפח 1ecccfe10d6c540.png בסרגל הסטטוס העליון.

קובץ Docker

ליצור Dockerfile לאפליקציה הזו.

פותחים את main.py ומנסים את ההנחיות הבאות.

הנחיה 1: צור קובץ Dockerfile לאפליקציה הזו.

הנחיה 2: צור קובץ Dockerfile לאפליקציה הזו. מעתיקים את כל הקבצים למאגר.

9c473caea437a5c3.png

צריך גם להגדיר את ENVARS עבור INSTANCE_CONNECTION_NAME,‏ DB_USER,‏ DB_PASS ו-DB_NAME. אפשר לעשות את זה באופן ידני. קובץ ה-Dockerfile צריך להיראות כך:

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]

משתמשים ב-OPEN a4c7ed6d845df343.png בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם Dockerfile.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.

הפעלת האפליקציה באופן מקומי

אחרי שפותחים את Dockerfile, אפשר לנסות את ההנחיה הבאה.

הנחיה 1: איך מריצים באופן מקומי קונטיינר באמצעות קובץ Dockerfile הזה

570fd5c296ca8c83.png

פועלים לפי ההוראות.

# Build
docker build -t shipping .
# And run
docker run -p 5000:5000 -it shipping

הפלט אמור להיראות כך:

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.17.0.2:5000
Press CTRL+C to quit

מחלון טרמינל שני, ניגשים למאגר התגים.

curl localhost:5000/packages/1

הפלט אמור להיראות כך:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

האפליקציה בקונטיינר פועלת.

מזינים CTRL_C כדי לצאת מהקונטיינר הפעיל של Docker בטרמינל.

פיתוח קובץ אימג' של קונטיינר ב-Artifact Registry

יוצרים את קובץ האימג' של הקונטיינר ומעבירים אותו בדחיפה ל-Artifact Registry.

cd ~/shipping
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping .
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping

קונטיינר האפליקציה נמצא עכשיו במיקום us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping, שאפשר לפרוס אותו ב-GKE.

6. פריסת האפליקציה באשכול GKE

כשבניתם את משאבי ה-GCP לסדנה הזו, נוצר אשכול GKE Autopilot. מתחברים לאשכול GKE.

gcloud container clusters get-credentials gke1 \
    --region=us-central1

מוסיפים הערה לחשבון השירות שמוגדר כברירת מחדל ב-Kubernetes עם חשבון השירות של Google.

kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com

הפלט אמור להיראות כך:

serviceaccount/default annotated

מכינים ומחילים את הקובץ k8s.yaml.

cp ~/duetaidev/k8s.yaml_tmpl ~/shipping/.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export IMAGE_REPO=us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
envsubst < ~/shipping/k8s.yaml_tmpl > k8s.yaml
kubectl apply -f k8s.yaml

הפלט אמור להיראות כך:

deployment.apps/shipping created
service/shipping created

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

kubectl get pods
kubectl get service shipping

הפלט אמור להיראות כך:

# kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
shipping-f5d6f8d5-56cvk   1/1     Running   0          4m47s
shipping-f5d6f8d5-cj4vv   1/1     Running   0          4m48s
shipping-f5d6f8d5-rrdj2   1/1     Running   0          4m47s

# kubectl get service shipping
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
shipping   LoadBalancer   34.118.225.125   34.16.39.182   80:30076/TCP   5m41s

באשכולות GKE Autopilot, צריך להמתין כמה רגעים עד שהמשאבים יהיו מוכנים.

גישה לשירות דרך הכתובת EXTERNAL-IP.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

הפלט אמור להיראות כך:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

7. בונוס: פתרון בעיות באפליקציה

מסירים את תפקיד ה-IAM של לקוח CloudSQL מחשבון השירות cloudsqlsa. הפעולה הזו גורמת לשגיאה בחיבור למסד הנתונים של CloudSQL.

gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

מפעילים מחדש את ה-Pod של המשלוח.

kubectl rollout restart deployment shipping

אחרי שה-Pod מופעל מחדש, מנסים לגשת שוב לשירות shipping.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1 

הפלט אמור להיראות כך:

...
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>

כדי לבדוק את היומנים, עוברים אל Kubernetes Engine > Workloads

d225b1916c829167.png

לוחצים על הפריסה shipping ואז על הכרטיסייה Logs (יומנים).

1d0459141483d6a7.png

לוחצים על הסמל הצגה בכלי לבדיקת יומנים df8b9d19a9fe4c73.png בצד שמאל של שורת הסטטוס. ייפתח חלון חדש של Log Explorer.

e86d1c265e176bc4.png

לוחצים על אחת מהשגיאות Traceback ואז על Explain this Log Entry (הסבר על רשומה ביומן).

d6af045cf03008bc.png

אפשר לקרוא את ההסבר לשגיאה.

בשלב הבא, נבקש מ-Duet AI לעזור בפתרון הבעיה.

אפשר לנסות את ההנחיה הבאה.

הנחיה 1: עזרה בפתרון השגיאה הזו

9288dd6045369167.png

מזינים את הודעת השגיאה בהנחיה.

הנחיה 2: Forbidden: נראה שחשבון המשתמש המאומת ב-IAM לא מורשה לשלוח בקשה ל-API. צריך לוודא ש-Cloud SQL Admin API מופעל בפרויקט GCP ושניתן התפקיד Cloud SQL Client לחשבון הראשי ב-IAM

f1e64fbdc435d31c.png

ואז.

הנחיה 3: איך מקצים את התפקיד 'לקוח Cloud SQL' לחשבון שירות של Google באמצעות gcloud?

bb8926b995a8875c.png

מקצים את התפקיד Cloud SQL Client למשתמש cloudsqlsa.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

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

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

הפלט אמור להיראות כך:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

השתמשתם בהצלחה ב-Duet AI ב-Cloud Logging, ב-Log Explorer ובתכונה Log Explainer כדי לפתור את הבעיה.

8. סיכום

מעולה! סיימתם את ה-Codelab הזה.

ב-codelab הזה למדתם:

  1. מפעילים את Duet AI בפרויקט GCP ומגדירים אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
  2. שימוש ב-Duet AI ליצירה, להשלמה ולהסבר של קוד.
  3. להשתמש ב-Duet AI כדי להסביר ולפתור בעיה באפליקציה.
  4. תכונות של Duet AI כמו צ'אט IDE ושיחה עם זיכרון, צ'אט לעומת יצירת קוד בשורה, פעולות חכמות כמו הסבר על קוד ואישור של הקראת קוד ועוד.

9. נספח

package-service.yaml

swagger: "2.0"
info:
 title: Shipping and Package Information API
 description: This API provides information about shipping and packages.
 version: 1.0.0
host: shipping.googleapis.com
schemes:
 - https
produces:
 - application/json
paths:
 /packages/{product_id}:
   get:
     summary: Get information about a package
     description: This method returns information about a package, including its height, width, depth, weight, and any special handling instructions.
     parameters:
       - name: product_id
         in: path
         required: true
         type: integer
         format: int64
     responses:
       "200":
         description: A successful response
         schema:
           type: object
           properties:
             height:
               type: integer
               format: int64
             width:
               type: integer
               format: int64
             depth:
               type: integer
               format: int64
             weight:
               type: integer
               format: int64
             special_handling_instructions:
               type: string
       "404":
         description: The product_id was not found

data_model.py

from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base

from connect_connector import engine

Base = declarative_base()

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(Integer, nullable=False)
    height = Column(Float, nullable=False)
    width = Column(Float, nullable=False)
    depth = Column(Float, nullable=False)
    weight = Column(Float, nullable=False)
    special_handling_instructions = Column(String, nullable=True)

def create_tables():
    Base.metadata.create_all(engine)

if __name__ == '__main__':
    create_tables()

    print('Tables created successfully.')

connect_connector.py

import os

from google.cloud.sql.connector import Connector, IPTypes
import sqlalchemy

# You may need to manually import pg8000 and Base as follows
import pg8000
from sqlalchemy.ext.declarative import declarative_base


def connect_with_connector() -> sqlalchemy.engine.base.Engine:
   """Initializes a connection pool for a Cloud SQL instance of Postgres."""
   # Note: Saving credentials in environment variables is convenient, but not
   # secure - consider a more secure solution such as
   # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
   # keep secrets safe.
   instance_connection_name = os.environ[
       "INSTANCE_CONNECTION_NAME"
   ]  # e.g. 'project:region:instance'
   db_user = os.environ["DB_USER"]  # e.g. 'my-database-user'
   db_pass = os.environ["DB_PASS"]  # e.g. 'my-database-password'
   db_name = os.environ["DB_NAME"]  # e.g. 'my-database'

   ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

   connector = Connector()

   def getconn() -> sqlalchemy.engine.base.Engine:
       conn: sqlalchemy.engine.base.Engine = connector.connect(
           instance_connection_name,
           "pg8000",
           user=db_user,
           password=db_pass,
           db=db_name,
           ip_type=ip_type,
       )
       return conn

   pool = sqlalchemy.create_engine(
       "postgresql+pg8000://",
       creator=getconn,
       # ...
   )
   return pool

# Create a connection pool
engine = connect_with_connector()

# Create a sessionmaker class to create new sessions
SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine)

# Create a Base class for ORM
# You may need to manually fix the following
Base = declarative_base()

db_init.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from connect_connector import engine

from data_model import Package

def create_packages():
    # Create a session
    session = sessionmaker(bind=engine)()

    # Create 10 sample packages
    for i in range(10):
        package = Package(
            product_id=i,
            height=10.0,
            width=10.0,
            depth=10.0,
            weight=10.0,
            special_handling_instructions="No special handling instructions."
        )

        # Add the package to the session
        session.add(package)

    # Commit the changes
    session.commit()

if __name__ == '__main__':
    create_packages()

    print('Packages created successfully.')

main.py

from flask import Flask, request, jsonify

from data_model import Package
from connect_connector import SessionMaker

app = Flask(__name__)

session_maker = SessionMaker()

@app.route("/packages/<int:product_id>", methods=["GET"])
def get_package(product_id):
  """Get information about a package."""

  session = session_maker

  package = session.query(Package).filter(Package.product_id == product_id).first()

  if package is None:
    return jsonify({"message": "Package not found."}), 404

  return jsonify(
      {
          "height": package.height,
          "width": package.width,
          "depth": package.depth,
          "weight": package.weight,
          "special_handling_instructions": package.special_handling_instructions,
      }
  ), 200

if __name__ == "__main__":
  app.run(host="0.0.0.0")

test.py

import unittest

from data_model import Package
from connect_connector import SessionMaker

from main import app

class TestPackage(unittest.TestCase):

    def setUp(self):
        self.session_maker = SessionMaker()

    def tearDown(self):
        self.session_maker.close()

    def test_get_package(self):
        """Test the `get_package()` function."""

        package = Package(
        product_id=11, # Ensure that the product_id different from the sample data
        height=10,
        width=10,
        depth=10,
        weight=10,
        special_handling_instructions="Fragile",
        )

        session = self.session_maker

        session.add(package)
        session.commit()

        response = app.test_client().get("/packages/11")

        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.json,
            {
                "height": 10,
                "width": 10,
                "depth": 10,
                "weight": 10,
                "special_handling_instructions": "Fragile",
            },
        )

if __name__ == "__main__":
    unittest.main()

requirements.txt

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
Flask==3.0.0
gunicorn==20.1.0
psycopg2-binary==2.9.3

קובץ Docker

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]