在 Cloud Run 中執行程式碼

Cloud Run 已採用沙箱和隔離機制,因此非常適合代管 AI 代理。啟用 Cloud Run 沙箱後,容器中就會提供 sandbox 指令列工具。使用這個指令列工具,在經過高度最佳化的沙箱環境中執行以任何語言編寫的不受信任程式碼,與容器的其餘部分隔離。

AI 代理可利用沙箱安全地執行子代理、執行運算工作,或在快速隔離的環境中開啟瀏覽器,不會對主機系統造成風險。

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

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

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

  • 存取權控管和環境:程序會以 sudo 權限執行,做為非根使用者,讓您在執行期間使用 aptpipnpm 等套件管理工具安裝工具。沙箱環境是暫時性的,完成後就會刪除,但您可以使用持續性目錄快照儲存特定工作區,或將資料對應至 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 服務

啟用沙箱

啟用沙箱後,Cloud Run 服務會部署在第二代執行環境中。如要在 Cloud Run 服務中啟用沙箱,請使用 Google Cloud CLI 或 YAML 設定:

gcloud

如要部署或更新服務,請指定 --sandbox-launcher 標記:

  • 如要部署新服務,請執行下列指令:

    gcloud beta run deploy SERVICE --image IMAGE_URL --sandbox-launcher

    更改下列內容:

    • SERVICE:Cloud Run 服務的名稱。
    • IMAGE_URL:容器映像檔的參照,例如 us-docker.pkg.dev/cloudrun/container/hello:latest。如果您使用 Artifact Registry,則必須先建立存放區 REPO_NAME。網址格式為 LOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/PATH:TAG
  • 如要更新現有服務,請執行下列指令:

    gcloud beta run services update SERVICE --sandbox-launcher

YAML

  1. 如要建立新服務,請略過這個步驟。 如要更新現有服務,請下載其 YAML 設定

    gcloud run services describe SERVICE --format export > service.yaml
  2. 更新服務 YAML 檔案,在容器設定中加入 sandboxLauncher 屬性,並將其設為 true

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: SERVICE
      annotations:
        run.googleapis.com/launch-stage: BETA
    spec:
      template:
        spec:
          containers:
          - name: CONTAINER
            image: IMAGE_URL
            sandboxLauncher: true
            port: 8080
    

    更改下列內容:

    • SERVICE:Cloud Run 服務的名稱。
    • CONTAINER:容器名稱。
    • IMAGE_URL:容器映像檔的參照,例如 us-docker.pkg.dev/cloudrun/container/hello:latest。如果您使用 Artifact Registry,則必須先建立存放區 REPO_NAME。網址格式為 LOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/PATH:TAG
  3. 使用下列指令建立或更新服務:

    gcloud run services replace service.yaml

    如果存在 service.yaml 檔案,gcloud run services replace 指令預設會使用該檔案。

沙箱會共用分配給主機容器的 CPU 和記憶體。請確認主要容器的 CPU 和記憶體限制足夠,可同時容納應用程式和執行的任何有效沙箱。

停用沙箱

如要禁止在服務中啟動沙箱,請使用 Google Cloud CLI 或 YAML 設定:

gcloud

執行下列指令,使用 --no-sandbox-launcher 旗標更新服務:

gcloud beta run services update SERVICE --no-sandbox-launcher

SERVICE 改為您的服務名稱。

YAML

  1. 如要建立新服務,請略過這個步驟。 如要更新現有服務,請下載其 YAML 設定

    gcloud run services describe SERVICE --format export > service.yaml
  2. 更新服務 YAML 檔案,移除容器設定中的 sandboxLauncher 屬性:

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: SERVICE
    spec:
      template:
        spec:
          containers:
          - name: CONTAINER
            image: IMAGE_URL
            port: 8080
    

    更改下列內容:

    • SERVICE:Cloud Run 服務的名稱。
    • CONTAINER:容器名稱。
    • IMAGE_URL:容器映像檔的參照,例如 us-docker.pkg.dev/cloudrun/container/hello:latest。如果您使用 Artifact Registry,則必須先建立存放區 REPO_NAME。網址格式為 LOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/PATH:TAG
  3. 使用下列指令建立或更新服務:

    gcloud run services replace service.yaml

    如果存在 service.yaml 檔案,gcloud run services replace 指令預設會使用該檔案。

啟動沙箱

啟用沙箱後,您就能從容器執行環境啟動沙箱。沙箱二進位檔位於 /usr/local/gcp/bin/sandbox

您可以在原始碼中參照二進位檔的絕對路徑,藉此執行二進位檔。舉例來說,如要在隔離的沙箱中列印 Hello,請選擇下列其中一個選項:

Node.js

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

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

Python

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

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

Go

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

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

Sandbox CLI

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

/usr/local/gcp/bin/sandbox do -- /bin/echo "Hello"

本指南中的範例使用 sandbox 指令,而非絕對路徑 /usr/local/gcp/bin/sandbox

如要查看可用指令的完整清單,請執行 /usr/local/gcp/bin/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 旗標傳遞密鑰,因為沙箱程序可能會看到這些密鑰。

建立檔案系統快照

在背景部署具名沙箱,處理網頁伺服器或長時間執行的代理程式工作流程等持續性工作,針對沙箱動態執行指令,並將修改後的檔案系統狀態擷取至 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()

後續步驟