GitHub Actions 自動部署(CD)到 GCP Cloud Run 完整教學
GitHub Actions 自動部署(CD)到 GCP Cloud Run 完整教學
本文聚焦在「程式碼推上 GitHub → 自動 build → 自動部署到 Cloud Run」這條 CD 管線的設定。
為了安全,所有專案 ID、專案編號、Service Account email、WIF 路徑等敏感資訊都以佔位符(
<...>)取代,實際操作時請替換成自己的值,切勿把明碼寫進公開文件。
整體流程
git push origin main | v GitHub Actions 觸發 | +- 透過 Workload Identity Federation 認證 GCP(免金鑰) +- docker build 映像檔 +- docker push 到 Artifact Registry +- gcloud run deploy 部署到 Cloud Run | v 瀏覽器開 Cloud Run URL 看到網站 ```
核心觀念:用 **Workload Identity Federation(WIF)** 建立 GitHub 與 GCP 的信任關係,GitHub Actions 不需要持有任何金鑰即可取得部署權限。
---
## 前置準備
### 1. 啟用必要的 GCP API
於 GCP Console 啟用:
- Cloud Run Admin API
- Artifact Registry API
- Cloud Build API
- Service Networking API
- Secret Manager API
### 2. 建立 Artifact Registry(存放 Docker 映像檔)
- Format:Docker
- Region:`asia-east1`
- Mode:Standard
完整路徑格式:
```
asia-east1-docker.pkg.dev/<PROJECT_ID>/<REPOSITORY>
```
### 3. 建立 Service Account 並授予角色
建立一個專供 GitHub 推送/部署使用的 Service Account,逐一加上以下 3 個角色:
| 角色 | 用途 |
|---|---|
| Artifact Registry **寫入者**(Writer) | 推送映像檔(注意不是「服務代理」) |
| Cloud Run 管理員 | 部署服務 |
| 服務帳戶使用者 | 部署時模擬執行身分 |
---
## 設定 Workload Identity Federation(免金鑰認證)
### 建立 Workload Identity Pool
- Pool ID:`github-pool`
### 新增 OIDC Provider
- Provider type:OpenID Connect (OIDC)
- Provider Name:`github-provider`
- Issuer URL:`https://token.actions.githubusercontent.com`
### 屬性對應(Attribute Mapping)
| Google | OIDC |
|---|---|
| `google.subject` | `assertion.sub` |
| `attribute.repository` | `assertion.repository` |
| `attribute.repository_owner` | `assertion.repository_owner` |
### 屬性條件(關鍵安全設定)
限制只有自己的 GitHub 帳號/組織能使用此 Provider:
```
assertion.repository_owner == '<GITHUB_OWNER>'
```
### 綁定 Service Account 與 WIF
到 Service Account 的「**具備存取權的主體(Principals with access)**」分頁,點「**授予存取權**」,新增主體:
```
principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/github-pool/attribute.repository/<GITHUB_OWNER>/<REPO_NAME>
```
角色:**Workload Identity 使用者**
> 注意:「這個 SA 能做什麼」在 IAM 頁編輯;「誰能用這個 SA」在 SA 的「具備存取權的主體」頁編輯,兩者方向不同,容易搞混。
---
## GitHub Secrets 設定
到 GitHub repo 的 `Settings -> Secrets and variables -> Actions`,新增 3 個 Secrets(實際值請填自己的,勿外流):
| Name | 內容說明 |
|---|---|
| `GCP_PROJECT_ID` | 你的 GCP 專案 ID |
| `GCP_WIF_PROVIDER` | `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/github-pool/providers/github-provider` |
| `GCP_SA_EMAIL` | 部署用 Service Account 的 email |
---
## GitHub Actions Workflow
於 `.github/workflows/push-image.yml`:
```yaml
name: Build, Push and Deploy to Cloud Run
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
id-token: write # WIF 取得 OIDC token 必須
env:
REGION: asia-east1
REPOSITORY: <REPOSITORY>
IMAGE_NAME: <IMAGE_NAME>
SERVICE_NAME: <SERVICE_NAME>
jobs:
build-push-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate to GCP
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
service_account: ${{ secrets.GCP_SA_EMAIL }}
- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker auth
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet
- name: Build Docker image
run: |
docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
- name: Push to Artifact Registry
run: |
docker push ${{ env.REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Deploy to Cloud Run
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image=${{ env.REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
--region=${{ env.REGION }} \
--platform=managed \
--allow-unauthenticated \
--port=8080 \
--memory=256Mi \
--cpu=1 \
--min-instances=0 \
--max-instances=2
- name: Show service URL
run: |
URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} --region=${{ env.REGION }} --format='value(status.url)')
echo "Service URL: $URL"
```
---
## 觸發部署
```bash
git add .
git commit -m "feat: setup CI/CD pipeline"
git push origin main
```
到 GitHub repo 的 **Actions** 分頁看執行狀況,跑完後開 Cloud Run 配發的 URL 即可看到部署結果。
---
## 踩過的坑(CD 相關)
### 坑 1:組織政策禁止建立 SA 金鑰
錯誤訊息:`服務帳戶金鑰建立功能已停用`(組織政策 `iam.disableServiceAccountKeyCreation`)。
解法:改用 Workload Identity Federation,完全不用金鑰 —— 這其實是更安全的業界最佳實踐。
### 坑 2:Artifact Registry 角色加錯
push 階段報錯:`denied: Permission 'artifactregistry.repositories.uploadArtifacts' denied`。
| 錯誤的角色 | 正確的角色 |
|---|---|
| Artifact Registry 服務代理(Service Agent) | Artifact Registry **寫入者**(Writer) |
### 坑 3:授予存取權的入口位置
要在 SA 詳情頁分頁列最右邊的「**具備存取權的主體 / Principals with access**」,才會看到正確的「+ 授予存取權」按鈕,而不是「權限」分頁。
### 坑 4:Cloud Run 要求容器必須監聽 HTTP port
只「跑完就結束」的容器會一直重啟。Cloud Run 是「無狀態 HTTP 服務」,容器必須監聽 port(預設 `8080`)。
---
## 安全提醒
- 切勿把專案 ID、專案編號、SA email、WIF 完整路徑等寫進公開文件;統一以 Secrets 管理。
- WIF 的屬性條件(`repository_owner`)務必設定,避免其他人的 repo 冒用你的 Provider。
- `permissions: id-token: write` 是 WIF 運作的必要條件,別漏掉。