הרצת קוד ב-Cloud Run

ארגזי חול (sandboxes) של Cloud Run מאפשרים להריץ קוד לא מהימן בסביבה מבודדת, ישירות בתוך עומסי עבודה בקונטיינרים. היכולת הזו עוזרת לסוכני AI להריץ סוכני משנה, לבצע משימות חישוביות או להפעיל דפדפנים אוטומטיים בצורה בטוחה, בלי לסכן את המערכת המארחת.

יתרונות עיקריים

לארגזי החול של Cloud Run יש כמה יתרונות מרכזיים:

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

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

  • בקרת גישה וסביבה: תהליכים פועלים עם הרשאות sudo כמשתמש שאינו משתמש Root, ומאפשרים לכם להתקין כלים באמצעות מנהלי חבילות כמו apt,‏ pip או npm במהלך ההפעלה. סביבת ה-sandbox היא זמנית ונמחקת בסיום, אבל אתם יכולים להשתמש בספריות קבועות או בתמונות מצב כדי לשמור סביבות עבודה ספציפיות או למפות נתונים לקטגוריה של Cloud Storage.

לפני שמתחילים

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

  3. מתקינים ומפעילים את ה-CLI של gcloud.
  4. פורסים משאב של Cloud Run, כמו שירות, משימה, מאגר מכונות או מכונה מהדור השני.

הפעלת ארגזי חול

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

הפעלת ארגזי חול

אחרי שמפעילים את ארגזי החול, אפשר להפעיל אותם מתוך סביבת ההרצה של הקונטיינר. הקובץ הבינארי של ארגז החול נמצא ב-/usr/local/gcp/bin/sandbox.

בדוגמאות במדריך הזה נעשה שימוש בפקודה sandbox במקום בנתיב המוחלט שלה /usr/local/gcp/bin/sandbox.

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

Node.js

כדי להריץ את פקודת ארגז החול מאפליקציית Node.js, צריך לכלול את הקוד הבא:

exec(`sandbox do -- /bin/echo "Hello"`, (e, stdout, stderr) => {
    res.send({ stdout, stderr });
});

Python

כדי להריץ את פקודת ארגז החול מאפליקציית Python, צריך לכלול את הקוד הבא:

import subprocess
result = subprocess.run(
    ["sandbox", "do", "--", "/bin/echo", "Hello"],
    capture_output=True,
    text=True,
)
return {"stdout": result.stdout, "stderr": result.stderr}

המשך

כדי להריץ את פקודת ארגז החול מאפליקציית Go, צריך לכלול את הקוד הבא:

cmd := exec.Command("sandbox", "do", "--", "/bin/echo", "Hello")
out, err := cmd.CombinedOutput()

Sandbox CLI

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

sandbox do -- /bin/echo "Hello"

כדי לראות את הרשימה המלאה של הפקודות הזמינות, מריצים את הפקודה sandbox -h.

גבולות אבטחה לקוד לא מהימן

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

  • תעבורת נתונים יוצאת (egress) ברשת (--allow-egress): כברירת מחדל, הגישה לרשת היוצאת חסומה. הפעלת --allow-egress מאפשרת לקוד בארגז החול להתחבר למארחים חיצוניים ולנקודות קצה פנימיות של VPC, שאליהן המשאב של Cloud Run יכול להגיע.
  • נקודות טעינה מקושרות (--mount): נקודות טעינה מקושרות מצרפות ספריות מארחות כקריאה-כתיבה כברירת מחדל. תמיד צריך להוסיף את ,readonly כשחושפים ספריות מארחות לקוד לא מהימן, ואף פעם לא להעלות נתיבים שמכילים סודות או פרטי כניסה.
  • מערכת קבצים בסיסית (--rootfs ו---write): כברירת מחדל, --rootfs טוען את מערכת הקבצים של מארח הקונטיינר לקריאה בלבד ב-/. אל תאחסנו קבצים רגישים בדיסק של מארח הקונטיינר, כי תהליכים בארגז החול יכולים לקרוא אותם.
  • קובצי snapshot של ארכיון (--import-tar,‏ --export-tar,‏ --sync-tar ו-sandbox tar): אף פעם אל תשתפו ארכיונים של tar שניתן לכתוב בהם בין משתמשים או דיירים שונים שלא מהימנים. אל תחלצו ארכיונים לא מהימנים של ארגז חול באמצעות tar -xf ישירות במאגר המארח, כי ארכיונים יכולים להכיל קישורים סימבוליים זדוניים. במקום זאת, צריך להציג את רשומות הארכיון באמצעות tar -tf או לייבא מחדש את הארכיון לארגז חול אחר באמצעות --import-tar.
  • משתני סביבה (-e ו---env): כברירת מחדל, ארגזי חול לא יורשים משתני סביבה של מארח. לעולם אל תעבירו מפתחות API, טוקנים של חשבונות שירות או סודות לקוד לא מהימן באמצעות --env.

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

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

הרצת פקודה בארגז החול

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

  1. מפעיל סביבת ארגז חול (sandbox run).
  2. מריץ את הפקודה שציינתם (sandbox exec).
  3. מחיקת ארגז החול אחרי הפעלה מוצלחת (sandbox delete).

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

Node.js

כדי להריץ את פקודת ארגז החול מאפליקציית Node.js:

exec(`sandbox do -- /usr/bin/python3 -c "print(1+2)"`, (e, stdout, stderr) => {
    res.send({ stdout, stderr });
});

Python

כדי להריץ את פקודת ארגז החול מאפליקציית Python:

import subprocess
result = subprocess.run(
    ["sandbox", "do", "--", "/usr/bin/python3", "-c", "print(1+2)"],
    capture_output=True,
    text=True,
)
return {"stdout": result.stdout, "stderr": result.stderr}

המשך

כדי להריץ את פקודת ארגז החול מאפליקציית Go:

cmd := exec.Command("sandbox", "do", "--", "/usr/bin/python3", "-c", "print(1+2)")
out, err := cmd.CombinedOutput()

Sandbox CLI

כדי להריץ את פקודת ארגז החול ישירות משורת הפקודה:

sandbox do -- /usr/bin/python3 -c "print(1+2)"

אם מריצים פקודה לפי שם בלי הנתיב המוחלט שלה, למשל python3 במקום /usr/bin/python3, צריך להגדיר במפורש את משתנה הסביבה PATH בארגז החול באמצעות הדגל --env.

שמירת נתונים בין הרצות שונות

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

כשמריצים את הפקודה sandbox do, משתמשים בדגלים הבאים:

  • --export-tar: מתעד קובצי שכבת-על ששונו בקובץ ארכיון tar בסיום.
  • --import-tar: חילוץ קבצים מקובץ ארכיון בפורמט tar לארגז החול לפני ההפעלה.
  • ‫--sync-tar: מבצע סנכרון דו-כיווני על ידי ייבוא לפני ההפעלה וייצוא בסיום.

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

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

  1. כתיבת נתונים בתוך ארגז חול וייצוא המצב לקובץ ארכיון:

    sandbox do --write --export-tar=/tmp/work.tar \
      -- /usr/bin/bash -c "mkdir -p /workspace && echo 'task-complete' > /workspace/status.txt"
    
  2. מייבאים את קובץ הארכיון בקריאה הבאה כדי לאחזר את הנתונים:

    sandbox do --write --import-tar=/tmp/work.tar \
      -- /usr/bin/bash -c "cat /workspace/status.txt"
    

לחלופין, כדי לייבא באופן אוטומטי את מצב הארכיון הקיים ולייצא שינויים חדשים בפקודה אחת, משתמשים ב---sync-tar=/tmp/work.tar. כשמסתיים תהליך בארגז החול, Cloud Run מוחק באופן סופי קובצי שכבת-על זמניים שלא יוצאו לקובץ ארכיון.

הרצת פקודה ברקע

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

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

sandbox run my-web-server --detach -- /usr/bin/long_running_or_idle_program

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

לדוגמה, כדי להריץ פקודת בדיקה בתוך ארגז החול הקיים של my-web-server ברקע, מריצים את הפקודה הבאה:

sandbox exec my-web-server -- /usr/bin/python3 -c "print('test-complete')"

הגדרת משתני סביבה

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

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

sandbox do --env AGENT_MODE="test" -- /usr/bin/bash -c "echo \$AGENT_MODE"

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

יצירת תמונות מצב של מערכת הקבצים

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

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

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

    sandbox run --write my-sandbox --detach -- /usr/bin/bash -c "echo 'hi' > /workspace/hello.txt && sleep 1h"
    
  2. יוצרים קובץ snapshot של מערכת הקבצים ששונתה בארגז החול שפועל, באמצעות הפקודה sandbox tar:

    sandbox tar my-sandbox --file=/tmp/foo.tar
    
  3. מציגים את התוכן של קובץ הארכיון של קובץ ה-snapshot כדי לוודא שהוא מכיל את הנתונים שנכתבו בתוך ארגז החול:

    tar -tf /tmp/foo.tar
    

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

    ./
    ./workspace/
    ./workspace/hello.txt
    

הגדרה של רשתות

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

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

sandbox do --allow-egress -- /usr/bin/python3 -c 'import urllib.request; print(urllib.request.urlopen("https://google.com").getcode())'

הפקודה הזו מחזירה את קוד סטטוס של HTTP‏ 200, שמציין שהחיבור הצליח.

גישה למערכת הקבצים

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

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

הרשאת קריאה בלבד כברירת מחדל

בתוך ארגז החול, תהליכים יכולים לקרוא קבצים מהקונטיינר המארח, והם לא יכולים לכתוב למערכת הקבצים ברמה הבסיסית (root) מחוץ ל-/tmp.

בדוגמאות הבאות, נניח שאתם מריצים פקודות מתיקיית השורש (/) של הקונטיינר המארח.

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

  1. יוצרים סקריפט Python בקונטיינר המארח:

    mkdir -p /tmp/my-scripts
    echo "print('hi')" > /tmp/my-scripts/task.py
    
  2. מוודאים שהקובץ קיים באופן מקומי:

    cat /tmp/my-scripts/task.py
    
  3. מריצים את הקובץ בתוך ארגז החול:

    sandbox do -- /usr/bin/python3 /tmp/my-scripts/task.py
    

    הפקודה הזו מחזירה את הערך hi, שמאשר שלארגז החול יש גישת קריאה.

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

    מריצים את הפקודה הבאה כדי לכתוב למערכת הקבצים ברמה הבסיסית (root):

    sandbox do -- /usr/bin/bash -c "echo 'hi' > /testfile.txt"
    

    הפקודה נכשלת ומופיעה השגיאה הבאה:

    /usr/bin/bash: line 1: /testfile.txt: Read-only file system
    Error: failed to exec in container: cmd.Wait(exec) failed: exit status 1
    

שיתוף נתונים באמצעות נקודות טעינה מקושרות

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

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

  1. יוצרים ספריית אמצעי אחסון משותפת במאגר המארח ומזינים בה קובץ ראשוני:

    mkdir -p /tmp/my-volume
    echo 'read' > /tmp/my-volume/readwrite.txt
    
  2. מריצים את ארגז החול כדי לקרוא את הקובץ מנתיב הטעינה המקושר:

    sandbox do --mount type=bind,source=/tmp/my-volume,destination=/mnt/my-mount -- /usr/bin/bash -c "cat /mnt/my-mount/readwrite.txt"
    

    הפקודה הזו מחזירה את הערך read.

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

    sandbox do --mount type=bind,source=/tmp/my-volume,destination=/mnt/my-mount -- /usr/bin/bash -c "echo 'write' > /mnt/my-mount/readwrite.txt"
    
  4. מוודאים במאגר המארח שארגז החול שינה את הקובץ:

    cat /tmp/my-volume/readwrite.txt
    

    הפקודה הזו מחזירה את הערך write.

הגדרת טעינות לקריאה בלבד

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

לדוגמה, מריצים את הפקודה הבאה כדי לבדוק את מגבלות הכתיבה ב-bind mount לקריאה בלבד:

sandbox do --mount type=bind,source=/tmp/my-volume,destination=/mnt/my-mount,readonly -- /usr/bin/bash -c "echo 'fails' > /mnt/my-mount/hello.txt"

ניסיון הכתיבה נכשל ומוצגת השגיאה הבאה:

/usr/bin/bash: line 1: /mnt/my-mount/hello.txt: Read-only file system
Error: failed to exec in container: cmd.Wait(exec) failed: exit status 1

צפייה ביומנים

‫Cloud Run מתעד באופן אוטומטי אירועים במחזור החיים של ארגז החול, כמו התחלה ויציאה מהפעלה, ב-Cloud Logging.

ה-CLI של sandbox כותב פלט סטנדרטי (stdout) ושגיאה סטנדרטית (stderr) מפקודות בארגז חול ישירות לסטרימינג הסטנדרטי של תהליך ההפעלה. כדי לראות את היומנים האלה ב-Cloud Logging, צריך לנתב את הסטרימינג לפלט הסטנדרטי (stdout) ולשגיאה הסטנדרטית (stderr) של הקונטיינר:

Node.js

const { exec } = require('child_process');
const child = exec('sandbox do -- /usr/bin/python3 -c "print(1+2)"');
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

Python

subprocess.run(
    ["sandbox", "do", "--", "/usr/bin/python3", "-c", "print(1+2)"],
    stdout=sys.stdout,
    stderr=sys.stderr,
)

המשך

cmd := exec.Command("sandbox", "do", "--", "/usr/bin/python3", "-c", "print(1+2)")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()

המאמרים הבאים