iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Software Development

系統設計一招一式:最基本的功練到爛熟就是殺手鐧,從單體架構到分布式系統的 Lab 實作筆記系列 第 23

Day 23 | 第三階段系統優化 | GitHub Actions + GCP 部署血淚史:踩雷大會

  • 分享至 

  • xImage
  •  

前言

今天的目標其實很單純:設定一個 GitHub Actions workflow,讓它能夠自動建置 Docker、推送到 Artifact Registry,然後部署到 GCE 實例上。聽起來就是個標準的 CI/CD 流程,應該半小時搞定的事情。
結果花了幾個小時在各種奇怪的權限和認證問題上打轉,不過回頭看,這整個過程暴露了不少平時容易忽略的雷點,也讓我重新思考了雲端資源管理的重要性。

實作

坑:SSH 權限問題

一開始就卡在 GitHub Actions 無法 SSH 到 GCE 實例。錯誤訊息說缺少 compute.instances.setMetadata 權限,無法將 SSH 金鑰添加到實例的 metadata。這個問題的根本原因是服務帳號權限不足,解決方法是添加 Compute Instance Admin 角色,或者使用 gcloud compute config-ssh 預先設置 SSH 配置。

坑:Docker Pull 認證地獄

SSH 問題解決後,遇到更頭痛的問題:GCE 實例無法從 Artifact Registry 拉取映像。錯誤訊息是經典的 "Unauthenticated request",但問題的複雜度遠超預期。

最初以為是服務帳號缺少 artifactregistry.reader 權限,但添加後問題依然存在。接著發現是 GCE 實例的 OAuth scope 不夠,只有 devstorage.read_only 等基本權限,缺少關鍵的 cloud-platform scope。

坑:專案 ID 混亂

最讓人困惑的是,系統一直顯示錯誤的專案 ID 1067582438577,但這個專案根本不存在。一開始懷疑是服務帳號金鑰檔案有問題,重新生成了好幾次,但問題依然存在。

後來發現是 GCE 實例內部的 gcloud 配置有舊的設定殘留,導致認證時會嘗試存取錯誤的專案。解決方法是清除實例內的 gcloud 配置:

rm -rf ~/.config/gcloud

坑:認證傳遞問題

即使解決了專案 ID 問題,Docker pull 還是失敗。根本原因是 GitHub Actions 的認證沒有正確傳遞到 GCE 實例內部。最終的解決方案是將認證檔案複製到實例,然後重新認證:

gcloud compute scp $GOOGLE_APPLICATION_CREDENTIALS $GCE_INSTANCE:/tmp/sa-key.json --zone=$GCE_ZONE

gcloud compute ssh $GCE_INSTANCE --zone=$GCE_ZONE --command="
  export GOOGLE_CLOUD_PROJECT=$PROJECT_ID
  export CLOUDSDK_CORE_PROJECT=$PROJECT_ID
  
  gcloud auth activate-service-account --key-file=/tmp/sa-key.json
  
  ACCESS_TOKEN=\$(gcloud auth print-access-token)
  echo \$ACCESS_TOKEN | sudo docker login -u oauth2accesstoken --password-stdin $GAR_LOCATION-docker.pkg.dev
  
  sudo docker pull $IMAGE_URL
  # ... 其他部署指令
"

坑:sudo 權限隔離

最後一個問題是 Docker 認證在 sudo 環境下無效。一般用戶的 Docker 認證無法被 sudo docker 命令使用,所以需要直接為 root 用戶設定認證。

總結

這次的踩雷經驗讓我學到幾個重要的教訓:

1. 資源管理重要性

雲端環境下,各種資源(服務帳號、IAM 權限、API scope、專案設定)之間的關係錯綜複雜。一個小小的配置錯誤就可能導致整個流程失效。提前規劃和文檔化這些資源配置是絕對必要的,不能等到出問題才臨時抱佛腳。

2. 認證傳遞的複雜性

在多層架構中(GitHub Actions → GCE 實例 → Docker),認證資訊的傳遞不是理所當然的。每一層都可能有自己的認證機制和權限限制,需要明確地處理認證傳遞問題。

3. 環境隔離帶來的意外

sudo 命令會切換到不同的用戶環境,很多環境變數和配置都會失效。這種環境隔離是安全機制,但也容易被忽略。

4. 錯誤訊息不一定反映真正的問題

"Unauthenticated request" 可能是權限問題,也可能是 scope 問題,或者是認證傳遞問題。需要系統性地排除各種可能性,而不是只看表面的錯誤訊息。

往後要避開的雷

  1. 服務帳號設計要一步到位:提前規劃需要哪些權限,一次性配置完整,避免後續補丁式修改
  2. 環境一致性檢查:確保開發、測試、生產環境的配置一致,避免「我本地可以跑」的問題
  3. 認證機制文檔化:清楚記錄每個環節的認證方式和權限需求
  4. 測試先行:複雜的部署流程應該先在簡化環境下測試各個環節

上一篇
Day 22 | 第三階段系統優化 | GitHub Actions & Docker Build
下一篇
Day 24 | 第三階段系統優化 | 繼續 GitHub Actions + GCE 部署:回頭把某些概念 solidify 一下
系列文
系統設計一招一式:最基本的功練到爛熟就是殺手鐧,從單體架構到分布式系統的 Lab 實作筆記25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言