Skip to main content

GitHub Actions 自動部署(CD)到 GCP Cloud Run 完整教學

本文聚焦在

本文聚焦在「「程式碼推上 GitHub → 自動 build → 自動部署到 Cloud Run」 這條 CD 管線的設定。

為了安全,所有專案⚠️ 安全提醒:文中所有專案 ID、專案編號、Service Account email、WIF 路徑等敏感資訊都以佔位符(路徑等敏感資訊,都以佔位符(<...>)取代,取代。實際操作時請替換成自己的值,切勿把明碼寫進公開文件。切勿把明碼寫進公開文件。


整體流程

整條 CD 管線只要一個 git push 就會自動跑完:

git push origin main |│ v▼ GitHub Actions 觸發 |│ +-├─ ① 透過 Workload Identity FederationWIF 認證 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 啟用:啟用以下 -API:

    Cloud Run Admin API - Artifact Registry API - Cloud Build API - Service Networking API - Secret Manager API ###

    2. 建立 Artifact Registry(存放 Docker 映像檔)

    -
    Format: 設定 值 Format Docker - Region:` Region asia-east1`east1 - Mode: 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:`ID:github-pool`pool
    ###

    新增 OIDC Provider

    -
    設定 值 Provider type:type OpenID Connect (OIDC) - Provider Name:`Name github-provider`provider - Issuer URL:`URL https://token.actions.githubusercontent.com`com ###

    屬性對應(Attribute Mapping)

    |
    Google |OIDC OIDC | |---|---||`google.subject`subject |assertion.sub ` attribute.repository assertion.sub`repository | |`attribute.repository`repository_owner |assertion.repository_owner `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`Actions,新增 3 個 Secrets(Secrets(實際值請填自己的,勿外流):

    |
    Name |內容說明 內容說明 | |---|---||`GCP_PROJECT_ID`GCP_PROJECT_ID | 你的 GCP 專案 ID | |`GCP_WIF_PROVIDER`GCP_WIF_PROVIDER | `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/github-pool/providers/github-provider`provider | |`GCP_SA_EMAIL`GCP_SA_EMAIL | 部署用 Service Account 的 email | --- ##

    GitHub Actions Workflow

    於 `.github/workflows/push-image.yml`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**Actions 分頁看執行狀況,分頁看執行狀況;跑完後開 Cloud Run 配發的 URL 即可看到部署結果。

    ---
    ##

    踩過的坑(CD 相關)

    ###

    🕳️ 坑 1:組織政策禁止建立 SA 金鑰

    錯誤訊息:`服務帳戶金鑰建立功能已停用`(組織政策

    錯誤訊息

    `
    服務帳戶金鑰建立功能已停用
    組織政策:iam.disableServiceAccountKeyCreation`)。disableServiceAccountKeyCreation
    解法:

    解法:改用 Workload Identity Federation,完全不用金鑰 —— 這其實是更安全的業界最佳實踐。

    ###

    🕳️ 坑 2:Artifact Registry 角色加錯

    push

    錯誤訊息

    階段報錯:`
    denied: Permission 'artifactregistry.repositories.uploadArtifacts' denied`。denied
    |
    錯誤的角色 |正確的角色 正確的角色 | |---|---|| Artifact Registry 服務代理(Service Agent) | Artifact Registry **寫入者**寫入者(Writer) | ###

    🕳️ 坑 3:授予存取權的入口位置

    要在 SA 詳情頁分頁列最右邊的「**詳情頁分頁列最右邊的「具備存取權的主體 / Principals with access**」access」,才會看到正確的「+ 授予存取權」按鈕,而不是「權限」分頁。

    ###

    🕳️ 坑 4:Cloud Run 要求容器必須監聽 HTTP port

    只「跑完就結束」的容器會一直重啟。Cloud Run 是「無狀態 HTTP 服務」,容器必須監聽容器必須監聽 port(port(預設 `8080`8080)。

    ---
    ##

    安全提醒

    安全提醒
    -🔒 切勿把專案 ID、專案編號、SA email、WIF 完整路徑等寫進公開文件;統一以 Secrets 管理。 -🔒 WIF 的屬性條件(`repository_owner`repository_owner)務必設定,避免其他人的 repo 冒用你的 Provider。 -🔒 `permissions: id-token: write`write 是 WIF 運作的必要條件,別漏掉。別漏掉。