איך פורסים אתר עם ממשק משתמש גנרטיבי ב-Cloud Run

1. מבוא

סקירה כללית

בשיעור ה-Lab הזה תבנו ותפרסו אתר שהתוכן שלו נוצר תוך כדי תנועה על ידי מודלים גדולים של שפה (LLM) של Gemini מבית Google. האתר יהיה מעין משחק אינטראקטיבי שבו המשתמש בוחר את ההרפתקה שלו, וכל קליק יוצר דף חדש עם קישורים חדשים על סמך הבחירה שלו. תבנו את האפליקציה הזו באמצעות Node.js ו-Fastify, תשתמשו ב-Vertex AI SDK כדי לקרוא ל-Gemini, תפרסו אותה כשירות מאובטח ומוכן לייצור ב-Cloud Run ותאבטחו אותה באמצעות שרת proxy לאימות זהויות (IAP).

הפעולות שתבצעו:

  • יצירת אפליקציית Node.js Fastify שמשתמשת ב-Vertex AI.
  • פריסת האפליקציה ב-Cloud Run מקוד המקור בלי קובץ Dockerfile.
  • מאבטחים את נקודת הקצה של Cloud Run באמצעות שרת proxy לאימות זהויות (IAP).

מה תלמדו

  • איך משתמשים ב-Vertex AI SDK ל-Node.js כדי ליצור תוכן.
  • איך פורסים אפליקציית Node.js ב-Cloud Run.
  • איך מאבטחים אפליקציה ב-Cloud Run באמצעות IAP.

‫2. הגדרת הפרויקט

  1. אם עדיין אין לכם חשבון Google, אתם צריכים ליצור חשבון Google.
    • משתמשים בחשבון לשימוש אישי במקום בחשבון לצורכי עבודה או בחשבון בית ספרי. יכול להיות שבחשבונות לצורכי עבודה או בחשבונות בית ספריים יש הגבלות שימנעו מכם להפעיל את ממשקי ה-API שנדרשים למעבדה הזו.
  2. נכנסים למסוף Google Cloud.
  3. מפעילים את החיוב במסוף Cloud.
    • העלות של השלמת ה-Lab הזה במשאבי Cloud צריכה להיות פחות מ-1$.
    • כדי למחוק משאבים ולמנוע חיובים נוספים, אפשר לבצע את השלבים בסוף ה-Lab הזה.
    • משתמשים חדשים זכאים לתקופת ניסיון בחינם בשווי 300$.
  4. יוצרים פרויקט חדש או בוחרים להשתמש מחדש בפרויקט קיים.
    • אם מופיעה שגיאה לגבי מכסת הפרויקט, צריך לעשות שימוש חוזר בפרויקט קיים או למחוק פרויקט קיים כדי ליצור פרויקט חדש.

‫3. פתיחת Cloud Shell Editor

  1. כדי לעבור ישירות אל Cloud Shell Editor, לוחצים על הקישור הזה.
  2. אם תתבקשו לאשר בשלב כלשהו היום, תצטרכו ללחוץ על אישור כדי להמשיך. לוחצים כדי לתת הרשאה ל-Cloud Shell
  3. אם הטרמינל לא מופיע בתחתית המסך, פותחים אותו:
    • לוחצים על הצגה.
    • לוחצים על Terminal (מסוף)פתיחת טרמינל חדש ב-Cloud Shell Editor.
  4. בטרמינל, מגדירים את הפרויקט באמצעות הפקודה הבאה:
    • פורמט:
      gcloud config set project [PROJECT_ID]
      
    • דוגמה:
      gcloud config set project lab-project-id-example
      
    • אם אתם לא זוכרים את מזהה הפרויקט:
      • כדי לראות את כל מזהי הפרויקטים, מריצים את הפקודה:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      הגדרת מזהה הפרויקט בטרמינל של Cloud Shell Editor
  5. תוצג ההודעה הבאה:
    Updated property [core/project].
    
    אם מופיע WARNING ומוצגת השאלה Do you want to continue (Y/n)?, כנראה שהזנתם את מזהה הפרויקט בצורה שגויה. לוחצים על n, לוחצים על Enter ומנסים להריץ שוב את הפקודה gcloud config set project.
  1. הגדרת משתנה הסביבה GOOGLE_CLOUD_PROJECT
    export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
    

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

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

gcloud services enable \
  run.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iap.googleapis.com

אם מתבקשים לאשר, לוחצים על אישור כדי להמשיך. לוחצים כדי לתת הרשאה ל-Cloud Shell

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

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

5. הכנת פרויקט Node.js

  1. יוצרים תיקייה בשם gen-ui-on-cloudrun לאחסון קוד המקור לפריסה:
    mkdir gen-ui-on-cloudrun && cd gen-ui-on-cloudrun
    
  2. מפעילים פרויקט Node.js:
    npm init -y
    
  3. מגדירים את הפרויקט לשימוש במודולי ES ומגדירים סקריפט הפעלה באמצעות הפקודות הבאות:
    npm pkg set type="module"
    
  4. מתקינים את fastify לשרת האינטרנט ואת @google/genai ל-Vertex AI SDK:
    npm install fastify @google/genai
    

6. יצירת קוד האפליקציה

  1. יוצרים ופותחים קובץ index.ts חדש לקוד המקור של האפליקציה:
    cloudshell edit ~/gen-ui-on-cloudrun/index.ts
    
    הפקודה cloudshell edit תפתח את הקובץ index.ts בעורך שמעל הטרמינל.
  2. מוסיפים את קוד המקור הבא של שרת ממשק המשתמש הגנרטיבי לקובץ index.ts:
    import fastifyLib from 'fastify';
    import { GoogleGenAI } from '@google/genai';
    
    const fastify = fastifyLib({ logger: true });
    
    const ai = new GoogleGenAI({
        vertexai: true,
        project: process.env.GOOGLE_CLOUD_PROJECT,
        location: process.env.GOOGLE_CLOUD_LOCATION || 'europe-west1',
    });
    
    const SYSTEM_INSTRUCTION = `The user should have submitted an html page and the id of the element just clicked.
    Given the next page description, create a new webpage with a link back to "Start Over" (the / route), a brief overview of the topic, and a list of clickable link elements related to the page.
    When an element is clicked, the webpage should link to the base route / with the nextPageDescription as a query string parameter.
    All information needed to generate the next page should be included in the nextPageDescription without additional context.
    Each nextPageDescription should be less than 1500 characters.
    
    Example:
    If the current HTML page is for a small pet store, it might include a link to an "About" page.
    The href for the about page link should be /?nextPageDescription=about%20page%20for%20small%20pet%20store%20website
    
    All responses should be valid HTML without markdown backticks.`;
    
    interface QueryParams {
        nextPageDescription?: string;
    }
    
    fastify.get<{ Querystring: QueryParams }>('/', async (request, reply) => {
        const {
            nextPageDescription = 'A web page with interesting fun facts where I can select a fact to learn more about that topic.'
        } = request.query;
    
        try {
            const response = await ai.models.generateContent({
                model: 'gemini-2.5-flash',
                contents: nextPageDescription,
                config: {
                    systemInstruction: SYSTEM_INSTRUCTION,
                    temperature: 0.9,
                }
            });
    
            reply.type('text/html; charset=utf-8').send(response.text);
        } catch (error: any) {
            request.log.error(error);
            reply.status(500).send('An error occurred calling the AI.');
        }
    });
    
    const start = async () => {
        try {
            await fastify.listen({ port: Number(process.env.PORT) || 8080, host: '0.0.0.0' });
        } catch (err) {
            fastify.log.error(err);
            process.exit(1);
        }
    };
    
    start();
    

הקוד הזה מגדיר שרת אינטרנט שמקשיב לבקשות HTTP GET בנתיב הבסיס (/). כשמתקבלת בקשה, הוא משתמש בפרמטר השאילתה nextPageDescription (או בערך ברירת מחדל) כהנחיה למודל Gemini 2.5 Flash דרך Vertex AI. המודל מקבל הנחיה מ-SYSTEM_INSTRUCTION להחזיר דף HTML שמכיל קישורים, כאשר כל קישור כולל nextPageDescription ליצירת הדף הבא.

7. יצירת חשבון שירות

אתם צריכים חשבון שירות בשביל ששירות Cloud Run יוכל לבצע אימות באמצעות Vertex AI API.

  1. יוצרים חשבון שירות בשם gen-navigator-sa:
    gcloud iam service-accounts create gen-navigator-sa --display-name="Generative Navigator Service Account"
    
  2. נותנים לחשבון השירות הרשאה להשתמש ב-Vertex AI:
    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member="serviceAccount:gen-navigator-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role="roles/aiplatform.user"
    

8. פריסה ב-Cloud Run

עכשיו פורסים את האפליקציה ב-Cloud Run ישירות מקוד המקור, בלי צורך בקובץ Dockerfile.

  1. מריצים את הפקודה gcloud כדי לפרוס את האפליקציה:
    cd ~/gen-ui-on-cloudrun
    gcloud beta run deploy generative-web-navigator \
        --source . \
        --no-build \
        --base-image=nodejs24 \
        --command="node" \
        --args="index.ts" \
        --region=europe-west1 \
        --no-allow-unauthenticated \
        --iap \
        --service-account="gen-navigator-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --set-env-vars GOOGLE_CLOUD_PROJECT="$GOOGLE_CLOUD_PROJECT",GOOGLE_CLOUD_LOCATION="europe-west1"
    
    בדוגמה הזו השתמשנו בכמה דגלים חשובים:
    • --source . --no-build --base-image=nodejs24: הפקודה הזו מורה ל-Cloud Run לפרוס את קוד המקור מהספרייה הנוכחית, לדלג על שלב הבנייה ולהפעיל את האפליקציה באמצעות תמונת בסיס של Node.js 24 שנבנתה מראש.
    • --no-allow-unauthenticated: כך רק משתמשים מאומתים יכולים לגשת לשירות.
    • --iap: מאפשר לשרת proxy לאימות זהויות (IAP) לנהל את הגישה לאפליקציה. בעזרת IAP אפשר לשלוט בגישה על סמך זהות המשתמש וההקשר, במקום רק על סמך כתובות IP.
  2. אחרי כמה דקות, תופיע הודעה כמו:
    Service [generative-web-navigator] revision [generative-web-navigator-12345-abc] has been deployed and is serving 100 percent of traffic.
    

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

9. הגדרת גישה ל-IAP

כשמפעילים IAP ב-Cloud Run, שרת IAP מיירט את כל הבקשות ומחייב את המשתמשים לעבור אימות ולקבל הרשאה לפני שהם יכולים להגיע לשירות. כדי שהפעולה הזו תתבצע, צריך לתת שתי הרשאות:

  • מתן אפשרות לשירות IAP עצמו להפעיל את שירות Cloud Run.
  • מאפשרים לעצמכם (או למשתמשים או לקבוצות אחרים) לגשת לאפליקציה באמצעות IAP.
  1. כדי לזהות את סוכן השירות של IAP, צריך למצוא את מספר הפרויקט:
    export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
    
  2. מקצים לסוכן השירות של IAP את התפקיד roles/run.invoker בשירות Cloud Run. כך, אחרי שמשתמש עובר אימות ומקבל הרשאה, IAP יכול להפעיל את השירות שלכם.
    gcloud run services add-iam-policy-binding generative-web-navigator \
        --region=europe-west1 \
        --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-iap.iam.gserviceaccount.com" \
        --role="roles/run.invoker"
    
  3. מקצים לחשבון המשתמש את התפקיד roles/iap.httpsResourceAccessor. כך תוכלו לגשת למשאבי HTTPS שמוגנים על ידי IAP.
    gcloud beta iap web add-iam-policy-binding \
        --resource-type=cloud-run \
        --region=europe-west1 \
        --service=generative-web-navigator \
        --member="user:$(gcloud config get-value account)" \
        --role="roles/iap.httpsResourceAccessor"
    

10. בדיקת האפליקציה

  1. מקבלים את כתובת ה-URL של השירות שפרסתם:
    gcloud run services describe generative-web-navigator --format='value(status.url)' --region=europe-west1
    
  2. מעתיקים את כתובת ה-URL ופותחים אותה בדפדפן האינטרנט. מכיוון שהשירות מאובטח באמצעות IAP, תתבקשו להתחבר לחשבון Google אם עדיין לא התחברתם. אחרי האימות, אמור להופיע הדף הראשון שנוצר אוטומטית.
  3. לוחצים על קישור כלשהו כדי לעבור לדף חדש, שייווצר על ידי ה-AI על סמך הקישור שלחצתם עליו.

הצלחתם! הצלחתם לפרוס אתר עם ממשק משתמש גנרטיבי ב-Cloud Run ולאבטח אותו באמצעות IAP.

11. סיכום

מעולה! הצלחתם לפרוס ולאבטח אתר עם ממשק משתמש גנרטיבי באמצעות Cloud Run,‏ Vertex AI ו-IAP.

(אופציונלי) ניקוי

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

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

אם רוצים, אפשר למחוק את הפרויקט:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

מומלץ גם למחוק משאבים מיותרים מהדיסק של Cloud Shell. אתם יכולים:

  1. מוחקים את ספריית הפרויקט של ה-codelab:
    rm -rf ~/gen-ui-on-cloudrun
    
  2. אזהרה! אי אפשר לבטל את הפעולה הבאה! אם רוצים למחוק את כל מה שיש ב-Cloud Shell כדי לפנות מקום, אפשר למחוק את כל ספריית הבית. חשוב לוודא שכל מה שרוצים לשמור נשמר במקום אחר.
    sudo rm -rf $HOME