Skip to Content
操作流程建立環境並建置

建立環境並建置

**環境權限:**公司擁有環境由公司管理員管理;部門擁有環境可由該部門管理員或公司管理員建立/管理。部門管理員建立時須傳 owner_scope: "department" 與該部門的 owner_id。Context 上傳、版本、build、重試與 archive 都套用相同 owner-scope 閘門;curated 環境沒有租戶寫入路徑。Metadata 仍可由同公司身分讀取,上傳 session 則要求 owner 管理權。未授權租戶在 router 回 404,受限 key 會先被 auth 拒絕。

確認目錄(不必登入)

下拉選單不要自己發明 region 或 profile 字串。去抓封閉的 V1 集合:

GET /public/info/sandbox/regions GET /public/info/sandbox/profiles

這些在 /public/info 底下,不在 /private/module/sandbox。不必 JWT。把 code 當成 allowed_regions/build_region。把 id 當成 resource_profile,只有 tenant_selectable 為 true 才可以送。xlarge 租戶不可選。 Region 再與 GET /settings.allowed_regions 取交集(只管 runtime 放置)。營運端啟用仍在 GET /root/sandbox/regions。

Region 清單是部署所用 provider 的目錄(v5.10.11):託管雲端列出雲端區域(tier 1 或 2),永遠不列 onprem;地端部署只列 onprem(tier 3)。見雲端與地端部署。

策展路徑(租戶不上傳、不建置)

GET /environments 也會回 owner_kind=system_curated。那些版本由 root 發布。租戶讀取並釘選:

  1. GET /environments/GET /environments/{id}/versions 直到可釘的版本(state=ready)。live 目錄名稱是 SCFG Standard,每個公開區域都是 ready。
  2. POST /tasks/{id}/versions 帶那個 environment_version_id。不要上傳 context,也不要送租戶 source_digest 建置。
  3. 發布 + 分享 + 房間啟用,與其他任務相同(任務受眾)。

不要叫一般使用者去建環境。除非產品需要租戶自寫映像,否則走這條。

自訂環境步驟

  1. 建立父層 — POST /environments(name 1–256,description ≤2048)。回 state: "active"。這一步不上傳位元組、也不開始建置。
  2. 初始化 context 上傳 — POST /environments/{id}/context-uploads:
{ "declared_bytes": 123456, "archive_format": "tar.gz" }

archive_format ∈ zip/tar/tar.gz(預設 tar.gz)。declared_bytes 是封存的精確大小,1..1 GiB。回應含 upload_session_id、capability_token、capability_expires_at(15 分鐘)、put_header_name(X-Sandbox-Upload-Capability)、put_content_type(application/octet-stream)。這不是聊天室/DCC 的 blob_id,也不是 R2 presign。

  1. PUT 位元組 — PUT /environments/{id}/context-uploads/{upload_session_id},標頭 X-Sandbox-Upload-Capability,Content-Type: application/octet-stream。Body 不得超過 declared_bytes。回 bytes_received + digest(sha256:<hex>)。
  2. Complete — POST /environments/{id}/context-uploads/{session_id}/complete(可選 expected_size/expected_digest,用 PUT 回的值)。回 owned_object_id。已完成則冪等。Complete 不會開始建置。需要看 state 就 GET .../context-uploads/{session_id}。
  3. Abort(可選)— POST .../context-uploads/{session_id}/abort 釋放尚未完成的 slot。已完成的 session 不能 abort。
  4. 建立版本 — POST /environments/{id}/versions:
{ "owned_object_id": "<from complete>", "source_ref": "app-env-v3.tar.gz", "resource_profile": "standard" }

首選:complete 回的 owned_object_id。單獨送 source_digest 時後端不會比對任何已完成的 context 物件——只要格式是 sha256:<64 hex> 就會建出 draft 版本;若該 digest 沒有對應已上傳的封存,排隊建置仍會成功,錯誤要到建置實際執行、抓不到 context 時才出現,所以正式流程一律送 complete 回的 owned_object_id。兩個都送就必須相符。resource_profile 用公開目錄,且必須租戶可選。版本不可變;state: "draft"。

封存必須在根層帶一個名為 Dockerfile 的一般檔案(不可是符號連結、不可放在子目錄、不可改名為 Dockerfile.prod)。打包時要 tar czf ctx.tar.gz -C myapp .,不要 tar czf ctx.tar.gz myapp/——後者讓 Dockerfile 變成 myapp/Dockerfile,建置一定以 error_code=context_invalid 失敗。封存裡的 Dockerfile 可以 FROM 任何公開映像(node:20-alpine、python:3.12-slim、debian:bookworm-slim、ubuntu:24.04、scratch……)——從 2026-08-25 起沒有固定允許清單了。隔離邊界是沙盒 runtime 本身(強制簽章 entrypoint,無法脫逃 host),不是建置期的映像允許清單,所以 compose 不再按名字擋你的 base 映像。但它仍然會檢查合成後的 layer:任何檔案、連結、whiteout 或 device node 落在平台保留路徑上(usr/local/gcp/、teamsync-runner/、.teamsync-platform/、etc/ld.so.preload,或落在 proc/、sys/、dev/ 底下)都會讓建置失敗——error_code=context_invalid,error_detail=reserved_path_or_hostile_entry。每個真實 base 都會帶的無害節點——空的 /dev、/proc、/sys 掛載點,以及指到 /proc 的 /etc/mtab symlink——則明確放行。不論 base 是什麼,compose 仍然會直接拒絕:動態($/{)的 FROM 參照、任何 # syntax= 指示行(含最常見的 # syntax=docker/dockerfile:1,檔案中任何一行都算,不只第一行)、來自網路來源的 ADD、RUN --network=host、type=secret/type=ssh 的 RUN --mount 或 from= 指到未審查 stage 的 --mount、COPY --from 指到未審查的 stage,以及超過 1 MiB 的 Dockerfile——這些是在保護平台本身,不是在檢查你的內容。

建置與(預設下)執行期都有網路:Dockerfile 的 RUN 步驟可以連公開網際網路(私有網段、loopback、metadata 端點被防火牆擋掉);公司設定 runtime_egress_enabled 為 true(預設開啟)時,執行期的 startup.sh/work.sh 也能 pip install/npm ci/go get,對外流量會計量。想要快且可重現,仍建議把相依烤進映像——工作階段安裝每次執行都會重跑一遍。

CVE 掃描結果只是參考,永遠不會讓建置失敗——vulnerability_reject 已經不存在。SBOM 與漏洞報告仍會產生並附掛在該次 build attempt 上;只有真正的政策/契約違規(偽造 runner 身分、竄改 digest、缺簽章……)才會產生 error_code=policy_reject。

  1. 排隊建置 — POST /environments/{id}/versions/{vid}/builds(省略 build_region → 託管雲端 asia-east1、地端部署 onprem;省略 build_profile → standard)。build_region 不在區域目錄內——雲端的 onprem、地端的雲端代碼——會是 422 build_region_unavailable。
  2. 輪詢(沒有 streaming):
    • Attempt:GET /.../builds/{attempt_id} 直到 terminal_class 有值。
    • Version:GET /.../versions/{vid} 直到 state 是 ready。

前端會撞到的上限:每個封存 1 GiB、每公司 20 個 live staging slot 與 20 GiB 宣告量、每分鐘 5 次 init。429 staging_slot_exhausted/upload_rate_limited。未完成的上傳會漏 slot,直到 abort/reconcile — 那個 429 連 scan report 寫入也會卡住。已 complete 但還沒被版本採用的 context 也會佔著 slot:complete 不會釋放 staging lease,slot 要等 POST .../versions 採用該 owned_object_id 時才釋放,而已完成的 session 不能 abort(422),所以「上傳完但不建版本」會一路吃滿 20 個 slot。規則:每次 complete 之後就立刻建版本;Abort 只回收未完成的殘留 session;不要對 init 空轉重試。

除了 1 GiB 壓縮上限,封存解開後也有上限:解壓總量 5 GiB、entry 數 100,000、封存 metadata 64 MiB;解壓/壓縮比 ≥100 且解壓量超過 10 MiB 會被當成 decompression bomb 拒絕。這些由 context validator 在建置排隊後才判定,失敗一律回 error_code=context_invalid,且 error_detail 也只是 context_invalid(驗證器的具體理由不會外露)——不是上傳當下的 4xx。

要多久(2026-09-03 實測)

建環境+上傳 context+建版本+送出建置:API 時間約 1 秒。之後跑兩個 Cloud Build(後端 ≥ #1154):先是你的 Dockerfile(customer build,獨立身分),再一個受信任的 release Build,其步驤為 static scan 與 smoke 並行,再 sign → attest → promote → final verify,機型 E2_HIGHCPU_8。stage 映像每個 Build 只拉一次,不再每個 gate 拉一次:

基底映像就緒時間
python:3.12-slim + 一個 pip 套件約 5–6 分鐘(原約 12)
golang:1.22-bookworm + apt python3/node約 7 分鐘(原約 19)

輪詢 GET …/versions/{id},進行中時顯示 build_stage(build_stage_index / build_stage_count、build_stage_since、build_expected_seconds)與 build_next_action;失敗時呈現 last_build_error_code / last_build_error_detail 與 build_next_action(後端 ≥ #1140)。build_stage 仍逐 gate 前進(build_stage_count 不變)——release Build 進行中時,每完成一個步驟對應的 gate 就會翻過去;build_expected_seconds 現在約 360。六個驗證 gate 在該 Build 內共用一個身分(sandbox-release);不受信任的 customer build 仍用自己的身分(2026-09-04 決策:以受信任鏈內的 gate 隔離換取建置時間)。

失敗與重試分支

情況版本狀態動作
可重試失敗retryable_failedPOST /.../retry-build——不吃 request body,且新 attempt 一律用 build_region=asia-east1、build_profile=standard,不會沿用上次的區域或 builder 尺寸。要在其他區域或用別的 builder 尺寸重建,改打 POST /.../versions/{vid}/builds 並自己帶 build_region/build_profile(同樣受 7 天重試窗限制)
佈建卡住provisioning_blocked/佈建失敗POST /.../retry-provisioning
客戶端建置失敗build terminal_class: customer_failed先看 error_code(context_invalid、dockerfile_exit、image_limit、timeout_customer、policy_reject…)判斷類別,再看 error_detail 找出具體規則(dockerfile_not_utf8、dynamic_base_forbidden、remote_add_source_forbidden、host_network_forbidden、remote_syntax_frontend_forbidden、copy_from_unknown_stage、dockerfile_too_large、reserved_path_or_hostile_entry…),修正封存,開新版本
平台失敗build terminal_class: platform_failed看 error_code(identity_config…)。重試;一直失敗就回報後端
取消POST /.../builds/{attempt_id}/cancel(kill switch 下仍可用)一律 cancel_requested 加上可持久化的 provider-cancel intent — 即使 attempt 還在排隊。輪詢到 terminal_class=cancelled。不是同一請求內結算。

其他分支狀態:quarantined、rejected、blocked、abandoned、superseded、archived。

archived 是終態:controller 幾分鐘內就會回收該版本各區的 Job 與映像套件(見保留),所以只歸檔沒有任務還釘著的版本。runner_release_approved 為 false 的版本在 operator 核准該 runner release、或你重新排一次建置之前,都不能跑 bundle 工作;runner_release_next_action 會說要走哪條。

常見錯誤

  • 409 build_not_eligible:版本目前狀態不能開始建置。
  • 409 nonterminal_exists:此版本已有未結束的 attempt。去輪詢那個。
  • 409 not_cancellable:attempt 已終態。cancel_requested/cancelling 冪等(回當下那一列)。
  • 409 capability_fenced:上傳 capability 對不上這個 session。
  • 413 context_too_large:PUT body/Content-Length 超過 declared_bytes。
  • 422:缺少 owned_object_id 與 source_digest、digest 格式、未知欄位、空 PUT body。
  • 422 build_region_unavailable(POST .../builds):build_region 不在這個部署的區域目錄內(地端在已有 active 區域列、但它不在其中時也是)。什麼都沒排入;請改用 GET /public/info/sandbox/regions 裡的代碼。
  • 429 staging_slot_exhausted/upload_rate_limited。
  • 503 build_admission_fenced(POST .../builds、POST .../retry-build):平台 stage image 正在輪替,暫時擋下新的建置 admission;沒有任何 attempt 被排入。照 Retry-After(最多 300 秒)重試即可,不要當成平台故障。
  • 404:未授權/別公司的 id/受限金鑰/對策展環境上傳。

UI 建議

  • 三層「環境列表 → 版本列表 → 每版本建置狀態」。
  • state=ready 之前禁止把版本釘到任務上。
  • 建置報告:report_total_bytes > 0 表示有報告(本體不在租戶 API)。
  • 不要送 generic blob_id。

下一步:任務、分享與啟用。

選擇環境 owner(v5.10.0)

owner_scope 預設 company,此時可省略 owner_id 以使用呼叫者的公司 id。部門管理員必須明確選擇自己的部門:

{ "name": "Department runtime", "owner_scope": "department", "owner_id": "<department-id>" }

部門與公司環境共用同一個公司 active-parent 上限,PATCH 不可改 owner。看得到環境 metadata 不代表有寫入權;owner_kind=company 表示 tenant-custom,應以獨立的 owner_scope 決定顯示哪些管理操作。

哪些任務可釘選此環境

公司擁有與 curated 環境可供同公司符合條件的任務釘選;部門擁有環境可供同部門任務或公司擁有任務釘選,但不可給其他部門任務或 chatroom-owned Quick Run 任務。Quick Run 因此需要公司擁有或 curated 環境;ready、security 與租戶檢查仍然有效。

Last updated on