Cloud Build CI/CD 簡介
Cloud Build 是 Google Cloud 的無伺服器建置平台。你交給它一份 YAML 檔案(或一份 Dockerfile)以及一個原始碼位置,它就會依序在 Google 託管的基礎設施上以容器方式執行每一個步驟。沒有 Jenkins agent 要打補丁,沒有 GitHub runner 要自動擴展,也沒有 Buildkite VM 要設定規格。工作的最小單位是 step,計費的最小單位是 build 分鐘。
對於 GCP Professional Cloud Developer (PCD) 考試來說,Cloud Build 位於三個重要領域的交集:持續整合、容器封裝與供應鏈安全。考試會測驗你能否正確設定 trigger、在不外洩的情況下取得 secret、為網路依賴型建置挑對 pool、用手動核准為正式環境設閘門,並讀取 SLSA provenance 來證明到底發布了什麼。本份學習筆記涵蓋上述每個面向,加上能讓真實團隊管線運作或崩壞的 IAM 權限。
白話文解釋 (Plain English Explanation)
在深入 YAML 語法與 IAM 綁定之前,先用三個類比。重點不是背 feature 名稱,而是體會 Cloud Build 每個機制實際在做什麼。
Cloud Build 就像工廠的組裝線
一份 cloudbuild.yaml 就是組裝線的藍圖。每一個 step 就是一個工作站。第一站可能拆開貨櫃(git clone,Cloud Build 的 fetcher 已經幫你做了),下一站跑單元測試,再下一站建出容器映像檔,再下一站推送到 Artifact Registry,最後一站部署到 Cloud Run。每個工作站都由專職機器人駐守,在 Cloud Build 裡這個機器人就是裝好工具的 Docker 容器(gcr.io/cloud-builders/* 系列映像檔,或是你自己客製的映像檔)。
工作站之間的輸送帶是會在每個 step 間傳遞的 /workspace volume。前一站丟到輸送帶上的東西,下一站就看得到。如果你需要平行作業(同時跑單元測試與 lint),就讓兩個工作站靠 waitFor 宣告「上一個 step 跑完我就可以開工」。如果完全不用 waitFor,整條線就會是單列序列、跑得很慢。
Build trigger 就像門鈴
一個 trigger 就是接到特定門上的門鈴。push 到 main 響的是正式環境門鈴;開 pull request 響的是 PR 門鈴;打上 v1.2.3 tag 響的是 release 門鈴。同一座工廠回應每一個門鈴,但每個門鈴會送出不同的 cloudbuild.yaml 進到組裝線,並帶不同的 substitution 變數。門鈴不在乎是誰按下去的(其實在 IAM 層面是在乎的),它只在乎哪扇門響了、訪客帶了什麼進來。
手動 trigger 是只有屋主能刻意按的門鈴,要從 Cloud Console 或 gcloud builds triggers run 才能按。Webhook trigger 則是把門鈴直接拉到馬路上,任何能對指定 URL 發出帶有正確 secret 的 POST 就能把它按響,適合用於 Jira、Linear,或其他不講 GitHub 那一套的 SaaS。
Private pool 就像私人工坊
預設的 Cloud Build pool 是一個位於網際網路上的共用公共工坊,啟動很快,但這個工坊有公網位址。如果你的建置需要透過 private IP 去打 Cloud SQL 執行個體、透過 Cloud VPN 去打地端的 artifact 伺服器、或是去打躲在企業防火牆後面的自架 GitLab,公共 pool 根本到不了。
Private pool 就是你自己的私人工坊,丟在跟你環境互相對等連線 (VPC peering) 的 VPC 裡。建置在裡面跑,擁有固定的出口位址,也能存取私有資源。你會付比較多錢(要保留容量),但能對私有網路建置、鎖緊出口、並挑更高規格的機器來跑慢吞吞的 Bazel 或 Gradle 建置,不必跟其他共用 pool 客戶排隊。
cloudbuild.yaml 解剖與 Build Step
cloudbuild.yaml 是 Cloud Build 的脊椎。其他東西(trigger、IAM、provenance)都是這份 YAML 周圍的設定。把這份 YAML 弄得很熟,其他都會跟著到位。
最頂層的欄位
一個典型的 build 設定大致長這樣:
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA']
images:
- 'us-central1-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA'
options:
logging: CLOUD_LOGGING_ONLY
machineType: E2_HIGHCPU_8
timeout: 1200s
steps 是有順序的 build step 清單。images 是一個方便欄位:列在裡面的映像檔在 build 成功時會自動推送到 Artifact Registry(或舊的 Container Registry)。options 控制全域行為(machine type、logging 模式、substitution 選項、source provenance hashing)。timeout 設定整個 job 的逾時時間;預設是 10 分鐘,最長 24 小時。
Builder 與 step 容器
每個 step 的 name 是一個容器映像檔名稱。Cloud Build 提供一組精選的 cloud-builders(gcloud、docker、kubectl、gsutil、mvn、npm、yarn、go、gradle、gke-deploy、helm、bazel 等等)。你也可以讓 name 指向 Artifact Registry、Docker Hub 或 GitHub Container Registry 上的任何映像檔。Build 對每個 step 其實就是執行 docker run <name> <args>。
entrypoint 欄位會覆寫容器預設的 entrypoint,常用於你想跑 shell 而不是容器原本的主程式。env 注入環境變數。dir 在 /workspace 內切換工作目錄。較新的 script 欄位則讓你直接寫 inline shell,不必把所有東西都塞進 args 裡。
用 waitFor 設定平行 step
預設情況下,每個 step 都會等上一個 step 跑完才開始。要平行執行,給每個 step 一個 id,然後用 waitFor 指向更早的 id(或用 - 代表「立刻開始」):
steps:
- id: 'install'
name: 'node:20'
entrypoint: 'npm'
args: ['ci']
- id: 'test'
name: 'node:20'
waitFor: ['install']
entrypoint: 'npm'
args: ['test']
- id: 'lint'
name: 'node:20'
waitFor: ['install']
entrypoint: 'npm'
args: ['run', 'lint']
install 跑完後,test 與 lint 會同時開始。整段 build 的時間就變成 max(test, lint) 而不是 test + lint。
請把 builder 映像檔釘到特定 digest 或特定 tag,永遠不要用 latest。週一還能跑的建置,可能因為 gcr.io/cloud-builders/docker:latest 從 20.10 滾到 24.0 並破壞了某個 flag,在週二就掛了。為了最強保證,可以用 name: 'gcr.io/cloud-builders/docker@sha256:...'。詳見 https://cloud.google.com/build/docs/build-config-file-schema
Substitutions 與建置變數
Substitutions 是 Cloud Build 在執行每一個 step 之前會先解析的變數。它們讓同一份 YAML 可以跨 branch、跨環境、跨 trigger 重複使用。
預設 substitutions
Cloud Build 會注入一組固定變數:$PROJECT_ID、$BUILD_ID、$PROJECT_NUMBER、$LOCATION、$TRIGGER_NAME、$COMMIT_SHA(完整)、$SHORT_SHA(前七碼)、$REVISION_ID、$REPO_NAME、$BRANCH_NAME、$TAG_NAME。要標映像檔 tag 或寫部署標籤,這組變數通常就夠用。
使用者自訂 substitutions
使用者自訂的 substitutions 必須以底線開頭:_REGION、_ENV、_ARTIFACT_REPO。可以在 trigger 設定中定義,也能透過 gcloud builds submit --substitutions=_REGION=us-east1 傳入。底線開頭是強制規定,否則 build validation 會直接失敗。
substitutions:
_REGION: us-central1
_ENV: dev
steps:
- name: 'gcr.io/cloud-builders/gcloud'
args: ['run', 'deploy', 'api-${_ENV}', '--region=${_REGION}', '--image=us-central1-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA']
YAML 裡寫的預設值會被 trigger 設定或 CLI 呼叫覆寫。options.substitution_option: ALLOW_LOOSE flag 會放寬「所有引用的 substitutions 都必須存在」這條規則;好用,但同時是個地雷,因為打錯字會悄悄變成空字串。
Substitution 的範疇與引號
Substitutions 純粹是字串替換,所以在 YAML 裡需要小心引號。_TAGS: 'a,b,c' 這種值能撐過一次替換,但如果你想把它丟給一個期望 YAML list 的 step,就會出問題。複雜值請改走 Secret Manager(如果是 secret)或 repo 裡的設定檔(如果是結構化資料)。
請用 $SHORT_SHA 而不是 $COMMIT_SHA 來當映像檔 tag。完整 SHA 加上 registry 路徑後會超過 128 字元的 image tag 上限,在 log 裡也很難讀。$SHORT_SHA 加上 branch 名稱($BRANCH_NAME-$SHORT_SHA)的 tag 既獨一無二、又對人友善。詳見 https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values
Secret Manager 整合
把 secret 寫死在 cloudbuild.yaml 裡是 CI/CD 的頭號禁忌。Cloud Build 的正解是 availableSecrets 區塊,在執行階段從 Secret Manager 取出 secret。
availableSecrets 寫法
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_NUMBER/secrets/npm-token/versions/latest
env: 'NPM_TOKEN'
- versionName: projects/$PROJECT_NUMBER/secrets/docker-hub-password/versions/3
env: 'DOCKER_PASS'
steps:
- name: 'gcr.io/cloud-builders/npm'
entrypoint: 'bash'
args: ['-c', 'echo "//registry.npmjs.org/:_authToken=$$NPM_TOKEN" > ~/.npmrc && npm ci']
secretEnv: ['NPM_TOKEN']
availableSecrets 區塊宣告 Cloud Build 可以取出哪幾個 Secret Manager 版本。每個 step 用 secretEnv 主動 opt-in 拿特定 secret。Secret 的值只在那個 step 內被注入為環境變數,並會在 build log 中被遮罩。
$$NPM_TOKEN(兩個錢字號)的寫法是告訴 Cloud Build「這個變數不要做 YAML-time substitution,請讓 shell 在 step 執行階段才處理」。如果只用單一錢字號,會在 YAML 替換階段就被解析,secret 值會直接出現在已替換的 YAML 裡。
存取 Secret Manager 所需的權限
Cloud Build 服務帳戶(預設是 <project-number>@cloudbuild.gserviceaccount.com,也可以是你自訂的 service account)需要對每個它要讀的 secret 擁有 secretmanager.secretAccessor 角色。正式環境請釘到特定 version,不要用 latest,避免一次 secret rotation 就悄悄改變建置行為。
替代方案與反模式
舊的 _SECRET_KEY 替換式寫法(用 KMS 加密、把密文貼到 YAML 裡)仍然能跑,但難以輪替也難以稽核。新的工作一律改用 availableSecrets。永遠不要把真正的 secret 放進 substitution 變數中;任何持有 cloudbuild.builds.editor 的人都看得到 trigger 設定。
不要把 secret 印出來。即使是 echo 到 stdout 也會落到 Cloud Logging。如果要 debug 一個用到 secret 的 step,請先遮罩:bash -c 'test -n "$$NPM_TOKEN" && echo set || echo unset'。Build log 會把 stdout/stderr 全收進來,不小心開個 set -x 就會把值漏出去。詳見 https://cloud.google.com/build/docs/securing-builds/use-secrets
Private Worker Pool
預設的 Cloud Build pool 跑在 Google 託管的 VPC 裡。Private pool(也就是改名後的 「Cloud Build private pools」 服務,原本叫 「private worker pools」)讓你在自己專案的 VPC 裡擁有專屬 pool。
何時該用 private pool
只要符合下列任一條件,就請考慮 private pool:
- 建置需要打私有端點(只有 private IP 的 Cloud SQL、internal load balancer、透過 Cloud VPN 或 Interconnect 連到地端服務、自架 GitLab)。
- 你需要固定的出口 IP 給第三方加入允許清單。
- 需要更大的機器(
E2_STANDARD_32、N1_HIGHCPU_32)而不想在共用 pool 排隊。 - 企業層級的建置需要穩定可預期的並發量與專屬容量。
- 建置需要納入 VPC Service Controls 範圍。
建立 private pool
gcloud builds worker-pools create my-pool \
--project=my-project \
--region=us-central1 \
--worker-machine-type=e2-standard-8 \
--worker-disk-size=200GB \
--no-public-egress \
--peered-network=projects/my-project/global/networks/build-vpc
Pool 會跟你指定的 VPC 對等連線。--no-public-egress 會擋掉 worker 對公網的存取(外部依賴就得透過 NAT 或 Cloud Build 認得的 Private Google Access 取得)。Pool 是區域層級,挑離建置目標資源最近的區域。
把 build 導向到 pool
在 build YAML 的 options 裡引用 pool:
options:
pool:
name: 'projects/my-project/locations/us-central1/workerPools/my-pool'
或是直接在 trigger 設定上指定,這樣該 trigger 的每一次 build 都會走那個 pool。
成本取捨
Private pool 的每分鐘費率比共用 pool 高,也可以額外保留容量。對 build 量低的團隊來說,「共用 pool + Secret Manager 嚴管」的數學比較划算。對 build 量大或受合規約束的場域,private pool 帶來的 VPC 可達性與可預期效能會把成本賺回來。
Build Triggers:GitHub、GitLab、Bitbucket、手動、Webhook
Trigger 就是把程式碼事件轉成 build 的開關。Cloud Build 支援多種 trigger 來源,每種的設定與雷區都不太一樣。
GitHub trigger(Cloud Build GitHub App)
推薦的 GitHub 整合是 Cloud Build GitHub App,安裝到 repo 或 org 上。它透過 Pub/Sub 把事件送到 Cloud Build,支援 push trigger、pull request trigger 與 tag trigger。PR trigger 是殺手級功能:每個 PR 都有獨立的 build,狀態會以 check 形式回寫 GitHub。Branch filter 使用 regex(^main$、^feature/.*,或用 ^(?!main$).* 排除 main)。
GitLab 與 Bitbucket trigger
對 GitLab.com 與自架 GitLab 而言,Cloud Build 可透過 Secure Source Manager 整合或舊的 webhook 流程。Bitbucket Cloud 與 Bitbucket Server 則走 host connection(第二代)或 webhook trigger(第一代)。趨勢是往第二代(host connection)走,能拿到 PR 狀態、branch protection 與更豐富的事件過濾。
手動 trigger
手動 trigger 沒有來源事件;只有當有人在 Console 點 「Run」 或執行 gcloud builds triggers run my-trigger --branch=main 才會啟動。手動 trigger 適合用在升版閘門(「QA 簽過字後把這個映像檔部署到正式環境」)或一次性運維任務(service account 金鑰輪替)。
Webhook trigger
Webhook trigger 暴露出一個 URL,接受帶有共享 secret header 的 POST。任何能 POST JSON 的東西都能觸發它:Jira automation、Linear webhook、自訂 Slack slash command、內部 admin 工具。Trigger 會把 request body 作為 substitution 變數使用。對於真實資料來源不在 Git 上的混合流程很實用。
Cloud Source Repositories
如果你的 repo 在 Cloud Source Repositories(Google 自家託管的 Git),trigger 不必安裝 app 就能原生運作。CSR 多半是已經很深入 GCP、想用 IAM 管 Git 存取權的團隊在用,而不是去用 SaaS Git 供應商。
請務必為每個 trigger 設定 branch 或 tag filter。沒有 filter 的 trigger 會對每一次 push 都啟動 build,feature branch、hotfix branch、Dependabot bump 全都會打進來,吃光配額也搞亂審查視窗。最低要求是 ^main$ 跑正式環境,外加一個獨立的 PR trigger。詳見 https://cloud.google.com/build/docs/automating-builds/create-manage-triggers
Build Provenance 與 SLSA
供應鏈安全已經是基本配備。Cloud Build 會為每一次 build 產生 SLSA(Supply-chain Levels for Software Artifacts)provenance,證明什麼樣的原始碼產出什麼樣的 artifact。
Provenance 包含哪些內容
一份 Cloud Build SLSA provenance attestation 包含 source URI(Git repo 與 commit SHA)、builder 身分(https://cloudbuild.googleapis.com/...)、觸發它的 build trigger、build 設定的雜湊、輸入材料(被拉進來的其他 artifact)以及輸出 artifact 的 digest。這份 attestation 由 Google 簽署,與映像檔一起存放在 Artifact Registry 上。
讀取 provenance
gcloud artifacts docker images describe \
us-central1-docker.pkg.dev/my-project/app/api@sha256:abc... \
--show-provenance
輸出是符合 SLSA v1.0 in-toto schema 的 JSON。Sigstore 的 cosign verify-attestation 與 Google 的 binary-authorization 政策引擎都可以消費這份資料,用來決定要不要放行部署。
SLSA build level
在私有 pool、預設設定下執行的 Cloud Build 可達到 SLSA Build Level 3,是目前共用 CI 服務能達到的天花板。Level 3 代表 hermetic、無參數可篡改、隔離良好的建置,並且 provenance 不可偽造。共用公共 pool 的 build 通常落在 Level 2。
與 Binary Authorization 的整合
Binary Authorization 是這套 provenance 最自然的消費者。一條政策可以說「只能部署擁有 Cloud Build provenance、且來源 repo 是 github.com/myorg/api、trigger 是 prod-trigger 的映像檔」,就能拿到可驗證的供應鏈。政策在 GKE、Cloud Run、Cloud Functions 的部署環節都會強制執行。
Artifact 上傳到 GCS 與 Artifact Registry
Cloud Build 在 build 成功時的最後一個動作就是上傳 artifact。
images 欄位
最簡單的 artifact 上傳就是頂層的 images 欄位。列在裡面的每一個映像檔都會被推送到對應的 registry(Artifact Registry 或已棄用的 Container Registry),並登錄在 build metadata 裡。映像檔必須先在本機存在(前面某個 docker build step 已產生),而 registry 路徑必須在同一個專案,或是 build service account 有權推送的專案。
非映像檔輸出用 artifacts 欄位
非映像檔 artifact(JAR、ZIP、source map、SBOM)請改用 artifacts 區塊:
artifacts:
objects:
location: 'gs://my-build-artifacts/$BUILD_ID/'
paths: ['target/*.jar', 'sbom.json']
符合 glob 的檔案會在所有 step 成功後上傳到 Cloud Storage。Cloud Build 會把每個物件的 MD5 與時間戳記錄到 build metadata 中。
Artifact Registry 的優勢
Artifact Registry 在 2023 年取代了 Container Registry,現在是預設選項。它支援 Docker、Maven、npm、Python、apt、yum 以及通用格式。每個 repo 有獨立的 IAM、可納入 VPC Service Controls、可選區域或多區域放置。Docker 的 URI 形式是 <region>-docker.pkg.dev/<project>/<repo>/<image>:<tag>。舊的 gcr.io/<project>/<image> URI 雖然底層也已經由 Artifact Registry 提供,但新 repo 應該明確使用 pkg.dev 路徑。
Artifact Registry 的 repository URI 永遠包含區域、專案、repo 名稱:us-central1-docker.pkg.dev/my-project/app-repo/api:v1。Container Registry 舊的 gcr.io/<project>/api:v1 仍可用,但本質上是疊在 Artifact Registry 之上。新專案請直接建立明確的 Artifact Registry repository,不要依賴隱式的 gcr.io 別名。詳見 https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr
核准者 (Build Approver) 與手動核准閘門
有些 build 即使 trigger 的來源事件成立,也不該自動執行。Cloud Build 的 approval 機制會在 build 開頭暫停,等一位真人按下 「Approve」。
在 trigger 上設定核准
打開 trigger 的 require approval。當 trigger 觸發後,build 會進入 PENDING 狀態,並透過 Pub/Sub 或 email 通道把通知送到核准者。具備 cloudbuild.builds.approver 角色的核准者可以核准或拒絕。核准動作會把核准者身分寫進 build metadata。
Approver IAM 角色
roles/cloudbuild.builds.approver 跟 editor 角色是分開的。資深工程師可以核准正式環境部署,但不必擁有修改 YAML 的權限。這個職責分離在考試裡很常見:PCD 情境題常問該為「部署守門員」角色授哪一個 role。
真實場景的核准流程
常見模式:PR trigger 全自動跑、不需要核准(快速回饋)。main 分支的 CI trigger 全自動跑、產出並推送映像檔。另一個獨立的「部署到正式環境」的 trigger 走核准閘門,只在 tag push 或手動觸發時啟動,把已經建好的映像檔滾到 Cloud Run。這把「建置」(便宜、全自動)與「部署」(受控、較慢)拆開,核准只聚焦在部署這一步。
被拒絕的 build
被拒絕的 build 會把拒絕者與拒絕備註寫入 metadata,且不會執行任何 step。這是很好的稽核軌跡:「我們在週五下午五點拒絕了 v1.4.7 的正式部署,因為公司進入變更凍結期。」
多階段 Docker 與 Build Cache
大部分 Cloud Build 管線最終會產出一個 Docker 映像檔。映像檔的建置本身往往佔掉大部分的耗時與成本。
多階段 Dockerfile
多階段 Dockerfile 有多個 FROM 行。每個 stage 是一個 build 環境;最後一個 stage 才是最終上線版。一個典型的 Go 服務:
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/api ./cmd/api
FROM gcr.io/distroless/static-debian12
COPY --from=build /out/api /api
ENTRYPOINT ["/api"]
build stage 帶著完整 Go toolchain(很大、可變動),最終 stage 是 distroless/static-debian12(5MB、沒 shell、沒 package manager)。Runtime image 很小、攻擊面極小。
Docker layer cache
Cloud Build 可以利用 Docker 的 layer cache,跳過輸入沒變的 layer。兩種常見作法:
Kaniko cache:用 gcr.io/kaniko-project/executor 並傳 --cache=true --cache-ttl=24h。Layer cache 存在 Artifact Registry。首次 build 較慢,之後的 build 快很多。
Docker buildx 搭配 cache-to / cache-from:docker buildx build --cache-to=type=registry,ref=<repo>:cache --cache-from=type=registry,ref=<repo>:cache。在 buildx 層面達到同樣效果。
Cloud Build 的原始碼 cache
對於非 Docker 的相依快取(node_modules、.m2、.gradle),可以拿 Cloud Storage bucket 當人工 cache:
steps:
- name: 'gcr.io/cloud-builders/gsutil'
args: ['-m', 'rsync', '-r', 'gs://my-cache/node_modules', 'node_modules']
- name: 'node:20'
entrypoint: 'npm'
args: ['ci']
- name: 'gcr.io/cloud-builders/gsutil'
args: ['-m', 'rsync', '-r', 'node_modules', 'gs://my-cache/node_modules']
便宜、簡單,但 cache 失效由你自己處理。Node 專案在小型 repo 上,靠 lockfile 驅動的 npm ci 通常不需要 bucket 級別的 cache 就夠快。
並行度與機器規格
Cloud Build 預設機器是 E2_MEDIUM。對於要編譯 Rust 或大型 Java 應用的 Docker build,跑在 E2_HIGHCPU_8 或 E2_HIGHCPU_32 上常常快 10 倍。透過 options.machineType 調整並做基準量測;通常總價差不多,因為較高費率的機器會等比例更快完工。
Build IAM 與服務帳戶
任何 Cloud Build 設定中最容易出錯的部分就是 IAM。PCD 考試經常在這條線上出題。
預設 Cloud Build service account
每個專案會有一個預設的 Cloud Build service account:<PROJECT_NUMBER>@cloudbuild.gserviceaccount.com。Build 預設以這個帳戶身分執行。它預設帶有 cloudbuild.builds.builder 角色,這個角色看似廣(只能讀取 secret、推 Artifact Registry),但實際上仍要另外授予個別的 role。
為每個 trigger 指派自訂 service account
正式環境的最佳實踐:透過 serviceAccount 欄位為每個 trigger 指派自訂 service account。正式部署的 trigger 用 [email protected],給它較窄的權限(部署到 Cloud Run、讀正式環境 secret)。CI trigger 用 [email protected],只給 build 用的權限(推到 Artifact Registry、讀非正式環境 secret)。在不切割專案的情況下達到職責分離。
必背的 IAM 角色
roles/cloudbuild.builds.editor:建立、更新、刪除 trigger 與 build。權限很廣,視為開發人員寫權限。roles/cloudbuild.builds.viewer:唯讀存取 build 與 trigger。給稽核人員是安全的。roles/cloudbuild.builds.approver:核准或拒絕需要核准的 build。和 editor 是分開的。roles/cloudbuild.builds.builder:授予預設 service account 的角色,讓 service account 能執行 build。roles/iam.serviceAccountUser(在 build service account 上):要讓使用者或 trigger 在 build 過程「以該帳戶身分行事」就需要這個。
陷阱:給人 cloudbuild.builds.editor 等於讓對方能改 trigger 使用的 service account,實質上就是權限提升。請把 editor 視為特權角色。
Build IAM 職責分離 (Build IAM separation) 是「每個 trigger 用不同 service account」的設計模式,目的是把單一 trigger 被攻破時的影響範圍壓到最小。CI trigger 的 service account 不能部署到正式環境;正式環境部署 trigger 的 service account 不能讀非正式環境 secret。再搭配核准閘門與 Binary Authorization,就是縱深防禦 CI/CD 管線的基石。詳見 https://cloud.google.com/build/docs/cloud-build-service-account
常見陷阱與正式環境經驗
幾個會在正式環境咬人的模式,跟考題情境很像。
「共用 service account 啥都能做」的陷阱
第一天最方便,第一次事故時最痛。一個對整個專案具備 Editor 權限的 Cloud Build service account 可以部署到任何地方、讀每一個 secret、輪替每一把金鑰。請盡早拆開。
替換變數洩漏 secret
在 trigger 設定裡放 _DB_PASSWORD: 'hunter2',任何具備該 trigger editor 權限的人都看得到。Substitution 不是 secret,沒遮罩就會印在 build log。真正的 secret 一律走 Secret Manager + availableSecrets。
沒有 branch filter 的 trigger
沒設 filter 的 trigger 會對每一個 branch 的每一次 push 都觸發。Dependabot 升版了某個傳遞相依,就剛好用 prod 規格跑了一次 build。永遠設 filter。
長時 build 在 10 分鐘逾時
預設 timeout 是 600 秒。第一次拉乾淨 Maven cache 的 build 可能跑 15 分鐘。安全起見先設 timeout: 3600s,再隨著最佳化向下調整。
透過 API 繞過手動核准
只要使用者具備 cloudbuild.builds.editor,就能直接用 gcloud builds submit 送出 build,繞過需要核准的 trigger。核准閘門保護的是 trigger 那條路徑,不是底層 build API。請鎖緊 editor,並用 Binary Authorization 在部署端再把守一次。
常見問題 (FAQ)
我可以在本機跑 Cloud Build 做 debug 嗎?
可以。cloud-build-local 工具(雖然已是舊版,但仍能用)用 Docker 模擬 Cloud Build 環境。對於迭代 cloudbuild.yaml 而不想消耗雲端建置分鐘很有幫助,但本機模擬無法完全比照 Cloud Build 的網路或 service account 上下文。許多團隊現在改採快速的 PR trigger,跳過本機模擬。
我要如何在一次 build 中部署到多個區域?
加多個部署 step,每個對應一個區域,靠 waitFor 平行執行。每個 step 用不同 --region 呼叫 gcloud run deploy。映像檔只要推送一次到多區域 Artifact Registry repository,所有區域共用來源。如果區域跨洲,映像檔拉取的延遲會主導,這時建議每區域有一個區域 repo,由另一個 job 把映像檔複製過去,會更快。
Cloud Build 與 Cloud Deploy 的差別?
Cloud Build 是建置引擎:把原始碼變成 artifact。Cloud Deploy 是漸進交付引擎:把 artifact 推到各環境,含 promotion pipeline、核准閘門與 rollback。PCD 考試會要求你知道兩者互補:Cloud Build 產出映像檔,Cloud Deploy 在 dev > staging > prod 之間滾並做驗證。
Docker layer 跨次建置該怎麼快取?
用 Kaniko(gcr.io/kaniko-project/executor)搭配 --cache=true,或用 Docker buildx 搭配 --cache-to=type=registry,ref=<repo>:cache。兩者都把 layer cache 存在 Artifact Registry。第一次 build 速度正常;之後只動上層 layer 的 build 會快很多。不要依賴 build VM 自己的 Docker cache;build VM 是一次性的,build 結束 cache 就沒了。
Cloud Build 能不能從我們公司網路裡的 GitLab 拉程式碼?
可以,前提是 private pool 與一個透過 Cloud VPN 或 Interconnect 連到公司網路的 VPC 互相對等連線。Pool 的 worker 會走私有連線打 GitLab。事件端可用 webhook trigger,或者若你的 GitLab 在 GitLab.com 上,可改採第二代 GitLab 整合。
用最便宜的方式為部署加上手動核准閘門?
只在「部署」這一步用手動或需要核准的 trigger,CI 保持全自動。產映像檔的 build 不需要核准、走快車道。部署 trigger 要求 cloudbuild.builds.approver 角色,授給一個小群體。runtime 端再用 Binary Authorization 加一層,就有可驗證、低摩擦的閘門。
SLSA Build Level 3 與 Level 2 差在哪?
Level 2 要求版本控管的原始碼、腳本化建置、可驗證的 provenance。Level 3 再加上不可偽造的 provenance、隔離良好的建置(不能 SSH 進 builder、不能任意操弄環境)。Cloud Build private pool 達到 Level 3;共用公共 pool 通常達到 Level 2。考試可能會問怎樣的 Cloud Build 設定能達到 Level 3。
相關章節
- Artifact Registry 涵蓋大多數 Cloud Build 輸出的目的地,包含 IAM、漏洞掃描,以及 Docker/Maven/npm 等格式支援。
- Deployment Manager 與 Terraform 與 Cloud Build 搭配,用於 IaC 驅動的環境佈建,由 build 執行
terraform apply。 - Secret Manager 與 KMS 是 build 時期 secret 的來源,透過
availableSecrets呈現出來。
延伸閱讀
- Google Cloud, Cloud Build configuration file schema: https://cloud.google.com/build/docs/build-config-file-schema
- Google Cloud, Private pools overview: https://cloud.google.com/build/docs/private-pools/private-pools-overview
- Google Cloud, Using secrets from Secret Manager: https://cloud.google.com/build/docs/securing-builds/use-secrets
- Google Cloud, Creating and managing build triggers: https://cloud.google.com/build/docs/automating-builds/create-manage-triggers
- Google Cloud, View build provenance and SLSA: https://cloud.google.com/build/docs/securing-builds/view-build-provenance
- Google Cloud, Cloud Build service account: https://cloud.google.com/build/docs/cloud-build-service-account