提高開發效率!用 GitHub Action 自動部署 Docker 容器到 Cloud Run 教學

github action cloud run自動部署教學

每次上傳新的 Docker Image、要部署到 Google Cloud Run 時,都要另外打 gcloud 指令實在是有點繁瑣,本篇教學會與你分享怎麼利用 GitHub Action,在你每次 commit 後,就能自動同時部署到 Cloud Run 上,不用再打部署指令,簡化開發流程!

前情提要

這是筆者第一篇程式的技術學習筆記,所以在解釋跟教學步驟上會略顯繁瑣、或是有需改善的地方,還請大佬或是讀者不吝指教。

而筆者盡可能將此文的技術門檻降至最低,所以會含括一些前置步驟的教學,如果你已經設定過可以直接略過!

Cloud Run + GitHub Action:省去手動部署的流程

GitHub Action 是 Github 的 CI 功能,簡而言之就是把 commit 後的 Build、Test、Deploy 的流程全都自動化,不用手動另外執行( 就是做 Jenkins 的工作 )

本篇文章技術的( 梗圖 )可視化

這位前輩的文章有更完整的解釋:GitHub Actions 基本介紹 – 開始自動化 workflow 的第一步 – iT 邦幫忙

那麽 GitHub Action 對於要部署到 Cloud Run 的專案有什麼好處呢?透過設定 GitHub Action,你可以將這些過程全都自動化:

  1. 完成Google Cloud & Docker 登入驗證
  2. 建立程式的 Docker 容器
  3. 提交 Docker 容器到 Artifact Registry
  4. 將容器部署到 Cloud Run 服務

而且是在每次新的 commit 後就會自動完成,你只需專注做程式碼的開發即可,是不是超方便的?

胡適沒有說過這句話

不過補充一下,GitHub Actions 是有額度限制的,超過額度後要付費才能繼續用,以下是 GitHub Action 的免費使用額度,收費標準以執行時間為單位:

  • GitHub 免費版:每個月 2,000 分鐘
  • GitHub Pro:每個月 3,000 分鐘
  • GitHub 公開 Repo: 不限次數免費使用

補充資料:GitHub 官方 Actions 收費說明頁面

開始之前,你應該要先 …

在進到教學正文之前,會需要你先做以下三件事:

  • 略懂 Docker 跟 GCP Cloud Run 的用途
  • 寫好專案的 Dockerfile
  • 在 GCP 建立好自己的專案,並取得專案編號

如果做好前置作業,就照著下文設定你的 GitHub Action 吧!整個過程大約花費 10 分鐘。

步驟一:設定 Google Cloud 專案的 Identity federation

本篇文章會教學用 Google 官方的 YAML,而針對 Auth 部分,會需要用到專案的 Workload Identity Federation 來進行 GitHub Action 的 OIDC 驗證,因此,這段會先教學如何建立 GCP 專案的 Identity federation。

下文會用 gcloud 指令進行操作,如果你的電腦還沒有裝 Google Cloud SDK,或之前都是用 GUI 網頁來部署的話,可以照著這篇文章的教學安裝 SDK 並完成設定:如何安裝 Google Cloud SDK 命令行工具?macOS 就是那麼簡單 – Python 編程.圖表

雖然上篇文章是 MacOS 版本的,但安裝過程跟 Windows 類似 ~

安裝好 SDK 並登入管理 GCP 的 Google 帳號後,打開 Terminal 先設定一個環境變數,省去重複打編號的過程:

export PROJECT_ID="你的專案 ID"

接著創建一個專案服務帳戶,你可以修改引號內的帳戶 ID( 如果你已經有一個專案服務帳戶,可以略過這步,並記下你現有專案服務帳戶的 email 位置 ):

gcloud iam service-accounts create "github-service-account" \
  --project "${PROJECT_ID}"

再來透過這個指令,新增一個 Workload Identity 集區( Workload Identity Pool )你可以自訂 “github-pool” 跟 display-name 的名稱:

gcloud iam workload-identity-pools create "github-pool" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --display-name="GitHub Deployment Poll"

接著輸入這段指令取得集區的編號:

gcloud iam workload-identity-pools describe "github-pool" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --format="value(name)"

應該會得到類似 projects/111111111/locations/global/workloadIdentityPools/github-pool 的輸出結果,將這組集區編號設定成環境變數,方便等等重複使用:

export WORKLOAD_IDENTITY_POOL_ID="..."

再來為集區新增一個 Provider:

gcloud iam workload-identity-pools providers create-oidc "github-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="github-pool" \
  --display-name="Github Provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
  --issuer-uri="https://token.actions.githubusercontent.com"

p.s. 如果你要建立全部 repo 權限的 Provider,attribute-mapping 要改成這個:

--attribute-mapping="google.subject=assertion.sub,attribute.repository_owner=assertion.repository_owner"

再來為服務帳號綁定工作集區的使用者 IAM 權限,先設定你的 Repo 名稱作為環境變數,將引號改成你的 GitHub username 和 repo 英文代號:

export REPO="github_username/repo_name"

再輸入這段指令:

gcloud iam service-accounts add-iam-policy-binding "github-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
  --project="${PROJECT_ID}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"

如果你要套用於全部 repo 的 GitHub Action,先將你的 GitHub name 設為環境變數:

export OWNER="username"

然後修改前兩個指令的 member 參數:

--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository_owner/${OWNER}"

最後輸入以下指令,拿到集區 Provider 的資源名稱並記下來:

gcloud iam workload-identity-pools providers describe "github-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="github-pool" \
  --format="value(name)"

你應該會得到類似以下的名稱 projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider ,稍後建立 GitHub secret 時會用到。

步驟二:設定 GitHub Repo Secret

由於上文設定的 Google Cloud 資源名稱跟服務帳戶都是機密資訊,無論 repo 是否為 public 還是 private,都不建議直接寫在 GitHub Action 的 YAML 檔上面。

而針對不想公開在網路上,但又需要用在 GitHub Action 內的敏感資訊( 像是 GCP 集區名稱、密鑰、資料庫網址 )可以設定成 repo 的 secret,讓 GitHub Action 可以用類似環境變數的方式存取,同時讓敏感資訊不會直接暴露在 YAML 上。

所以接下來要新增 Cloud Run 自動部署所需的 secret。先打開你的 GitHub Repository 頁面,進入 Setting,點選左側選單 Secrets and variables → Action:

github action secret 設定圖片
github action secret 設定圖片

點選 New repository secret:

新增兩個 secret:

  1. WIF_PROVIDER : 步驟一最後取得的 Identity federation 資源名稱
  2. WIF_SERVICE_ACCOUNT : 步驟一設定的 GCP 專案服務帳戶( 包含 @ 之後的網域 )
將 Identity federation 和服務帳戶新增為 secret

這樣就完成 secret 的設定了!

步驟三:建立 Cloud Run GitHub Action

最後,我們就能沿用 Google 官方寫好的 Cloud Run Deploy YAML 檔到 repo 內,回到你的 repo 頁面 → Actions:

搜尋 Cloud Run,並點選 “Build and Deploy to Cloud Run” :

修改 YAML 檔案第 51 ~ 55 行的環境變數,根據註解自訂成你的專案編號、Cloud Run 服務名稱跟地區,地區可以使用 asia-east1 也就是台灣的機房:

另外需要改一下 109 行的 Docker image 網址

將第 109 行改成這句,新增一個 Docker image 名稱的參數:

image: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.SERVICE }}/${{ env.SERVICE }}:${{ github.sha }}

筆者用官方的 YAML 部署時,就遇到了 image 網址少了一個 image name 參數的錯誤,上面的做法是參考這篇的討論:Docker push “Missing image name”-error when pushing to GCP Artifact Registry from Github Actions

完成所有修改後,按右上方的 Commit change、新增 GitHub Action 的 YAML 檔就大功告成:

之後每次 commit 到 repo 的 main 分支就會自動執行 GutHub Action,將程式打包成 Docker image 並部署到 Cloud Run上了!你可以在 repo 的 Actions 頁看到自動部署的進度跟結果:


總結 Wrap up

以上就是設定 Cloud Run 自動部署的 GitHub Action 操作教學了,再重新概述一下 3 個步驟:

  1. 設定 GCP 專案的 Identity federation
  2. 將集區的 Provider 名稱和服務帳戶新增到 Repo Action 的 secret
  3. 建立並自訂 GitHub Action YAML 檔案

透過以上 3 步,就能設定 Repo 的 Action 了,你也可以另外加入測試等其他步驟,完善你的 CI/CD 流程,不過使用時要注意 GitHub Action 的額度限制喔

寫程式時以為我缺的是天賦,沒想到我還缺錢

筆者會有寫這篇教學的靈感,是因為趁暑假自學 Web 開發的技術時,剛好碰到 Serverless 的部署,我希望能自動化 Cloud Run 部署的工作,也趁機學一下 GitHub Action 的原理跟實作,只是網路上的文章都是舊版的部署方法,便記錄新版 Google 官方的 Cloud Run Action 怎麼設定。

當然,筆者在這篇是直接用 Google 官方寫的 YAML 檔,也沒有加入 unit test 等項目,所以沒有發揮用 GitHub Actions 做 CI/CD 的功能,未來若有機會進一步學到這塊的話,會再撰寫文章跟大家分享我的淺見所學。

Reference

本篇教學主要是受到下文的啟發與幫助,感謝 Robby 前輩寫的筆記文:

延伸閱讀

前一篇文章

7 個簡報技巧分享:我在每場演講和報告時都會用!

下一篇文章

我怎麼開發串接網站通知的 LINE Bot?聊天機器人實作過程分享