‫AlloyDB Omni ו-EmbeddingGemma עם Gemma 4 ב-Kubernetes

1. מבוא

ב-Codelab הזה תלמדו איך לפרוס את AlloyDB Omni ב-Google Kubernetes Engine ‏ (GKE) ואיך להשתמש בו עם מודלים פתוחים כמו EmbeddingGemma ו-Gemma 4 להטמעות ולתחזיות. הפעלת מסד הנתונים והמודלים באותו אשכול מצמצמת את זמן האחזור ברשת ומונעת תלות בשירותים של צד שלישי. היא גם עוזרת לעמוד בדרישות התאימות ודרישות מיקום אחסון הנתונים, כי הנתונים אף פעם לא יוצאים מהסביבה שלכם.

דיאגרמת ארכיטקטורה של AlloyDB Omni ו-EmbeddingGemma ב-GKE

דרישות מוקדמות

  • הבנה בסיסית של Google Cloud ומסוף Google Cloud
  • ידע בסיסי ב-Kubernetes וב-GKE
  • היכרות עם ממשק שורת הפקודה ועם Google Cloud Shell

מה תלמדו

  • איך פורסים את AlloyDB Omni באשכול GKE
  • איך מתחברים ל-AlloyDB Omni
  • איך טוענים נתונים ל-AlloyDB Omni
  • איך פורסים מודלים של AI (הטמעה ו-LLM) ב-GKE
  • איך רושמים מודלים של AI ב-AlloyDB Omni
  • איך ליצור הטמעות לחיפוש סמנטי
  • איך מריצים שאילתות של חיפוש סמנטי ב-AlloyDB Omni
  • איך יוצרים ומשתמשים באינדקסים וקטוריים ב-AlloyDB Omni

הדרישות

  • חשבון ב-Google Cloud ופרויקט בענן של Google
  • דפדפן אינטרנט כמו Chrome

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

הגדרת הפרויקט

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

תיבת הדו-שיח לבחירת פרויקט במסוף Google Cloud

בחלון Select a project, לוחצים על New Project כדי לפתוח את תיבת הדו-שיח ליצירת פרויקט.

תיבת דו-שיח ליצירת פרויקט חדש

בתיבת הדו-שיח, מזינים שם פרויקט ובוחרים את הארגון או המיקום.

שדות להזנת פרטי הפרויקט

  • שם הפרויקט הוא השם המוצג למשתתפים בפרויקט. שם הפרויקט לא נמצא בשימוש ב-Google APIs, ואפשר לשנות אותו בכל שלב.
  • מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud ואי אפשר לשנות אותו אחרי שמגדירים אותו. מסוף Google Cloud יוצר מזהה ייחודי באופן אוטומטי, או שאתם יכולים לספק מזהה משלכם. ב-codelab הזה, מזהה הפרויקט מופיע כ-placeholder‏ .
  • מספר הפרויקט הוא מזהה שלישי שמשמש חלק מממשקי ה-API. מידע נוסף מופיע בתיעוד של מנהל המשאבים.

הפעלת החיוב

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

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

  • העלות של השלמת ה-Lab הזה היא פחות מ-5 $‎ על משאבי Google Cloud.
  • בסוף שיעור Lab הזה יש שלבים למחיקת משאבים כדי להימנע מחיובים נוספים.
  • משתמשים חדשים זכאים לתקופת ניסיון בחינם בשווי 300$.

הפעלת Cloud Shell

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

במסוף Google Cloud, לוחצים על סמל הפעלת Cloud Shell בסרגל הכלים שבפינה השמאלית העליונה:

הלחצן Activate Cloud Shell

אפשר גם להקיש על G ואז על S, או לפתוח את Google Cloud Shell ישירות.

כשמתחברים, Cloud Shell מציג את הנחיית הטרמינל:

צילום מסך של טרמינל Google Cloud Shell

‫Cloud Shell כולל אחסון מתמיד וכלים למפתחים. אפשר להריץ את כל השלבים ב-codelab הזה מהדפדפן.

3. הפעלת ממשקי ה-API

כדי להשתמש ב-Google Kubernetes Engine‏ (GKE) בשביל פריסות של AlloyDB Omni ומודלים, צריך להפעיל את ממשקי ה-API של Compute Engine ו-GKE בפרויקט בענן של Google.

ב-Cloud Shell, מוודאים שמזהה הפרויקט מוגדר:

PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID

אם מזהה הפרויקט לא מוגדר, צריך להגדיר אותו:

export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

מפעילים את ממשקי ה-API הנדרשים:

gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com

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

student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=test-project-001-402417
student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.

מידע על כל API שמופעל זמין בתיעוד.

4. פריסת AlloyDB Omni ב-GKE

כדי לפרוס את AlloyDB Omni ב-GKE, צריך להכין אשכול Kubernetes בהתאם לדרישות של אופרטור AlloyDB Omni.

יצירת אשכול GKE

פריסת אשכול GKE רגיל עם קיבולת להרצת AlloyDB Omni, האופרטור וקונטיינרים של ניטור. ‫AlloyDB Omni דורש לפחות שני מעבדים ו-8GB של זיכרון RAM. במדריך הזה נעשה שימוש בסוג המכונה n2-standard-4.

מגדירים את משתני הסביבה לפריסה:

export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=n2-standard-4

יוצרים את אשכול GKE הרגיל:

gcloud container clusters create ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION} \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --release-channel=rapid \
  --machine-type=${MACHINE_TYPE} \
  --num-nodes=1

הפלט הצפוי בקונסולה:

student@cloudshell:~ (test-project-001-402417)$ export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=n2-standard-4
Your active configuration is: [test-project-001-402417]
student@cloudshell:~ (test-project-001-402417)$ gcloud container clusters create ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION} \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --release-channel=rapid \
  --machine-type=${MACHINE_TYPE} \
  --num-nodes=1
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
Creating cluster alloydb-ai-gke in us-central1... Cluster is being health-checked (Kubernetes Control Plane is healthy)...done.                                                                                            
Created [https://container.googleapis.com/v1/projects/test-project-001-402417/zones/us-central1/clusters/alloydb-ai-gke].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1/alloydb-ai-gke?project=test-project-001-402417
kubeconfig entry generated for alloydb-ai-gke.
NAME: alloydb-ai-gke
LOCATION: us-central1
MASTER_VERSION: 1.36.3-gke.1640000
MASTER_IP: 34.121.243.65
MACHINE_TYPE: n2-standard-4
NODE_VERSION: 1.36.3-gke.1640000
NUM_NODES: 3
STATUS: RUNNING
STACK_TYPE: IPV4

הכנת האשכול

מתקינים את הרכיבים הנדרשים, כמו cert-manager, בקר האישורים המקורי של Kubernetes. פרטים נוספים זמינים במסמכי התיעוד בנושא התקנת cert-manager.

‫Cloud Shell כולל את כלי שורת הפקודה של Kubernetes‏ kubectl. מקבלים פרטי כניסה לאשכול באמצעות gcloud:

gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}

התקנת cert-manager באמצעות kubectl:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yaml

הפלט הצפוי בקונסולה (הושמטו פרטים):

student@cloudshell:~$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yaml
namespace/cert-manager created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
...
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created

התקנת אופרטור AlloyDB Omni

מתקינים את אופרטור AlloyDB Omni באמצעות Helm.

מורידים ומתקינים את תרשים האופרטור AlloyDB Omni:

helm install alloydbomni-operator oci://gcr.io/alloydb-omni/alloydbomni-operator \
--version 1.8.1 \
--create-namespace \
--namespace alloydb-omni-system \
--atomic \
--timeout 5m

הפלט הצפוי בקונסולה (הושמטו פרטים):

student@cloudshell:~$ helm install alloydbomni-operator oci://gcr.io/alloydb-omni/alloydbomni-operator \
> --version 1.8.0 \
> --create-namespace \
> --namespace alloydb-omni-system \
> --atomic \
> --timeout 5m
Flag --atomic has been deprecated, use --rollback-on-failure instead
Pulled: gcr.io/alloydb-omni/alloydbomni-operator:1.8.0
Digest: sha256:f2d98fa7a3b08dfc1e83b811582718b94e5c017b81aade700c83e917c59f0395
NAME: alloydbomni-operator
LAST DEPLOYED: Thu Aug 27 17:57:30 2026
NAMESPACE: alloydb-omni-system
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None

פורסים את אשכול מסד הנתונים.

קובץ המניפסט הבא מגדיר אשכול מסדי נתונים עם googleMLExtension מופעל ומאזן עומסים פנימי:

cat << 'EOF' > my-omni.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-pw-my-omni
type: Opaque
data:
  my-omni: "VmVyeVN0cm9uZ1Bhc3N3b3Jk"
---
apiVersion: alloydbomni.dbadmin.goog/v1
kind: DBCluster
metadata:
  name: my-omni
spec:
  databaseVersion: "18.3.0"
  primarySpec:
    adminUser:
      passwordRef:
        name: db-pw-my-omni
    features:
      googleMLExtension:
        enabled: true
    resources:
      cpu: 1
      memory: 8Gi
      disks:
      - name: DataDisk
        size: 20Gi
        storageClass: standard
    dbLoadBalancerOptions:
      annotations:
        networking.gke.io/load-balancer-type: "internal"
  allowExternalIncomingTraffic: true
EOF

הערך הסודי של הסיסמה הוא ייצוג Base64 של VeryStrongPassword. בסביבות ייצור, מומלץ להשתמש ב-Google Secret Manager כדי לנהל סיסמאות. פרטים נוספים זמינים במסמכי התיעוד של Secret Manager.

קובץ המניפסט נשמר בשם my-omni.yaml. ב-Cloud Shell, לוחצים על Open Editor (פתיחת העורך) בפינה השמאלית העליונה של חלון הטרמינל וקוראים את הקובץ.

פתיחת העורך ב-Cloud Shell

אחרי שקוראים את my-omni.yaml המניפסט, לוחצים על Open Terminal (פתיחת הטרמינל) כדי לחזור לשורת הפקודה.

פתיחת הטרמינל ב-Cloud Shell

החלת מניפסט my-omni.yaml:

kubectl apply -f my-omni.yaml

הפלט הצפוי בקונסולה:

secret/db-pw-my-omni created
dbcluster.alloydbomni.dbadmin.goog/my-omni created

בודקים את הסטטוס של אשכול my-omni:

kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default

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

הפלט הצפוי בקונסולה:

$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
NAME      PRIMARYENDPOINT   PRIMARYPHASE   DBCLUSTERPHASE   HAREADYSTATUS   HAREADYREASON
my-omni   10.131.0.33        Ready          DBClusterReady

אפשר גם לעקוב אחר פריסת האשכול באמצעות הפקודה kubectl log:

kubectl logs -l alloydbomni.internal.dbadmin.goog/dbcluster=my-omni --all-containers -f

התחברות ל-AlloyDB Omni

כשהאשכול מוכן, מתחברים ל-pod של מסד הנתונים באמצעות לקוח PostgreSQL ‏ (psql). הסיסמה היא VeryStrongPassword כפי שמוגדר ב-my-omni.yaml:

DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres

פלט לדוגמה בקונסולה:

DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres
Password for user postgres:
psql (18.3)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_128_GCM_SHA256, compression: off, ALPN: postgresql)
Type "help" for help.

postgres=#

כדי לצאת מהסשן של psql, מקלידים ‎\q ומקישים על Enter:

postgres=# \q

5. פריסת מודל EmbeddingGemma ב-GKE

כדי לבדוק את השילוב של AlloyDB Omni AI עם מודלים מקומיים, פורסים מודל הטמעה באשכול GKE. במדריך הזה נעשה שימוש במודל EmbeddingGemma של Google.

יצירת מאגר צמתים למודל

כדי להריץ היקש של מודל, צריך להכין מאגר צמתים ייעודי. אפשר להשתמש במאגר צמתים עם מעבד בלבד או במאגר צמתים עם האצת GPU (כמו g2-standard-8 עם GPU של NVIDIA L4). במדריך הזה נעשה שימוש במאגר צמתים מבוסס-CPU עם סוגי מכונות c3-standard-8.

יוצרים מאגר צמתים של מעבדי CPU עם צומת יחיד:

export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create cpupool \
  --project=${PROJECT_ID} \
  --location=${LOCATION} \
  --node-locations=${LOCATION}-a \
  --cluster=${CLUSTER_NAME} \
  --machine-type=c3-standard-8 \
  --num-nodes=1

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

student@cloudshell$ export PROJECT_ID=$(gcloud config get project)
Your active configuration is: [pant]
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
student@cloudshell$ gcloud container node-pools create cpupool \
>   --project=${PROJECT_ID} \
>   --location=${LOCATION} \
>   --node-locations=${LOCATION}-a \
>   --cluster=${CLUSTER_NAME} \
>   --machine-type=c3-standard-8 \
>   --num-nodes=1
Creating node pool cpupool...done.
Created [https://container.googleapis.com/v1/projects/gleb-test-short-003-483115/zones/us-central1/clusters/alloydb-ai-gke/nodePools/cpupool].
NAME     MACHINE_TYPE    DISK_SIZE_GB  NODE_VERSION
cpupool  c3-standard-8  100           1.34.1-gke.3355002

קבלת טוקן של Hugging Face

במדריך הזה נסביר איך לפרוס את מודל EmbeddingGemma מ-Hugging Face. כדי לגשת למשקלים של המודל, צריך ליצור טוקן גישה ל-Hugging Face:

  1. נכנסים לחשבון ב-Hugging Face או יוצרים חשבון.
  2. עוברים אל הפרופיל שלך > אסימוני גישה.
  3. לוחצים על יצירת טוקן חדש.
  4. מזינים שם לטוקן ובוחרים את התפקיד קריאה.
  5. לוחצים על Create token (יצירת טוקן) ומעתיקים את ערך הטוקן שנוצר.
  6. אם עוד לא עשיתם את זה, אתם צריכים לאשר את תנאי המודל בדף המודל EmbeddingGemma.

יוצרים סוד של Kubernetes שמכיל את האסימון של Hugging Face ב-Cloud Shell (מחליפים את placeholder האסימון באסימון שלכם):

export HF_TOKEN=<YOUR_HUGGING_FACE_TOKEN>
kubectl create secret generic hf-secret \
  --from-literal=hf_api_token=$HF_TOKEN \
  --dry-run=client -o yaml | kubectl apply -f -

הכנת מניפסט הפריסה

כדי לפרוס את המודל, משתמשים בחבילת הקונטיינר Text Embeddings Inference ‏ (TEI) של Hugging Face. מידע נוסף זמין במאמרי העזרה בנושא Hugging Face GKE TEI.

משכפלים את מאגר הפריסה מ-GitHub:

git clone https://github.com/huggingface/Google-Cloud-Containers

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

edit Google-Cloud-Containers/examples/gke/tei-deployment/cpu-config/deployment.yaml

קובץ המניפסט המעודכן לפריסת CPU:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tei-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tei-server
  template:
    metadata:
      labels:
        app: tei-server
        hf.co/model: Google--embeddinggemma-300m
        hf.co/task: text-embeddings
    spec:
      containers:
        - name: tei-container
          image: ghcr.io/huggingface/text-embeddings-inference:cpu-latest
          resources:
            requests:
              cpu: "6"
              memory: "24Gi"
            limits:
              cpu: "6"
              memory: "24Gi"
          env:
            - name: MODEL_ID
              value: google/embeddinggemma-300m
            - name: NUM_SHARD
              value: "1"
            - name: PORT
              value: "8080"
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
          volumeMounts:
            - mountPath: /tmp
              name: tmp
      volumes:
        - name: tmp
          emptyDir: {}
      nodeSelector:
        cloud.google.com/machine-family: "c3"

מקישים על ctrl+s כדי לשמור את השינויים וחוזרים למסוף.

פריסת המודל

מחילים את המניפסט כדי לפרוס את שרת ה-TEI:

kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/cpu-config

עוקבים אחרי הפריסה עד שהיא מגיעה למצב מוכן:

printf "Waiting for model to load..."; until kubectl logs -l app=tei-server --tail=50 2>/dev/null | grep -q "Ready"; do printf "."; sleep 3; done; printf '\n\033[1;32m========================================\n[SUCCESS] Model is loaded and ready!\nYou can now proceed to the next step.\n========================================\033[0m\n'

בודקים את שירות Kubernetes:‏ tei-service

kubectl get service tei-service

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

student@cloudshell$ kubectl get service tei-service
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
tei-service   ClusterIP   34.118.233.48   <none>        8080/TCP   10m

השירות CLUSTER-IP מטפל בבקשות באופן פנימי בכתובת http://34.118.233.48:8080/embed.

בודקים את נקודת הקצה של המודל באופן מקומי באמצעות kubectl port-forward:

kubectl port-forward service/tei-service 8080:8080

פותחים כרטיסייה שנייה ב-Cloud Shell על ידי לחיצה על + בחלק העליון של הטרמינל:

הוספת כרטיסיית Cloud Shell

בכרטיסייה החדשה, בודקים את הטמעת הדור עם curl:

curl http://localhost:8080/embed \
  -X POST \
  -d '{"inputs":"Test"}' \
  -H 'Content-Type: application/json'

הפלט הצפוי (מערך וקטורים):

curl http://localhost:8080/embed \
>     -X POST \
>     -d '{"inputs":"Test"}' \
>     -H 'Content-Type: application/json'
[[-0.018975832,0.0071419072,0.06347208,0.022992613,0.014205903
...
-0.03677433,0.01636146,0.06731572]]

עוצרים את העברת הפורטים בכרטיסייה הראשונה בלחיצה על ctrl+c.

6. רישום מודל ההטמעה ב-AlloyDB Omni

כדי להשתמש במודל שנפרס מ-AlloyDB Omni, צריך ליצור מסד נתונים, להגדיר פונקציות טרנספורמציה ולרשום את נקודת הקצה של המודל.

יצירת מכונה וירטואלית של לקוח ומסד נתונים

יוצרים מכונה וירטואלית ב-Compute Engine באותו VPC שתשמש כמארח קפיצה של לקוח:

דיאגרמת ארכיטקטורה של הרשת שמציגה מכונה וירטואלית של לקוח ו-AlloyDB Omni

ב-Cloud Shell, יוצרים את המכונה הווירטואלית של הלקוח:

export ZONE=us-central1-a
gcloud compute instances create instance-1 \
  --zone=$ZONE

מאחזרים את כתובת ה-IP של נקודת הקצה של AlloyDB Omni:

echo "INSTANCE_IP=$(kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default -o jsonpath='{.status.primary.endpoint}')"

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

INSTANCE_IP=10.128.0.33

הערך של INSTANCE_IP הוא כתובת ה-IP של מאזן העומסים הפנימי עבור אשכול AlloyDB Omni. בדוגמה הזו, זה 10.131.0.33.

מתחברים למופע של המכונה הווירטואלית באמצעות SSH:

gcloud compute ssh instance-1 --zone=$ZONE

בסשן ה-SSH ב-instance-1, מתקינים את לקוח PostgreSQL:

sudo apt-get update && sudo apt-get install --yes postgresql-client 

מייצאים את כתובת ה-IP של מאזן העומסים של AlloyDB Omni (מחליפים בכתובת ה-IP שלכם PRIMARYENDPOINT):

export INSTANCE_IP=10.131.0.33

מתחברים ל-AlloyDB Omni באמצעות psql (הסיסמה היא VeryStrongPassword):

psql "host=$INSTANCE_IP user=postgres sslmode=require"

בסשן psql, יוצרים את מסד הנתונים demo:

CREATE DATABASE demo;

עוברים למסד הנתונים demo:

\c demo

יצירת פונקציות לשינוי

נקודות קצה מותאמות אישית להטמעה דורשות פונקציות של טרנספורמציה של קלט ופלט כדי להתאים את פורמטי הנתונים בין AlloyDB Omni לבין ה-API של המודל.

יוצרים את פונקציית השינוי של הקלט:

CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_input JSON;
BEGIN
  SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
  RETURN transformed_input;
END;
$$;

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

demo=# CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_input JSON;
BEGIN
  SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
  RETURN transformed_input;
END;
$$;
CREATE FUNCTION
demo=#

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

CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_output REAL[];
BEGIN
  SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
  RETURN transformed_output;
END;
$$;

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

demo=# CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_output REAL[];
BEGIN
  SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
  RETURN transformed_output;
END;
$$;
CREATE FUNCTION
demo=#

רישום המודל

רושמים את המודל ב-AlloyDB Omni באמצעות ההליך google_ml.create_model. מציינים את http://tei-service:8080/embed בתור model_request_url כדי לנתב בקשות לשירות של אשכול Kubernetes:

CALL
  google_ml.create_model(
    model_id => 'embeddinggemma',
    model_request_url => 'http://tei-service:8080/embed',
    model_provider => 'custom',
    model_type => 'text_embedding',
    model_in_transform_fn => 'tei_text_input_transform',
    model_out_transform_fn => 'tei_text_output_transform');

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

demo=# CALL
  google_ml.create_model(
    model_id => 'embeddinggemma',
    model_request_url => 'http://tei-service:8080/embed',
    model_provider => 'custom',
    model_type => 'text_embedding',
    model_in_transform_fn => 'tei_text_input_transform',
    model_out_transform_fn => 'tei_text_output_transform');
CALL
demo=#

בודקים את המודל הרשום באמצעות שאילתת SQL לדוגמה:

SELECT google_ml.embedding('embeddinggemma', 'What is AlloyDB Omni?');

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

לוחצים על q כדי לחזור להנחיה של סשן psql.

כדי לצאת מהסשן של psql:

\q

7. בדיקת המודל באמצעות נתונים לדוגמה

טעינת נתונים לדוגמה

במדריך הזה נעשה שימוש במערך הנתונים הקמעונאי של Cymbal כדי להדגים חיפוש של דמיון וקטורי. תשתמשו ב-Google Cloud SDK ובלקוח PostgreSQL כדי לייבא נתונים ל-AlloyDB Omni.

בסשן ה-SSH ב-instance-1, מתחברים למסד הנתונים של ההדגמה ומפעילים את התוסף vector:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

בסשן psql:

CREATE EXTENSION IF NOT EXISTS vector;

יוצאים מהסשן של psql:

\q

מורידים את הסכימה ומחילים אותה כדי ליצור טבלאות במסד הנתונים demo:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"

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

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"
Password for user postgres:
SET
SET
SET
SET
SET
 set_config
------------

(1 row)

SET
SET
SET
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE SEQUENCE
ALTER TABLE
ALTER SEQUENCE
ALTER TABLE
ALTER TABLE
ALTER TABLE
student@cloudshell:~$

בודקים את הטבלאות שנוצרו:

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"

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

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
Password for user postgres: 
                                           List of relations
 Schema |       Name       | Type  |  Owner   | Persistence | Access method |    Size    | Description 
--------+------------------+-------+----------+-------------+---------------+------------+-------------
 public | cymbal_embedding | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_inventory | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_products  | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_stores    | table | postgres | permanent   | heap          | 8192 bytes | 
(4 rows)

טוענים נתונים לטבלה cymbal_products:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"

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

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"
COPY 941
student@cloudshell:~$ 

הנה דוגמה לכמה שורות מהטבלה cymbal_products.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"

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

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"
Password for user postgres: 
             uniq_id              |              left              |                        left                        | sale_price 
----------------------------------+--------------------------------+----------------------------------------------------+------------
 a73d5f754f225ecb9fdc64232a57bc37 | Laundry Tub Strainer Cup       |   Laundry tub strainer cup Chrome For 1-.50, drain |      11.74
 41b8993891aa7d39352f092ace8f3a86 | LED Starry Star Night Light La |  LED Starry Star Night Light Laser Projector 3D Oc |      46.97
 ed4a5c1b02990a1bebec908d416fe801 | Surya Horizon HRZ-1060 Area Ru |  The 100% polypropylene construction of the Surya  |       77.4
(3 rows)
student@cloudshell:~$ 

טוענים נתונים לטבלה cymbal_inventory:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"

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

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"
Password for user postgres: 
COPY 263861
student@cloudshell:~$ 

הנה דוגמה לכמה שורות מהטבלה cymbal_inventory.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"

פלט:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
Password for user postgres: 
 store_id |             uniq_id              | inventory 
----------+----------------------------------+-----------
     1583 | adc4964a6138d1148b1d98c557546695 |         5
     1490 | adc4964a6138d1148b1d98c557546695 |         4
     1492 | adc4964a6138d1148b1d98c557546695 |         3
(3 rows)
student@cloudshell:~$ 

טוענים נתונים לטבלה cymbal_stores:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"

הפלט הצפוי בקונסולה:

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"
Password for user postgres: 
COPY 4654
student@cloudshell:~$

הנה דוגמה לכמה שורות מהטבלה cymbal_stores.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"

פלט:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
Password for user postgres: 
 store_id |       name        | zip_code 
----------+-------------------+----------
     1990 | Mayaguez Store    |      680
     2267 | Ware Supercenter  |     1082
     4359 | Ponce Supercenter |      780
(3 rows)
student@cloudshell:~$ 

יצירת הטמעות

מתחברים למסד הנתונים של ההדגמה באמצעות psql ויוצרים הטמעות למוצרים שמתוארים בטבלה cymbal_products על סמך תיאורי המוצרים.

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

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

בעמודה embedding מסוג vector מאחסנים את הטמעות הטקסט שנוצרו לתיאורי מוצרים.

הפעלת תזמון שאילתות:

\timing

יוצרים הטבעות לכל תיאור מוצר ומאחסנים אותן בטבלה cymbal_embedding:

INSERT INTO cymbal_embedding (uniq_id, embedding)
SELECT uniq_id, google_ml.embedding('embeddinggemma', product_description)::vector
FROM cymbal_products;

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

demo=# INSERT INTO cymbal_embedding(uniq_id,embedding)  SELECT uniq_id, google_ml.embedding('embeddinggemma',product_description)::vector FROM cymbal_products;
INSERT 0 941
Time: 497878.136 ms (08:17.878)
demo=#

הרצת שאילתות של חיפוש סמנטי

בסשן psql, מוצאים את חמשת המוצרים המובילים שתואמים לשאלה "What kind of fruit trees grow well here?" באמצעות מרחק קוסינוס (<=>):

SELECT
    cp.product_name,
    left(cp.product_description, 80) AS description,
    cp.sale_price,
    cs.zip_code,
    (ce.embedding <=> google_ml.embedding('embeddinggemma', 'What kind of fruit trees grow well here?')::vector) AS distance
FROM
    cymbal_products cp
JOIN cymbal_embedding ce ON ce.uniq_id = cp.uniq_id
JOIN cymbal_inventory ci ON ci.uniq_id = cp.uniq_id
JOIN cymbal_stores cs ON cs.store_id = ci.store_id
WHERE
    ci.inventory > 0
    AND cs.store_id = 1583
ORDER BY
    distance ASC
LIMIT 5;

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

demo=# SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        (ce.embedding <=> google_ml.embedding('embeddinggemma','What kind of fruit trees grow well here?')::vector) as distance
FROM
        cymbal_products cp
JOIN cymbal_embedding ce on ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on cs.store_id=ci.store_id
WHERE
    ci.inventory > 0
    AND cs.store_id = 1583
ORDER BY
    distance ASC
LIMIT 5;
     product_name      |                                   description                                    | sale_price | zip_code |      distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------
 Cherry Tree           | This is a beautiful cherry tree that will produce delicious cherries. It is an d |      75.00 |    93230 | 0.5210549378080666
 California Lilac      | This is a beautiful lilac tree that can grow to be over 10 feet tall. It is an d |       5.00 |    93230 | 0.5639421771781971
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.5670010914504852
 Rose Bush             | This is a beautiful rose bush that will produce fragrant roses. It is a perennia |      50.00 |    93230 | 0.5731542622882957
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.5750934653011995
(5 rows)

Time: 83.610 ms
demo=#

השאילתה רצה במשך 83 אלפיות השנייה והחזירה רשימה של עצים מהטבלה cymbal_products שתואמים לבקשה, ושיש מלאי שלהם בחנות מספר 1583.

יצירת אינדקס של רשת עצבית מלאכותית

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

אם החיבור למסד הנתונים של ההדגמה אבד, צריך להתחבר אליו מחדש:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

מפעילים את התוסף alloydb_scann:

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

יוצרים את אינדקס ScaNN בעמודה embedding:

CREATE INDEX cymbal_products_embeddings_scann ON cymbal_embedding
  USING scann (embedding cosine)
  WITH (num_leaves=10, max_num_levels = 1);

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

SELECT
    cp.product_name,
    left(cp.product_description, 80) AS description,
    cp.sale_price,
    cs.zip_code,
    (ce.embedding <=> google_ml.embedding('embeddinggemma', 'What kind of fruit trees grow well here?')::vector) AS distance
FROM
    cymbal_products cp
JOIN cymbal_embedding ce ON ce.uniq_id = cp.uniq_id
JOIN cymbal_inventory ci ON ci.uniq_id = cp.uniq_id
JOIN cymbal_stores cs ON cs.store_id = ci.store_id
WHERE
    ci.inventory > 0
    AND cs.store_id = 1583
ORDER BY
    distance ASC
LIMIT 5;

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

demo=# SELECT
    cp.product_name,
    left(cp.product_description,80) as description,
    cp.sale_price,
    cs.zip_code,
    (ce.embedding <=> google_ml.embedding('embeddinggemma', 'What kind of fruit trees grow well here?')::vector) AS distance
FROM
    cymbal_products cp
JOIN cymbal_embedding ce ON ce.uniq_id = cp.uniq_id
JOIN cymbal_inventory ci ON ci.uniq_id = cp.uniq_id
JOIN cymbal_stores cs ON cs.store_id = ci.store_id
WHERE
    ci.inventory > 0
    AND cs.store_id = 1583
ORDER BY
    distance ASC
LIMIT 5;
     product_name      |                                   description                                    | sale_price | zip_code |      distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------
 Cherry Tree           | This is a beautiful cherry tree that will produce delicious cherries. It is an d |      75.00 |    93230 | 0.5210549378080666
 California Lilac      | This is a beautiful lilac tree that can grow to be over 10 feet tall. It is an d |       5.00 |    93230 | 0.5639421771781971
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.5670010914504852
 Rose Bush             | This is a beautiful rose bush that will produce fragrant roses. It is a perennia |      50.00 |    93230 | 0.5731542622882957
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.5750934653011995
(5 rows)

Time: 64.783 ms

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

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

יציאה מסשן psql:

\q

כדי לחזור אל Google Cloud Shell, מתנתקים מהסשן של instance-1 SSH על ידי הקשה על CTRL+D או הקלדה של exit.

8. פריסת Gemma באמצעות vLLM

הוספת מאגר צמתים ל-Gemma

קודם כל, בודקים אילו סוגי צמתים זמינים באזור שלכם:

export LOCATION=us-central1-a
gcloud compute accelerator-types list --filter="zone:${LOCATION}"

תוצג רשימה של סוגי מאיצים זמינים, כולל מאיץ nvidia-l4. עכשיו יוצרים מאגר צמתים עם סוג המאיץ nvidia-l4:

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create gpupool \
    --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
    --project=${PROJECT_ID} \
    --location=${LOCATION} \
    --node-locations=${LOCATION}-a \
    --cluster=${CLUSTER_NAME} \
    --machine-type=g2-standard-8 \
    --num-nodes=1

יוצרים מניפסט פריסה למודל Google Gemini 4 12B באמצעות vLLM:

cat << 'EOF' > gemma-12b-gpu-vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gemma-12b-gpu-vllm-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gemma-12b-gpu-vllm
  template:
    metadata:
      labels:
        app: gemma-12b-gpu-vllm
        ai.gke.io/model: gemma-4-12b-it
        ai.gke.io/inference-server: vllm
        examples.ai.gke.io/source: user-guide
    spec:
      containers:
      - name: inference-server
        image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:latest
        resources:
          requests:
            cpu: "4"
            memory: "16Gi"
            ephemeral-storage: "30Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "8"
            memory: "24Gi"
            ephemeral-storage: "30Gi"
            nvidia.com/gpu: "1"
        command: ["python3", "-m", "vllm.entrypoints.api_server"]
        args:
          - --model=$(MODEL_ID)
          - --host=0.0.0.0
          - --port=8000
          - --tensor-parallel-size=1
          - --enable-log-requests
          - --enable-chunked-prefill
          - --enable-prefix-caching
          - --enable-auto-tool-choice
          - --generation-config=auto
          - --tool-call-parser=gemma4
          - --dtype=bfloat16
          - --max-num-seqs=16
          - --max-model-len=32768
          - --gpu-memory-utilization=0.95
          - --reasoning-parser=gemma4
          - --trust-remote-code
          - --quantization=fp8
        env:
        - name: LD_LIBRARY_PATH
          value: ${LD_LIBRARY_PATH}:/usr/local/nvidia/lib64
        - name: MODEL_ID
          value: google/gemma-4-12b-it
        - name: HF_TOKEN
          valueFrom:
            secretKeyRef:
              name: hf-secret
              key: hf_api_token
        volumeMounts:
        - mountPath: /dev/shm
          name: dshm
      volumes:
      - name: dshm
        emptyDir:
            medium: Memory
      nodeSelector:
        cloud.google.com/gke-accelerator: nvidia-l4
        cloud.google.com/gke-gpu-driver-version: latest
---
apiVersion: v1
kind: Service
metadata:
  name: gemma-12b-gpu-vllm-service
spec:
  selector:
    app: gemma-12b-gpu-vllm
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000
EOF

מחילים את הפריסה gemma-12b-gpu-vllm-deployment.yaml שנשמרה:

kubectl apply -f gemma-12b-gpu-vllm-deployment.yaml

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

$ kubectl apply -f gemma-12b-gpu-vllm-deployment.yaml
deployment.apps/gemma-12b-gpu-vllm-deployment created
service/gemma-12b-gpu-vllm-service created

מחכים עד שהפריסה תסתיים והמודל ייטען. הפעולה עשויה להימשך כמה דקות.

printf "Waiting for model to load..."; until kubectl logs -l app=gemma-12b-gpu-vllm --tail=50 2>/dev/null | grep -q "Application startup complete"; do printf "."; sleep 3; done; printf '\n\033[1;32m========================================\n[SUCCESS] Model is loaded and ready!\nYou can now proceed to the next step.\n========================================\033[0m\n'

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

Waiting for model to load...
========================================
[SUCCESS] Model is loaded and ready!
You can now proceed to the next step.
========================================

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

kubectl port-forward svc/gemma-12b-gpu-vllm-service 8090:8000

בחלון מסוף אחר, משתמשים ב-curl כדי לשלוח פרומפט למודל:

curl http://localhost:8090/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "system", "content": "You are a helpful assistant running on GKE."},
      {"role": "user", "content": "What is AlloyDB Omni."}
    ],
    "temperature": 0.7
  }' | jq -r '.choices[0].message.content'

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

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3957  100  3761  100   196     85      4  0:00:49  0:00:43  0:00:06   830
**AlloyDB Omni** is a fully managed, PostgreSQL-compatible database engine from Google Cloud that can be run **on-premises, in other clouds, or in your own data centers.**

To understand it simply: It allows you to run the high-performance, enterprise-grade capabilities of Google's **AlloyDB** (a cloud-native database) on your own infrastructure.

Here is a breakdown of what makes it significant:

### 1. The "Best of Both Worlds" Architecture
Normally, you have to choose between:
*   **Managed Cloud Databases:** Easy to scale and manage, but you are locked into the cloud provider's infrastructure.
*   **Self-Managed Databases:** You have full control over the hardware/location, but you are responsible for scaling, patching, and high availability.

**AlloyDB Omni** bridges this gap. It provides the advanced features of a cloud-native database (like intelligent indexing, high availability, and massive scalability) while allowing you to run it anywhere.

אם העברת הפורטים עדיין פועלת, לוחצים על Ctrl+C כדי להפסיק אותה במסוף הראשון.

9. רישום מודל Gemma 4 ב-AlloyDB Omni

רושמים את מודל Gemma 12B ב-AlloyDB Omni באמצעות ההליך google_ml.create_model. מציינים את http://gemma-12b-gpu-vllm-service:8000/v1/chat/completions בתור model_request_url כדי לנתב בקשות לשירות של אשכול Kubernetes:

מאחזרים את כתובת ה-IP של נקודת הקצה של AlloyDB Omni:

echo "INSTANCE_IP=$(kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default -o jsonpath='{.status.primary.endpoint}')"

מתחברים למופע של המכונה הווירטואלית באמצעות SSH:

export ZONE=us-central1-a
gcloud compute ssh instance-1 --zone=$ZONE

אחרי שמתחברים ל-VM, מייצאים את המשתנה INSTANCE_IP מהשלב הקודם (הערך 10.128.0.33 ניתן כדוגמה – צריך להחליף אותו בכתובת ה-IP שלכם):

export INSTANCE_IP=10.128.0.33

ייצוא סיסמה של AlloyDB:

export PGPASSWORD=VeryStrongPassword

מתחברים למסד הנתונים של demo:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

בסשן psql, רושמים את המודל:

CALL
  google_ml.create_model(
    model_id => 'gemma-12b-gpu',
    model_request_url => 'http://gemma-12b-gpu-vllm-service:8000/v1/chat/completions',
    model_provider => 'custom',
    model_type => 'llm');

בודקים את המודל באמצעות שאילתת SQL לדוגמה:

SELECT google_ml.predict_row(
  model_id => 'gemma-12b-gpu',
  request_body => json_build_object(
    'messages', json_build_array(
      json_build_object('role', 'user', 'content', 'What is AlloyDB Omni?'))))->'choices'->0->'message'->'content';

מקישים על q כדי לצאת מחלון התוצאות ולחזור להנחיה psql.

שילוב של חיפוש וקטורי עם LLM RAG ב-AlloyDB Omni

משתמשים בחיפוש וקטורי עם בקשת LLM כדי להדגים RAG (יצירה משולבת-אחזור) עם LLM.

מריצים את שאילתת ה-SQL ב-plsql:

WITH trees AS (
SELECT
        cp.product_name,
        cp.product_description AS description,
        cp.sale_price,
        cs.zip_code,
        cp.uniq_id AS product_id
FROM
    cymbal_products cp
JOIN cymbal_embedding ce ON ce.uniq_id = cp.uniq_id
JOIN cymbal_inventory ci ON ci.uniq_id = cp.uniq_id
JOIN cymbal_stores cs ON cs.store_id = ci.store_id
WHERE
        ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        (ce.embedding <=> embedding('embeddinggemma',
        'What kind of fruit trees grow well here?')::vector) ASC
LIMIT 1),
prompt AS (
SELECT
        'You are a friendly advisor helping to find a product based on the customer''s needs.
Based on the client request we have loaded a list of products closely related to search.
The list in JSON format with list of values like {"product_name":"name","product_description":"some description","sale_price":10}
Here is the list of products:' || json_agg(trees) || 'The customer asked "What kind of fruit trees grow well here?"
You should give information about the product, price and some supplemental information' AS prompt_text
FROM
        trees),
response AS (
SELECT
        google_ml.predict_row(
          model_id =>'gemma-12b-gpu',
          request_body => json_build_object(
            'messages', json_build_array(
              json_build_object('role', 'user', 'content',prompt_text)
            )))->'choices'->0->'message'->'content' AS resp
FROM
        prompt)
SELECT
REPLACE(resp::text, '\n', CHR(10))
FROM
        response;

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

----------------------------------------------------------------------------------------------------------------------------------------------
 "Hello there! I'd be happy to help you find the perfect tree for your garden.                                                               +
                                                                                                                                             +
 Based on your location, we have a wonderful option that would grow beautifully in your area:                                                +
                                                                                                                                             +
 **Cherry Tree**                                                                                                                             +
 *   **Price:** $75.00                                                                                                                       +
 *   **Description:** This is a stunning deciduous tree that not only provides a beautiful landscape but also produces delicious cherries.   +
 *   **Supplemental Information:**                                                                                                           +
     *   **Growth:** It grows to about 15 feet tall.                                                                                         +
     *   **Appearance:** You can look forward to dark green leaves in the summer that transform into a vibrant red in the fall.              +
     *   **Benefits:** It's a great choice if you're looking for both fruit and extra shade or privacy in your yard.                         +
     *   **Care Tips:** It performs best in a cool, moist climate with sandy soil. Since you are in a suitable zone, it should thrive nicely!+
                                                                                                                                             +
 Would you like more details on how to plant this, or would you like to proceed with an order?"
(1 row)

השאילתה משלימה את הפרומפט ל-LLM באמצעות תוצאות החיפוש הווקטורי.

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

יציאה מהסשן של psql:

\q

מתנתקים מסשן ה-SSH למכונה הווירטואלית:

exit

אל תשכחו של-AlloyDB Omni יש עוד תכונות ומעבדות.

10. ניקוי הסביבה

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

מחיקת אשכול GKE

ב-Cloud Shell, מוחקים את אשכול GKE:

export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container clusters delete ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION}

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

student@cloudshell:~$ gcloud container clusters delete ${CLUSTER_NAME} \
>   --project=${PROJECT_ID} \
>   --region=${LOCATION}
The following clusters will be deleted.
 - [alloydb-ai-gke] in [us-central1]

Do you want to continue (Y/n)?  Y

Deleting cluster alloydb-ai-gke...done.
Deleted

מחיקת המכונה הווירטואלית של הלקוח

ב-Cloud Shell, מחק את מופע Compute Engine:

export PROJECT_ID=$(gcloud config get-value project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
  --project=${PROJECT_ID} \
  --zone=${ZONE}

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

student@cloudshell:~$ export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
  --project=${PROJECT_ID} \
  --zone=${ZONE}
Your active configuration is: [cloudshell-5399]
The following instances will be deleted. Any attached disks configured to be auto-deleted will be deleted unless they are attached to any other instances or the `--keep-disks` flag is given and specifies them for keeping. Deleting a disk 
is irreversible and any data on the disk will be lost.
 - [instance-1] in [us-central1-a]

Do you want to continue (Y/n)?  Y

Deleted

אם יצרתם פרויקט חדש עבור מעבדת קוד זו, תוכלו למחוק את הפרויקט כולו ב-Google Cloud Resource Manager.

11. מזל טוב

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

מה כיסיתם

  • איך פורסים את AlloyDB Omni באשכול GKE
  • איך מתחברים ל-AlloyDB Omni
  • איך טוענים נתונים ל-AlloyDB Omni
  • איך פורסים מודלים של AI (הטמעה ו-LLM) ב-GKE
  • איך רושמים מודלים של AI ב-AlloyDB Omni
  • איך ליצור הטמעות לחיפוש סמנטי
  • איך מריצים שאילתות של חיפוש סמנטי ב-AlloyDB Omni
  • איך יוצרים ומשתמשים באינדקסים וקטוריים ב-AlloyDB Omni

מידע נוסף על עבודה עם AI ב-AlloyDB Omni זמין בתיעוד.

סקר

פלט:

איך תשתמשו במדריך הזה?

רק קוראים את הטקסט קוראים את הטקסט ומבצעים את התרגילים