用 Cloud Build 串起測試、建置與 Cloud Run 部署
Terraform 可以管資源設定;應用程式更新時,還是得測試、建映像、推送並部署。如果每次都由人手動執行這串指令,步驟很容易漏掉,也難查是哪個版本上線。
Cloud Build 可以把這些步驟寫進 cloudbuild.yaml,再由觸發器按條件執行。是否自動部署到正式環境,仍由團隊決定與設定。
這篇會先看 build、step 和 trigger 怎麼配合,再把 Artifact Registry、Secret Manager 與服務帳號權限接進部署流程。最後比較什麼情況適合用 Cloud Build,什麼情況沿用 GitHub Actions。

安全交付的核心是build once、promote the same digest:測試、掃描與 provenance 都綁在同一個不可變產物,環境推進不重新建置。部署後 telemetry 回到 SLO gate,決定繼續、暫停或讓 Cloud Deploy 取回 Registry 中已知良好的 digest;密碼與 API key 則由 Secret Manager 在執行時提供,不烤進 image。
一、為什麼需要 CI/CD?
手動部署的問題
開發者 → 手動建 Docker Image → 手動推送到 Registry
→ 手動 SSH 到 VM 或執行 gcloud run deploy
→ 手動驗證部署是否成功
→ 如果失敗,手動回滾
這個流程的問題:
- 容易出錯:手動步驟容易漏執行或順序錯誤
- 沒有一致性:每個開發者的部署步驟可能不同
- 無法稽核:誰在什麼時間部署了什麼版本?
- 速度慢:一個部署可能需要 30 分鐘人工操作
CI/CD 管道的目標
git push → 自動觸發
→ 執行測試(CI)
→ 建立 Docker Image
→ 推送到 Artifact Registry
→ 部署到 Cloud Run(CD)
→ 通知成功或失敗
每一步都自動跑,開發者就能專心寫程式。
二、Cloud Build 核心概念
2.1 什麼是 Cloud Build?
Cloud Build 是 GCP 的全託管建置服務。每個 Build 都跑在自己獨立的 worker 上(每個步驟各是一個容器),彼此不干擾,也能多個 Build 同時跑。
費用部分這裡列清楚:
- 免費額度:每月 2,500 Build 分鐘免費,預設機型為 e2-standard-2(自 2023-09-01 起,由舊制的每日 120 分鐘 / e2-medium 預設改成現在這樣)
- 超出計費:預設的 e2-standard-2 是 $0.006/Build 分鐘;若改用較小的 e2-medium 則是 $0.003/Build 分鐘
還有一件事先記在心裡:Cloud Build 會用一個預設服務帳號(service account)替你跑 build,後面授權 Artifact Registry、Cloud Run、Secret Manager 全都圍著這個帳號轉。先有這個概念,等下到 Secret 跟權限章節就不會被它突然冒出來嚇到。(補充:2024 年之後新建的專案,預設可能改用 Compute Engine 的預設服務帳號,第八章會講清楚兩者差別。)
2.2 Build 的三個核心元素
Build(建置任務)
├── Steps(建置步驟)— 有序或平行執行的動作
├── Triggers(觸發器)— 什麼事件觸發 Build
└── Artifacts(產物)— Build 的輸出(Docker Image、檔案等)
2.3 Build Step 的結構
每個 Build Step 都會使用指定的容器映像來執行指令:
steps:
- name: 'CONTAINER_IMAGE' # 要執行的 Docker 映像
id: 'STEP_ID' # 步驟識別碼(選填)
args: ['ARG1', 'ARG2'] # 傳給容器的指令和參數
env: ['KEY=VALUE'] # 環境變數
dir: 'path/in/workspace' # 工作目錄
waitFor: ['OTHER_STEP_ID'] # 等待哪些步驟完成後再執行
內建 Builder 映像(Cloud Build 維護的工具容器):
| Builder | 說明 |
|---|---|
gcr.io/cloud-builders/docker | Docker 指令 |
gcr.io/google.com/cloudsdktool/cloud-sdk | gcloud / gsutil 指令 |
gcr.io/cloud-builders/git | Git 指令 |
gcr.io/cloud-builders/npm | npm 指令 |
gcr.io/cloud-builders/go | Go 建置工具 |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
三、Artifact Registry:Docker 映像的正確存放位置
⚠️ 重要公告:Container Registry(gcr.io)已於 2025 年 3 月 18 日關閉,無法再寫入映像;GKE 自 2025 年 6 月 3 日起也無法再讀取存放在 Container Registry 的映像。所有新專案必須使用 Artifact Registry。
3.1 建立 Artifact Registry Docker 倉庫
# 啟用 Artifact Registry API
gcloud services enable artifactregistry.googleapis.com
# 建立 Docker 倉庫(以 asia-east1 為例)
gcloud artifacts repositories create my-docker-repo \
--repository-format=docker \
--location=asia-east1 \
--description="Application Docker images"
# 查看倉庫列表
gcloud artifacts repositories list
3.2 Artifact Registry 映像路徑格式
LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG
# 範例:
asia-east1-docker.pkg.dev/my-project-123/my-docker-repo/my-app:v1.0.0
asia-east1-docker.pkg.dev/my-project-123/my-docker-repo/my-app:latest
對比 Container Registry(舊,已棄用):
# 既有 gcr.io 網址(若已遷到 Artifact Registry,仍可繼續使用)
gcr.io/my-project/my-app:v1.0
asia.gcr.io/my-project/my-app:latest
# 新建一般 Artifact Registry repository 常用的網址
asia-east1-docker.pkg.dev/my-project/my-repo/my-app:v1.0
📝 考場提點
Container Registry 已停止服務,新專案應使用 Artifact Registry。不過 Artifact Registry 也支援
gcr.io網址;看到映像路徑時,要確認它實際由哪個服務託管,不能只憑網域名稱判斷。若舊專案仍依賴 Container Registry 的儲存與權限設定,需要檢查映像和自動化流程是否都已遷移;單純改網址或 Region,不能代替這些驗證。
3.3 設定本地 Docker 認證(測試用)
# 設定 Docker 認證到 Artifact Registry
gcloud auth configure-docker asia-east1-docker.pkg.dev
# 測試推送
docker tag my-app:latest asia-east1-docker.pkg.dev/my-project/my-repo/my-app:latest
docker push asia-east1-docker.pkg.dev/my-project/my-repo/my-app:latest
四、撰寫 cloudbuild.yaml
4.1 基本範例:測試 → 建置 → 推送 → 部署
以下是一個完整的 Cloud Build 設定,把 Node.js 應用程式部署到 Cloud Run:
# cloudbuild.yaml
steps:
# Step 1: 安裝依賴套件
- name: 'node:24'
id: 'install'
entrypoint: 'npm'
args: ['ci']
# Step 2: 執行單元測試
- name: 'node:24'
id: 'test'
entrypoint: 'npm'
args: ['test']
waitFor: ['install']
# Step 3: 建立 Docker Image
- name: 'gcr.io/cloud-builders/docker'
id: 'build'
args:
- 'build'
- '-t'
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app:$COMMIT_SHA'
- '-t'
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app:latest'
- '.'
waitFor: ['test']
# Step 4: 推送到 Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
id: 'push'
args:
- 'push'
- '--all-tags'
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app'
waitFor: ['build']
# Step 5: 部署到 Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: 'deploy'
args:
- 'gcloud'
- 'run'
- 'deploy'
- 'my-service'
- '--image=asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app:$COMMIT_SHA'
- '--region=asia-east1'
- '--allow-unauthenticated'
waitFor: ['push']
# 聲明 Build 產出的映像(Cloud Build 會記錄到 Build 歷史)
images:
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app:$COMMIT_SHA'
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-docker-repo/my-app:latest'
# 設定 Build 逾時時間(預設 60 分鐘)
timeout: '1200s'
# 選項設定
options:
logging: CLOUD_LOGGING_ONLY # 日誌只送到 Cloud Logging
4.2 內建替換變數(Substitution Variables)
Cloud Build 自動提供以下變數供 cloudbuild.yaml 使用:
| 變數 | 說明 | 範例值 |
|---|---|---|
$PROJECT_ID | GCP 專案 ID | my-project-123456 |
$BUILD_ID | 此次 Build 的唯一 ID | abc123-def456-... |
$COMMIT_SHA | 完整的 Git commit hash | a1b2c3d4e5f6... |
$SHORT_SHA | 前 7 碼 commit hash | a1b2c3d |
$BRANCH_NAME | 觸發 Build 的分支名稱 | main、feature/new-api |
$TAG_NAME | 觸發 Build 的 Git tag | v1.0.0 |
$REPO_NAME | 倉庫名稱 | my-app-repo |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
# 使用變數範例
- name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- 'asia-east1-docker.pkg.dev/$PROJECT_ID/my-repo/my-app:$SHORT_SHA'
- '--label=build-id=$BUILD_ID'
- '--label=branch=$BRANCH_NAME'
- '.'
4.3 自訂替換變數
你可以在 cloudbuild.yaml 中定義自己的變數(名稱必須以 _ 開頭):
substitutions:
_SERVICE_NAME: 'my-service'
_REGION: 'asia-east1'
_MIN_INSTANCES: '1'
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
args:
- 'gcloud'
- 'run'
- 'deploy'
- '$_SERVICE_NAME'
- '--region=$_REGION'
- '--min-instances=$_MIN_INSTANCES'
五、平行執行 Build Steps
5.1 為什麼要平行執行?
預設情況下,Steps 是一個接一個跑。但如果前後端可以各自獨立建置,讓它們平行跑就能省下不少 Build 時間。
5.2 平行執行語法
steps:
# Step 1 和 Step 2 同時開始(waitFor: ['-'] = 立刻開始)
- name: 'gcr.io/cloud-builders/docker'
id: 'build-frontend'
args:
[
'build',
'-t',
'asia-east1-docker.pkg.dev/$PROJECT_ID/repo/frontend:$COMMIT_SHA',
'./frontend',
]
waitFor: ['-'] # '-' 表示不等待任何步驟,立刻開始
- name: 'gcr.io/cloud-builders/docker'
id: 'build-backend'
args:
['build', '-t', 'asia-east1-docker.pkg.dev/$PROJECT_ID/repo/backend:$COMMIT_SHA', './backend']
waitFor: ['-'] # 同樣立刻開始,與 build-frontend 平行執行
# Step 3 等 Step 1 和 Step 2 都完成後才執行
- name: 'gcr.io/cloud-builders/docker'
id: 'push-frontend'
args: ['push', 'asia-east1-docker.pkg.dev/$PROJECT_ID/repo/frontend:$COMMIT_SHA']
waitFor: ['build-frontend']
- name: 'gcr.io/cloud-builders/docker'
id: 'push-backend'
args: ['push', 'asia-east1-docker.pkg.dev/$PROJECT_ID/repo/backend:$COMMIT_SHA']
waitFor: ['build-backend']
# 最後的部署等兩個推送都完成
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: 'deploy'
args:
[
'gcloud',
'run',
'deploy',
'my-service',
'--region=asia-east1',
'--image=asia-east1-docker.pkg.dev/$PROJECT_ID/repo/backend:$COMMIT_SHA',
]
waitFor: ['push-frontend', 'push-backend']
執行流程示意:
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB
S["Build 開始"] --> F["build-frontend<br/>waitFor: ['-']"]
S --> B["build-backend<br/>waitFor: ['-']"]
F --> PF["push-frontend<br/>waitFor: build-frontend"]
B --> PB["push-backend<br/>waitFor: build-backend"]
PF --> J{"兩個 push<br/>都完成?"}
PB --> J
J -->|"是"| D["deploy<br/>waitFor: 兩個 push"]
J -->|"任一步驟失敗"| X["Build 失敗,不部署"] 六、Secret Manager 整合:安全管理建置密碼
6.1 為什麼不能把密碼放在 cloudbuild.yaml?
cloudbuild.yaml 是放在 git 倉庫裡的,任何有倉庫存取權的人都看得到。所以密碼絕對不能硬寫在這裡。
6.2 使用 Secret Manager 注入密碼
前置作業:把密碼存到 Secret Manager
# 建立 Secret
gcloud secrets create db-password \
--replication-policy="automatic"
# 設定 Secret 的值
echo -n "my-super-secret-db-password" | \
gcloud secrets versions add db-password --data-file=-
# 授予 Cloud Build SA 讀取 Secret 的權限(這裡用 legacy SA;新專案請換成實際執行 Build 的服務帳號,見 8.1)
gcloud secrets add-iam-policy-binding db-password \
--member="serviceAccount:$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')@cloudbuild.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
在 cloudbuild.yaml 中使用 Secret:
# 聲明 Secret 來源
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/db-password/versions/latest
env: 'DB_PASSWORD' # secretEnv 名稱,不需底線前綴
- versionName: projects/$PROJECT_ID/secrets/api-key/versions/latest
env: 'API_KEY'
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: 'run-migration'
entrypoint: 'bash'
args:
- '-c'
- |
gcloud run jobs execute db-migration \
--region=asia-east1 \
--update-env-vars DB_PASSWORD=$$DB_PASSWORD
secretEnv: ['DB_PASSWORD'] # 宣告此步驟使用的 Secret 變數
- name: 'node:24'
id: 'integration-test'
entrypoint: 'bash'
args:
- '-c'
- 'API_KEY=$$API_KEY npm run test:integration'
secretEnv: ['API_KEY']
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
sequenceDiagram
participant B as Cloud Build
participant SA as Build Service Account
participant SM as Secret Manager
participant S as 指定 Build Step
B->>SA: 以建置服務帳號執行
SA->>SM: 請求指定 secret version
SM->>SM: 驗證 secretAccessor 權限
alt 權限通過
SM-->>S: 以 secretEnv 注入到該 Step
S->>S: bash 以 $$VARIABLE 在執行期展開
S-->>B: 回傳成功/失敗,不把值寫入設定
else 權限不足
SM-->>B: PERMISSION_DENIED,停止 Build
end 💡 重要語法:
- Secret 環境變數名稱不需要以底線開頭,可直接用一般命名如
DB_PASSWORD、API_KEY(官方範例就用PASSWORD、USERNAME、GH_TOKEN等)。需要以底線_開頭的是使用者自訂的替換變數(substitutions)(如_DEPLOY_ENV),兩者是不同概念,別混淆。- 在 bash 腳本中引用時用雙錢字號
$$DB_PASSWORD(單個$是 Cloud Build 替換,$$才會跳脫成字面$,讓 bash 在執行期取得 Cloud Build 注入的 Secret 值)
七、設定觸發器(Triggers)
7.1 觸發器類型
| 觸發器類型 | 使用場景 |
|---|---|
| Push to branch | 推送到 main 分支自動部署 |
| Push to tag | 打 v1.0.0 tag 觸發 Production 部署 |
| Pull request | PR 開啟時執行測試(不部署) |
| Manual | 手動從 Console 觸發 |
| Pub/Sub | 接收 Pub/Sub 訊息觸發 |
| Webhook | 接收 HTTP 請求觸發 |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。

上面那張表的六種觸發器,在 Console 裡就是這一排單選鈕:推送至分支版本、推送新標記、提取要求(PR)、手動叫用、Pub/Sub、Webhook——你選哪一種,就決定了 cloudbuild.yaml 裡能拿到哪些替換變數(選分支才有 $BRANCH_NAME、選標記才有 $TAG_NAME)。要做到「只在 main 自動部署」,就是選第一顆「推送至分支版本」再把分支填成 ^main$;選「提取要求(PR)」則照慣例只跑測試、不部署。另外別漏了下方的「存放區代別」——第 1 代是舊的、第 2 代才是官方現在推薦的(7.2 連 GitHub 用的就是它),新專案接倉庫記得挑第 2 代。
7.2 連接 GitHub 倉庫(第二代,推薦)
# Step 1: 連接 GitHub 倉庫(第二代)
# 在 GCP Console → Cloud Build → Repositories → 2nd gen 分頁,
# 建立 host connection(會安裝 Cloud Build GitHub App)並連結倉庫
# Step 2: 建立 Push to Main 觸發器
# 第 2 代倉庫用 --repository 與 --region(舊的 --repo-name/--repo-owner 是第 1 代寫法);
# 新專案的預設服務帳號不是 legacy SA,建立觸發器時要用 --service-account 指定(見第八章)
gcloud builds triggers create github \
--name="deploy-on-push-to-main" \
--repository="projects/my-project/locations/asia-east1/connections/my-connection/repositories/my-app-repo" \
--region="asia-east1" \
--branch-pattern="^main$" \
--build-config="cloudbuild.yaml" \
--service-account="projects/my-project/serviceAccounts/custom-build-sa@my-project.iam.gserviceaccount.com" \
--description="Deploy to Cloud Run on push to main"
# Step 3: 建立 PR 測試觸發器(預設要有協作者在 PR 留言 /gcbrun 才會跑;要每個 PR 自動跑請加 --comment-control=COMMENTS_DISABLED)
gcloud builds triggers create github \
--name="test-on-pull-request" \
--repository="projects/my-project/locations/asia-east1/connections/my-connection/repositories/my-app-repo" \
--region="asia-east1" \
--pull-request-pattern=".*" \
--build-config="cloudbuild.pr.yaml" \
--service-account="projects/my-project/serviceAccounts/custom-build-sa@my-project.iam.gserviceaccount.com" \
--description="Run tests on pull request"
7.3 多環境部署策略
常見的分支策略:
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB F["feature/*"] --> PR["Pull Request Trigger<br/>測試與 lint,不部署"] PR --> D["develop"] D --> S["staging"] S --> TS["Staging Trigger"] TS --> BS["建置並部署<br/>Staging Service"] S --> M["main"] M --> TP["Production Trigger"] TP --> G["建置、測試與審批 Gate"] G --> BP["部署 Production Service"]
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: 'deploy'
entrypoint: 'bash'
args:
- '-c'
- |
if [ "$BRANCH_NAME" = "main" ]; then
gcloud run deploy my-app-prod \
--image=asia-east1-docker.pkg.dev/$PROJECT_ID/repo/my-app:$COMMIT_SHA \
--region=asia-east1
elif [ "$BRANCH_NAME" = "staging" ]; then
gcloud run deploy my-app-staging \
--image=asia-east1-docker.pkg.dev/$PROJECT_ID/repo/my-app:$COMMIT_SHA \
--region=asia-east1
fi
八、服務帳號與最小權限設定
8.1 Cloud Build 預設服務帳號
Cloud Build 會用一個預設服務帳號替你跑 Build,而「預設是哪一個」在 2024 年改過,新手最容易在這裡踩坑,先講清楚:
-
新專案(現行預設):Google 在 2024 年 5~6 月分批改了行為,新專案的第一個 Build 之後,Cloud Build 預設改用 Compute Engine 預設服務帳號:
{PROJECT_NUMBER}-compute@developer.gserviceaccount.com -
既有專案(舊行為):如果你的專案在這次變更之前就已經跑過第一個 Build,它會沿用舊的 legacy 服務帳號,格式是:
{PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
換句話說,判斷依據不是「專案哪年建的」,而是「第一個 Build 是在變更前還是變更後跑的」。所以如果你在新專案裡照著 @cloudbuild.gserviceaccount.com 找半天找不到,先別懷疑自己——去 Cloud Build 設定看一下實際是哪一個 SA 在跑 build,下面的授權對象就綁那一個。(官方說明:Cloud Build default service account change)
8.2 常見任務所需的 IAM 角色
# 取得 PROJECT_NUMBER
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
CLOUDBUILD_SA="${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" # 新專案請換成 ${PROJECT_NUMBER}-compute@developer.gserviceaccount.com 或你的自訂服務帳號(見 8.1)
# 允許推送到 Artifact Registry
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/artifactregistry.writer"
# 允許部署到 Cloud Run
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/run.admin"
# 允許讓 Cloud Run 使用指定的 Service Account(Cloud Run 部署需要)
gcloud iam service-accounts add-iam-policy-binding \
cloud-run-sa@${PROJECT_ID}.iam.gserviceaccount.com \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/iam.serviceAccountUser"
# 允許讀取 Secret Manager 的 Secret
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/secretmanager.secretAccessor"
📝 考場提點
Cloud Build 部署 Cloud Run 時跳
PERMISSION_DENIED,是 Cloud Build 部署時很典型的錯誤。答案要同時抓兩個角色:
roles/run.admin:讓 Build SA 能管理 Cloud Run 服務(建立、更新、部署)roles/iam.serviceAccountUser:讓 Build SA 能把 Cloud Run 的 runtime 服務帳號「附加到服務」(actAs)只選
run.admin、漏掉serviceAccountUser是容易掉進去的陷阱選項——少了後者,部署時就會卡在無法把 runtime SA 附加到服務(actAs)。另外提醒:新專案的預設 Build SA 可能是 Compute Engine 預設 SA(見 8.1),授權對象別綁到那個已經不在跑 build 的舊 SA 上。

在 Console 裡把角色指派給 Cloud Build 服務帳號,就是在這個選單做取捨。上面「基本」那三顆(Owner/Editor/Viewer)權限大到危險——給一個 Editor 幾乎等於把整個專案交出去;『最小權限』的做法是往下挑 predefined 預先定義角色,像你前面授的 run.admin、artifactregistry.writer,剛好夠用、不多給一分。萬一這個 SA 被入侵,攻擊者能碰的也就只有你勾的那幾格。
8.3 使用自訂服務帳號(最小權限原則)
# cloudbuild.yaml 中指定自訂服務帳號
serviceAccount: 'projects/my-project/serviceAccounts/custom-build-sa@my-project.iam.gserviceaccount.com'
steps:
- name: '...'
args: [...]
options:
logging: CLOUD_LOGGING_ONLY # 指定自訂服務帳號時必須自己指定日誌去處
九、檢視 Build 歷史與除錯
9.1 檢視 Build 歷史
# 列出最近的 Build
gcloud builds list --limit=10
# 查看特定 Build 的詳細資訊
gcloud builds describe BUILD_ID
# 串流顯示 Build 日誌(等待 Build 完成)
gcloud builds log --stream BUILD_ID
# 手動觸發 Build(不需要 push)
gcloud builds submit --config=cloudbuild.yaml .
9.2 推送前先手動送出 Build 測試 cloudbuild.yaml
在推送到 GitHub 前,可以先用 gcloud builds submit 從本機手動送出一次 Build(實際仍在 Cloud Build 上執行):
# 上傳當前目錄的程式碼,執行 cloudbuild.yaml
gcloud builds submit --config=cloudbuild.yaml .
# 手動指定內建替換變數的值(本機送出不會像觸發器那樣自動帶入)
gcloud builds submit --config=cloudbuild.yaml \
--substitutions=BRANCH_NAME=test,COMMIT_SHA=local-test .
9.3 常見錯誤與解決方式
| 錯誤 | 原因 | 解決方式 |
|---|---|---|
permission denied 推送映像 | Cloud Build SA 無 Artifact Registry 寫入權限 | 授予 artifactregistry.writer 角色 |
| Cloud Run 部署失敗 | Cloud Build SA 無 Cloud Run 管理權限 | 授予 run.admin 和 iam.serviceAccountUser 角色 |
| Secret 讀取失敗 | 未授予 secretmanager.secretAccessor | 對特定 Secret 或整個專案授予此角色 |
| Build 逾時 | 預設 60 分鐘逾時 | 在 cloudbuild.yaml 設定 timeout: '1800s' |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
十、GitHub Actions vs Cloud Build
兩者都能做 GCP 的 CI/CD,怎麼選就看你的情境:
| 比較面向 | Cloud Build | GitHub Actions |
|---|---|---|
| 最適合 | GCP 原生整合、需要存取私有 VPC | 程式碼在 GitHub、跨平台部署 |
| GCP 整合 | 原生支援,直接使用 GCP 服務 | 需自行設定認證(官方建議用 Workload Identity Federation) |
| 私有網路 | 支援(透過 Private Pools) | 自架 runner 可放進自己的網路;託管 runner 只有 larger runner 可接 Azure VNET |
| 社群資源 | 較小 | 龐大(數以萬計的公開 Actions) |
| 免費額度 | 每月 2,500 分鐘 | GitHub 提供的 runner 分鐘數 |
| 學習曲線 | 中等 | 較低(有大量範例) |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
表格裡的 Workload Identity Federation(WIF) 是讓 GitHub 不用存任何 GCP 金鑰、就能換取 GCP 短期憑證來部署的機制。它避免你把長期 service account 金鑰塞進 GitHub Secrets(金鑰外洩風險高);細節不必背,知道「GitHub Actions 要打 GCP,官方建議用 WIF 而不是貼金鑰」就夠了。
實務上常見的混搭:GitHub Actions 跑 CI,Cloud Build 跑 CD
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB
PR["Pull Request"] --> GH["GitHub Actions CI"]
GH --> T["Unit Test/Lint/PR<br/>Checks"]
T --> G{"CI 通過並合併?"}
G -->|"否"| X["阻擋合併"]
G -->|"是"| W["以 Workload Identity Federation<br/>取得短效 GCP 憑證"]
W --> CB["觸發 Cloud Build CD"]
CB --> AR["建置並推送<br/>Artifact Registry"]
AR --> M["執行 Migration/<br/>部署前檢查"]
M --> D["部署 Cloud Run/GKE"] 十一、ACE 考試重點
現行 ACE 考綱沒有點名 Cloud Build,但 Section 3.1 列了「Deploying new versions of a Cloud Run application」和「Configuring GKE to access Artifact Registry」,這一章的題型可以對著這兩項準備。
類型 1:Container Registry vs Artifact Registry
題目:你在 cloudbuild.yaml 中使用 gcr.io/my-project/my-app:latest 作為映像路徑。這個做法有什麼問題?
- ✅ Container Registry 已棄用,應改用 Artifact Registry(
asia-east1-docker.pkg.dev/my-project/my-repo/my-app:latest)
類型 2:服務帳號權限
題目:Cloud Build 執行 gcloud run deploy 時出現 PERMISSION_DENIED 錯誤。最可能的原因是?
- ✅ Cloud Build 的服務帳號缺少
roles/run.admin角色,或缺少roles/iam.serviceAccountUser(用於指定 Cloud Run 的服務帳號)
類型 3:平行建置
題目:你的 cloudbuild.yaml 有前端和後端兩個獨立的 Docker 建置步驟,預設會依序執行。如何讓它們同時執行?
- ✅ 在兩個步驟加上
waitFor: ['-'],讓它們立即並行啟動
類型 4:觸發器設定
題目:你希望只在推送到 main 分支時才自動部署到生產環境,其他分支的 push 不觸發部署。如何設定?
- ✅ 建立 Cloud Build Trigger,Branch 設定為
^main$(正規表達式精確匹配)
類型 5:Secret 管理
題目:Cloud Build 的 cloudbuild.yaml 需要使用資料庫密碼,如何安全地傳入?
- ❌ 直接把密碼寫在 cloudbuild.yaml
- ❌ 存到 git 倉庫的
.env檔案 - ✅ 存到 Secret Manager,在
availableSecrets中引用,用secretEnv注入到 Build Step
十二、完整實戰流程總結
# === 一次性設定 ===
# 1. 建立 Artifact Registry
gcloud artifacts repositories create my-docker-repo \
--repository-format=docker \
--location=asia-east1
# 2. 授予執行 Build 的服務帳號必要權限
# 新專案預設是 Compute Engine 預設服務帳號;2024 年變更前就跑過 Build 的舊專案才是
# ${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com(見 8.1)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
--format='value(projectNumber)')
CLOUDBUILD_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/artifactregistry.writer"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/run.admin"
# 讓 Build 能把日誌寫進 Cloud Logging
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/logging.logWriter"
# 部署 Cloud Run 還要能把 runtime 服務帳號附加到服務(actAs);
# cloudbuild.yaml 沒另外指定,runtime 就是 Compute Engine 預設服務帳號
gcloud iam service-accounts add-iam-policy-binding \
"${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--member="serviceAccount:${CLOUDBUILD_SA}" \
--role="roles/iam.serviceAccountUser"
# 3. 建立 GitHub 連接(在 GCP Console 操作)
# Cloud Build → Repositories → 2nd gen → Create host connection,再連結倉庫
# 4. 建立觸發器(第 2 代倉庫)
gcloud builds triggers create github \
--name="deploy-on-push-main" \
--repository="projects/${PROJECT_ID}/locations/asia-east1/connections/my-connection/repositories/my-app" \
--region="asia-east1" \
--branch-pattern="^main$" \
--build-config="cloudbuild.yaml" \
--service-account="projects/${PROJECT_ID}/serviceAccounts/${CLOUDBUILD_SA}"
# === 之後每次 push 都自動執行 ===
git add .
git commit -m "feat: add new feature"
git push origin main
# → 自動觸發 Cloud Build
# → 測試 → 建置 → 推送 → 部署
總結
| 概念 | 要記住的重點 |
|---|---|
| Artifact Registry | Container Registry 已棄用,必須用 Artifact Registry |
| 映像路徑格式 | LOCATION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG |
| Secret 變數 | 用 secretEnv 宣告,腳本中以雙錢字號 $$SECRET_NAME 引用(名稱不需底線前綴;底線前綴是「自訂替換變數 substitutions」的規則,如 _MY_VAR) |
| 平行執行 | waitFor: ['-'] 讓步驟立刻開始,不等前一步 |
| 服務帳號 | 需要 artifactregistry.writer + run.admin + serviceAccountUser(PERMISSION_DENIED 排查見第八章考場提點) |
| 免費額度 | 每月 2,500 Build 分鐘免費(預設機型 e2-standard-2) |
| 觸發器 | ^main$ 正規表達式精確匹配分支名稱 |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
系列文章連結
- 📖 上一篇:ACE-207: Terraform on GCP 實戰
- 📖 相關閱讀:ACE-206: GKE 入門
- 📖 相關閱讀:ACE-203: 無伺服器選型:Cloud Run
延伸閱讀
- Cloud Build 官方文件
- cloudbuild.yaml 語法參考
- Artifact Registry 快速入門
- Container Registry 遷移指南
- Cloud Build 觸發器設定
- Secret Manager 整合
- Cloud Build 定價
🎓 本文是「GCP 精通之路」系列的 ACE-208 篇(進階篇),掌握 Cloud Build CI/CD 後,建議繼續學習 GCP 企業上雲遷移策略(ACE-301)。
下一課 GCP-108:Cloud SQL 入門,學習托管關聯式資料庫的完全指南。
讀完後,留一個學習紀錄
試著用自己的話說出一個重點,以及你會如何運用。實作文章請先核對結果與清理資源,再標記為讀完。
文章進度以你確認讀完的標記為準,可隨時取消。舊版造訪紀錄保留,但不計入讀完;這份自評不代表已掌握內容。