在 Cloud Run 中執行程式碼

Cloud Run 沙箱可讓您在容器工作負載中,直接於隔離環境執行不信任的程式碼。這項功能可協助 AI 代理程式安全地執行子代理程式、運算工作或啟動自動瀏覽器,不會對主機系統造成風險。

主要優點

Cloud Run 沙箱具有下列主要優點:

  • 快速建立:沙箱具備互動性,幾乎可立即執行指令。在現有的 Cloud Run 資源中建立沙箱,代理程式會在其中執行,與為每個工作建立新的 Cloud Run 資源相比,可縮短建立時間。這項效率有助於確保代理程式保持回應。

  • 安全性:沙箱會隔離程序執行作業。根據預設,沙箱無法存取父項工作負載、環境變數、密鑰或 Google Cloud 中繼資料伺服器。所有沙箱都完全獨立。

  • 存取控管和環境:程序會以非超級使用者的 sudo 權限執行,因此您可以在執行期間使用套件管理工具 (例如 apt、pip 或 npm) 安裝工具。沙箱環境是暫時性的,完成後就會刪除,但您可以使用持續性目錄或快照,將特定工作區或地圖資料儲存至 Cloud Storage bucket。

事前準備

  1. 登入 Google Cloud 帳戶。如果您是 Google Cloud新手,歡迎 建立帳戶,親自體驗產品的實際應用成效。新客戶還能獲得價值 $300 美元的免費抵免額,能用於執行、測試及部署工作負載。
  2. 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

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

  4. 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

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

  6. 安裝並初始化 gcloud CLI。
  7. 部署 Cloud Run 資源,例如第二代 服務、工作、工作人員集區或執行個體。

啟用沙箱

如要使用沙箱,請先使用 --sandbox-launcher 旗標或 sandboxLauncher YAML 屬性,在 Cloud Run 資源上啟用沙箱。如需詳細設定步驟,請選擇下列資源類型:

啟動沙箱

啟用沙箱後,您就能在容器執行環境中啟動沙箱。沙箱二進位檔位於 /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

如要從 Go 應用程式執行沙箱指令,請加入下列程式碼:

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

Sandbox CLI

如要直接從指令列執行沙箱指令,請執行下列指令:

sandbox do -- /bin/echo "Hello"

如要查看可用指令的完整清單,請執行 sandbox -h 指令。

使用沙箱指令列功能

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

如要從 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),請使用 --env 旗標,在沙箱中明確設定 PATH 環境變數。

在不同執行作業之間保留資料

沙箱預設為暫時性,如要在同一個 Cloud Run 執行個體內,跨不同沙箱執行作業保留資料,可以使用標準 tar 封存檔案匯入及匯出工作區檔案系統狀態。或者,您也可以設定繫結掛接,直接在主機容器和沙箱環境之間共用目錄。

執行 sandbox do 指令時,請使用下列旗標:

  • --export-tar:完成後,將修改過的疊加檔案擷取到 tar 封存檔案中。
  • --import-tar:在執行前,從 tar 封存檔將檔案解壓縮到沙箱。
  • --sync-tar:執行前匯入資料,完成後匯出資料,進行雙向同步。

舉例來說,如要使用封存檔案在兩個沙箱呼叫之間傳遞資料,請執行下列指令:

  1. 在沙箱中寫入資料,並將狀態匯出至封存檔:

    sandbox do --write --export-tar=/tmp/work.tar \
      -- /usr/bin/bash -c "mkdir -p /tmp/work && echo 'task-complete' > /tmp/work/status.txt"
    
  2. 在後續呼叫中匯入封存檔案,即可擷取資料:

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

或者,如要使用單一指令自動匯入現有封存狀態並匯出新變更,請使用 --sync-tar=/tmp/work.tar。沙箱程序終止時,Cloud Run 會永久刪除未匯出至封存檔案的暫時性疊加檔案。

在背景執行指令

如要執行長時間執行的程序、無頭瀏覽器或背景伺服器 (例如持續監聽傳入要求的背景代理迴圈),請使用 --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')"

設定環境變數

在沙箱中設定環境變數,方法與設定其他容器相同。沙箱不會從主機容器繼承環境變數。執行 sandbox 指令時,必須使用 --env 旗標明確提供這些值。

舉例來說,如要將設定變數傳遞至沙箱,請執行下列指令:

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

請避免使用 env 旗標傳遞密鑰,因為沙箱程序可能會看到這些密鑰。

建立檔案系統快照

在背景部署具名沙箱,處理網頁伺服器或 Agentic Workflows 等持續性工作,動態執行沙箱指令,並將修改後的檔案系統狀態擷取至 tar 封存檔。

舉例來說,如要部署背景沙箱、將檔案寫入其疊加層,並將其狀態快照儲存為快照,以驗證資料是否已擷取,請執行下列指令:

  1. 在背景部署已命名的沙箱,並啟用寫入存取權,在沙箱的工作區中建立檔案:

    sandbox run --write my-sandbox --detach -- /usr/bin/bash -c "echo 'hi' > /tmp/hello.txt && sleep 1h"
    
  2. 使用 sandbox tar 指令,建立執行中沙箱的修改後檔案系統快照:

    sandbox tar my-sandbox --file=/tmp/foo.tar
    
  3. 解壓縮並確認快照封存檔包含寫入沙箱內的資料:

    tar -xvf /tmp/foo.tar
    

    您應該會看到下列結果:

    ./
    ./tmp/
    ./tmp/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,表示連線成功。

存取檔案系統

根據預設,您在沙箱中執行的程序只能讀取主機容器根檔案系統。您可以使用 --write 標記,啟用寫入暫存檔案系統 (tmpfs) 疊加層。不過,刪除沙箱後,寫入作業就會遺失。如要啟用對主機容器的永久寫入功能,可以設定繫結掛接。

預設唯讀存取權

在沙箱中,程序可以從主機容器讀取檔案,但無法寫入根檔案系統。

以下範例假設您是從主機容器的根目錄 (/) 執行指令。

如要驗證預設的唯讀存取權,請執行下列指令:

  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,確認沙箱具有讀取權。

    如果嘗試直接將資料寫入沙箱根檔案系統,但未進行額外設定,執行作業就會失敗。舉例來說,嘗試在預設沙箱中寫入 /tmp 會傳回唯讀檔案系統錯誤:

    執行下列指令,寫入根檔案系統:

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

    指令失敗,並顯示下列錯誤:

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

使用繫結掛接共用資料

如要允許沙箱內的程序寫入可保存的資料,請使用 --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 屬性附加至掛接規格。

舉例來說,執行下列指令可測試唯讀繫結掛接的寫入限制:

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 中擷取沙箱生命週期事件,例如執行開始和結束。

sandbox CLI 會將沙箱化指令的標準輸出內容 (stdout) 和標準錯誤 (stderr),直接寫入叫用程序的標準串流。如要在 Cloud Logging 中查看這些記錄,請將串流傳送至容器的標準輸出內容和標準錯誤:

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,
)

Go

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

後續步驟