1. 簡介
歡迎參加 Google Cloud 上的 Rails 8 研討會!在本實作程式碼研究室中,您將現代化 Rails 8 應用程式從簡單的本機 SQLite 基準,遷移至 Google Cloud 上可供企業使用的正式級參考架構。
在這場研討會中,我們不只是部署應用程式,而是執行有主見的生產環境等級雲端現代化 (正確的 lift-and-shift!)。
您會從典型的「成果僅存在於特定機器」本機設定 (暫時性磁碟、內嵌 SQLite、處理中作業) 開始,逐步演進為 Google Cloud 上的企業級、萬無一失的雲端架構:
- 🐘 從本機 SQLite → Cloud SQL (PostgreSQL):重新啟動時不會再清除資料庫。我們改用透過 Cloud SQL Auth Proxy (mTLS,無公開 IP) 安全連線的代管 PostgreSQL。
- 🪣 從暫時性本機儲存空間 → 私人 GCS:無伺服器磁碟是暫時性的。我們使用 IAM 已簽署的網址 (
iam: true) 將 ActiveStorage 連線至私人 Google Cloud Storage,因此不會有公開 bucket,也不會洩漏金鑰。 - 🚀 從程序內工作 → 多容器 Cloud Run:我們將 Rails 單體式架構和無伺服器雲端服務連結起來!我們使用標準 Docker Compose,在 Cloud Run 多重容器 Pod 中,將 Solid Queue 做為專屬工作站容器與 Puma 一併執行,完全沒有微服務蔓延問題,也沒有執行緒資源不足的問題,DHH 一定會很驕傲!
🍌 我們身處 2026 年,因此透過 Google Antigravity 全面提升整個堆疊的效能。這不是事後才想到的,我們有提示和 技能,可引導您善用這些工具,更順利地執行 (並享受) 這場研討會!
🏛️ 目標參考架構 (變體 1:乾淨的平面向量企業)
以下是您將建構及部署的標準 Google Cloud 參考架構 (變體 1:乾淨的扁平向量企業),展示我們與所有代管 Google Cloud 持續性和 AI 服務連線的生產環境多容器 Cloud Run 服務:

🎞️ 架構演進:從零到雲端原生
在每個研討會里程碑,觀看我們的堆疊從暫時性的單一機器基準,逐步現代化為全代管的 Google Cloud 藍圖:

課程內容
- 如何與 Google Antigravity 進行結對程式設計,解開 Rails 8 和 Google Cloud 的謎團。
- 如何執行自動飛行前診斷 (
just workshop-test)。 - 瞭解如何使用 Terraform 非同步佈建 Google Cloud 雲端基礎架構,同時使用
googleprovider 繼續進行本機開發,不會遭到封鎖。 - 如何消除安全反模式:私人 GCS 儲存空間 (
iam: true) 和 Cloud SQL Auth Proxy mTLS 通道,而非開啟0.0.0.0/0。 - 瞭解如何直接從 Google Cloud Secret Manager 插入密鑰。
- 如何透過 Solid Queue 協調非同步生成式 AI 背景工作 (NanoBanana 封面產生器、雙語 Podcast 合成)。
我們開始吧!
2. 步驟 0:先決條件、Antigravity 設定和帳單驗證
💡 情境:您和團隊正在建構任務關鍵型 Rails 8 應用程式。在觸及程式碼或啟動雲端資源之前,我們必須建立工具鍊、連結 Google Antigravity,並驗證 Google Cloud 憑證和帳單基礎。
1. 必要條件檢查清單
開始前,請確認環境中備妥下列工具:
just(1.21 以上): (just --version) 工作執行器 - 本研討會中的每個指令都是just配方 (請 AI 輔助程式安裝,或參閱skills/rails8app-workshop)。- Git (2.30 以上版本): (
git --version) 用於版本管控、分支和複製存放區。 - Google Cloud SDK (
gcloudCLI):已安裝並為最新版本。 - Terraform CLI (1.5 以上版本):用於宣告式基礎架構佈建。
docker和docker-compose:在本機安裝及執行。- Ruby
3.4.5: (由blog/.ruby-version釘選;如果缺少3.4.5,請要求 AI 測試架構安裝)。ruby -vRails 8 會在Gemfile中宣告,並由bundle install自動安裝。 - Google Antigravity 2.0:自主 AI 結對程式設計助理 (下載 Google Antigravity 2.0)。您可以使用個人 Gmail 登入,不需要 API 權杖。
2. Google Cloud 驗證、專屬設定和 ADC
為避免與現有的公司、個人或多帳戶 gcloud 設定發生衝突,強烈建議您為本研討會建立專屬的命名設定:
# 1. Create and activate dedicated workshop configuration (Fixes Issue #38)
gcloud config configurations create rails8-on-gcp-workshop --activate 2>/dev/null || \
gcloud config configurations activate rails8-on-gcp-workshop
# 2. Authenticate your account and Application Default Credentials (ADC)
gcloud auth login $GOOGLE_CLOUD_ACCOUNT
gcloud auth application-default login
# 3. Configure active project and region
gcloud config set project $GOOGLE_CLOUD_PROJECT
gcloud config set compute/region europe-west1
💡 為什麼要採用專屬設定?使用 gcloud config configurations create rails8-on-gcp-workshop 可隔離所有 CLI 設定 (帳戶、配額專案、預設區域),專供本研討會使用。完成後,你隨時可以按 gcloud config configurations activate default 切換回一般設定。

3. 🚨 GCP 帳單驗證
⚠️ 注意:Google Cloud SQL 和 Cloud Run 部署作業需要已連結的有效帳單帳戶,或有效的研討會教育訓練抵免額。請確認設定正確無誤。如要執行這項操作,請進行帳單驗證檢查:
gcloud beta billing projects describe $GOOGLE_CLOUD_PROJECT
確認 billingEnabled: true 已退回。您也可以列出所有帳單帳戶:
gcloud billing accounts list
請確認至少有一個顯示 OPEN: True。如果沒有,請在 Google Cloud 控制台的「帳單」頁面連結帳單帳戶,或兌換研討會抵免額優待券。

4. 複製存放區並與 Antigravity 配對
複製存放區並進入目錄。請注意,我們完全停留在 main:
git clone https://github.com/palladius/rails8-app-on-gcp.git
cd rails8-app-on-gcp
在 Google Antigravity 中開啟這個目錄。Antigravity 會自動檢查存放區、讀取 AGENTS.md,並待命成為你的結對程式設計師。
5. 🪫 如果 Antigravity 的免費點數用完
⚠️ 您剛兌換的 Cloud 抵免額「不會」為 Antigravity 補充點數。這兩者是不同的帳單平台:Antigravity 的配額位於 Google AI 計畫 (Google One),而優待券和 Cloud 抵免額則位於 Cloud Billing 帳戶。將帳單帳戶連結至 GCP 專案後,您不會獲得額外的 Antigravity 提示。
如果代理程式因配額問題停止運作,請依序嘗試下列方法,前兩種方法免費且即時:
- 切換模型集區。配額是依模型系列追蹤,Antigravity 則會顯示兩個獨立的配額集區 (Gemini 模型與 Claude/GPT 模型)。免費方案提供 Claude 和 GPT-OSS 模型,因此用盡 Gemini 額度通常不會影響其他模型。使用提示詞輸入框下方的模型選取器。

- 改用 Flash 模型,而非 Pro。速率限制與代理程式每次要求執行的工作量有關,因此較輕量的模型會充分利用剩餘的配額。
- 使用自己的 Gemini API 金鑰搭配 Antigravity CLI。Antigravity IDE 不支援自備金鑰,建議改用 CLI。在 Google AI Studio 中建立免費金鑰 (需要專案,但不需要帳單帳戶),然後下載並設定 Antigravity CLI。
- 等待,或升級。免費方案配額每週會重設一次 (錯誤訊息會顯示重設日期)。Google AI Pro/Ultra 是唯一有記錄的方法,可提高電腦版應用程式的基準;這些方案每五小時更新一次,且可將購買的 AI 點數用於超額用量。
- 雖然這是 Antigravity 工作坊,但大部分的 AI 程式碼輔助工具應該也適用,只要將工具指向存放區中的
AGENTS.md和skills/目錄即可。
6. 自動化步驟 0 驗證
確認步驟 0 環境 100% 符合評估套件:
just workshop-eval 0
✨ 令人驚豔的時刻:反重力引擎和工作坊評估引擎會在 2 秒內驗證 CLI 版本、Ruby 環境和 ADC 驗證!
3. 步驟 1:啟動 Terraform 基礎架構並進行前置診斷
💡 策略:Google Cloud SQL PostgreSQL 等代管資料庫的佈建時間約為 10 到 12 分鐘。我們現在在背景中透過 Terraform 啟動不可變的基礎架構,而不是稍後閒置等待,同時在本地開發!
1. 執行自動化飛行前診斷套件
啟動雲端基礎架構前,請先執行全面的飛行前測試套件:
just workshop-test
這個指令碼 (bin/workshop_diagnostics.rb):
- 驗證您的
GOOGLE_CLOUD_ACCOUNT身分設定。 - 驗證有效的帳單和專案連結。
- 驗證 Vertex AI 的應用程式預設憑證 (ADC)。
- 確認 ActiveStorage Canary 種子映像檔 (
blog/app/assets/images/gcs_dev_image.jpg)。

如果缺少 .env,請從記錄的範本複製:
cp .env.dist .env
# Edit .env and configure GOOGLE_CLOUD_ACCOUNT with your Google/Gmail account
💡 提示:如果 just workshop-test 報告專案已停用帳單 API,請透過下列方式快速啟用:
gcloud services enable cloudbilling.googleapis.com
2. ⏱️ 非同步啟動 Terraform 基礎架構
執行單一步驟的 Terraform 配方,建立遠端後端狀態 bucket 並觸發基礎架構部署作業:
just terraform-apply

手動替代方案 (或直接執行 `iac/bin/terraform-apply.sh`)
# Fall back to active gcloud config if environment variables are unset
GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project)}"
GOOGLE_CLOUD_REGION="${GOOGLE_CLOUD_REGION:-europe-west1}"
# Create remote state bucket idempotently
gcloud storage buckets create "gs://${GOOGLE_CLOUD_PROJECT}-tfstate" --location="$GOOGLE_CLOUD_REGION" 2>/dev/null || \
gcloud storage buckets describe "gs://${GOOGLE_CLOUD_PROJECT}-tfstate" >/dev/null
cd iac
terraform init -backend-config="bucket=${GOOGLE_CLOUD_PROJECT}-tfstate"
terraform apply -auto-approve -var="project_id=${GOOGLE_CLOUD_PROJECT}" -var="region=${GOOGLE_CLOUD_REGION}"
cd ..
Terraform 佈建的項目:
- Google Cloud Storage Bucket:使用私有存取權和 IAM 憑證簽署 (
iam: true) 建立。 - Canary 測試圖片:將
gcs_dev_image.jpg上傳至 bucket,啟用端對端 Blob 驗證。 - Google Cloud SQL PostgreSQL 執行個體:啟動背景佈建 (~10 到 12 分鐘)。

💡 注意:您可以放心地忽略自動備份遺失的警告,因為我們不需要這些備份來進行本研討會的資料庫 (雖然自動每日備份是任何實際生產資料庫的重要最佳做法!)。

📝 記下 Cloud Run 網址:Terraform 應會成功套用,並列印基礎架構輸出內容。記下 cloud_run_url (或複製到剪貼簿),稍後驗證即時服務時會用到!
3. 自動化步驟 1 驗證 (選用)
確認步驟 1 的必要條件和設定通過驗證:
just workshop-eval 1
⏱️ 注意:這項評估會檢查即時雲端資源,可能需要幾分鐘才能完成。這完全是選用步驟,在終端機執行這項指令時,您可以立即繼續下一個步驟,不必等待!
4. 步驟 2:本機基準、Mailpit 和管理員新手上路
我們的起點是乾淨的現代化 Rails 8 網誌應用程式,在本機託管上執行,並使用 SQLite、Mailpit 電子郵件攔截功能和以磁碟為基礎的 ActiveStorage。
1. 在本機啟動應用程式
📁 注意目錄!這個存放區包含 blog/ 中的 Rails 應用程式,以及 iac/ 中的 Terraform。步驟 1 會將您留在存放區根目錄,因此步驟 2 中的每個指令都會從
blog/ - Gemfile、bin/rails 和 compose.yaml 都住在該處。從根目錄執行這些指令會失敗,並顯示 no configuration file provided: not found 或缺少 Gemfile。
移至 Rails 應用程式,確認您使用的是已釘選的 Ruby,然後安裝 Gem 並設定資料庫:
cd blog
ruby -v # must print 3.4.5 — see Step 0 if it does not
bundle install
bin/rails db:setup
🧯 寫入 時發生權限錯誤
/var/lib/gems/
或
ruby -v
顯示系統 Ruby (例如 3.3.x) 嗎?Vanilla Linux 系統 Ruby 會完全忽略 .ruby-version 檔案,並嘗試在整個系統中安裝 Gem!確認 rbenv 已載入至現用終端機殼層:
eval "$(rbenv init - bash)" # or: eval "$(rbenv init - zsh)"
ruby -v # confirm it now outputs 3.4.5!
注意:如果您在步驟 0 中略過安裝 3.4.5,請執行 rbenv install 3.4.5。或者,您也可以直接使用下方的模式 A (Docker Compose) (just compose-up),在容器內完整執行釘選的 Ruby 堆疊,完全不會動到主機 Gem!
💡 不想考慮這件事嗎?just install 和 just compose-up 會為您執行 cd blog。
2. 🌱 智慧種子自動探索和管理員啟動程序 (問題 #21 和 #25)
資料庫種子 (db/seeds.rb) 具有智慧型環境自動探索功能:
- 檢查您目前使用的資料庫介面卡 (SQLite 或 Postgres) 和儲存空間設定。
- 系統會偵測第 0 階段 (本機主機),並自動建立初始管理員使用者,以及提供教學貼文:
[LOCAL BASELINE] Welcome to Rails 8 on Localhost!- 隨附的本機 sad 圖片附件 (
local_sad_image.png) 附有浮水印,說明本機磁碟儲存空間是暫時的。
- 系統會透過 ActionMailer 自動觸發重設密碼電子郵件。
驗證或重新執行種子 (系統會自動使用 .env 中的 GOOGLE_CLOUD_ACCOUNT):
bin/rails db:seed # or from repo root: just seed
透過 Docker Compose 啟動本機開發堆疊:
docker compose up -d # from blog/ — or `just compose-up` from the repo root

應用程式、本機 Mailpit SMTP 伺服器和 Adminer 資料庫檢視器現在會一起在獨立容器中執行:
- Rails 應用程式:http://localhost:3000
- Mailpit 網頁介面:http://localhost:8025
- Adminer DB UI:http://localhost:8081
🧯 通訊埠 3000 衝突或
server.pid
錯誤?如果您先前啟動了原生開發伺服器 (bin/dev),請使用 Ctrl+C 停止該伺服器。如果 Docker 回報通訊埠 3000 已分配,或找到剩餘的 server.pid,請使用下列指令清理:
just compose-down
rm -f blog/tmp/pids/server.pid
just compose-up
3. Mailpit 體驗與管理員登入
- 使用 Mailpit 擷取外寄電子郵件:在瀏覽器中開啟
http://localhost:8025。查看本機收件匣!在db:seed期間,ActionMailer 傳送了管理員新手上路電子郵件,而 Mailpit 在本機安全地擷取了這封郵件,完全沒有觸及外部電子郵件伺服器或憑證:
2. 登入網誌:在瀏覽器中開啟 http://localhost:3000。
- 你可以直接點選 Mailpit 電子郵件中的重設密碼連結來設定密碼。
- 或者,您也可以使用 Google Cloud 帳戶電子郵件地址 (來自
.env) 和預設密碼Ch4ng3m3!!1登入。
- 觀察視覺遙測徽章:
- 請注意使用者介面中的黃色環境橫幅和徽章:
Notice: Ephemeral Database Active (POSTGRESQL)和[EPHEMERAL DB / STORAGE] 💾 Local。 - 請注意貼文浮水印:🏠 戳記 (封面圖片右下角的
nanobanana_stamp_local.png)。這項資訊會立即以視覺化方式呈現,確認資產和資料庫目前繫結至暫時性本機儲存空間。
- 請注意使用者介面中的黃色環境橫幅和徽章:

💡 選用專業提示:Rails 控制台練習想知道 Rails 如何與 CLI 中的植入資料互動嗎?進入互動式控制台:
bin/rails console
使用環境變數動態檢查已植入的系統管理員使用者:
user = User.find_by(email_address: ENV.fetch("GOOGLE_CLOUD_ACCOUNT"))
# Or simply grab the first user:
user = User.first
puts "Admin email: #{user.email_address} (created via: #{user.created_via})"
exit
4. 自動執行步驟 2 驗證
驗證當地基準和管理員設定:
just workshop-eval 2

5. 注意事項:無狀態容器
Cloud Run 容器是無狀態的暫時性容器,如果我們直接將 SQLite 資料庫和本機 storage/ 目錄部署到 Cloud Run,每當容器縮減為零或重新啟動時,所有貼文和上傳的圖片都會永久清除。
在下一個步驟中,我們會刻意將這個暫時性設定部署至 Cloud Run,親身體驗 Stateless Shock!
5. 步驟 3:部署 1 - 無狀態衝擊 (3 分鐘內搶先體驗 WOW!)
不必等待雲端資料庫 15 到 20 分鐘,就能在網路上看到內容。現代無伺服器開發作業一開始就獲得勝利:在 3 分鐘內,將單一容器 Rails 應用程式直接部署到 Google Cloud Run!
1. 時光機回顧
如要確保起始設定 100% 為暫時性 (本機 SQLite 資料庫和本機磁碟檔案儲存空間),請使用 Zero-Branch Time-Machine 引擎:
just workshop-rewind 1
💡 發生什麼事了?workshop-rewind 1 將 stage-1-stateless 設定疊加層套用至 blog/config/,且未離開 main 分支。您的應用程式已在容器磁碟上設定 SQLite,並在本機檔案系統上設定 ActiveStorage。
2. 將單一容器 Puma 部署至 Cloud Run
直接從原始碼部署至 Cloud Run。新版 Rails 8 會自動產生可直接用於正式環境的官方 Dockerfile (內含多階段建構、jemalloc 和非根層級安全防護)。從 blog/ 資料夾部署時,Google Cloud Build 會直接偵測到這個原生 Dockerfile,並佈建代管無伺服器服務:
# Ensure you are inside the Rails application directory
cd blog
# Ensure default region is set
export GOOGLE_CLOUD_REGION="europe-west1"
# Deploy single-container service from source (with dummy key fallback if starting without credentials)
gcloud run deploy blog \
--source . \
--region $GOOGLE_CLOUD_REGION \
--allow-unauthenticated \
--set-env-vars GOOGLE_CLOUD_ACCOUNT=$GOOGLE_CLOUD_ACCOUNT,SECRET_KEY_BASE_DUMMY=1
您應該會看到類似以下的進度輸出內容:

⏳ Cloud Build 封裝容器並佈建 Cloud Run 修訂版本時,這項作業應該需要幾分鐘。
🧯 疑難排解與常見注意事項
PERMISSION_DENIED: Build failed because the default service account is missing required IAM permissions
? 兩種不同的原因會導致相同的錯誤:
- gcloud 剛為您啟用 API。如果部署作業已列印
The following APIs are not enabled ... cloudbuild.googleapis.com,而您回答Y,則建構作業可能在新的權限傳播完成前就已開始,gcloud 本身的提示會警告「這需要幾分鐘」。稍待片刻,然後重新執行完全相同的指令,通常就能成功。步驟 1 的terraform apply通常會為您啟用這個 API,因此您應該不會看到提示。 - 預設 Compute 服務帳戶確實缺少角色。新版 GCP 專案會強制執行最低權限,導致來源部署作業中斷。修正方法:
just project-status # grants storage.admin, logging.logWriter, artifactregistry.writer, cloudbuild.builds.builderjust workshop-test現在也會回報這項資訊,因此如果部署作業持續失敗,建議重新執行。
部署期間:
- Cloud Run 會建構 Rails 容器映像檔。
- 並指派公開且安全的 TLS 網域:
https://blog-[hash]-[region].a.run.app。 - 流量開始轉送至通訊埠 8080 上的 Puma。
3. ✨ 早期 WOW 時刻
在瀏覽器中開啟產生的 Cloud Run 網址!
- 您的新版 Rails 8 應用程式已在 Google Cloud 上線!
- 使用管理員憑證 (
GOOGLE_CLOUD_ACCOUNT和APP_ADMIN_PASSWORD) 登入。如果找不到登入按鈕,請按一下「New Post」,系統會先提示輸入使用者名稱和密碼:
- 按一下「New Post」(新文章),撰寫名為「My First Cloud Run Post」(我的第一篇 Cloud Run 文章) 的文章,附上圖片,然後按一下「Create Post」(建立文章)。
- 貼文會以完整格式發布,圖片也會轉譯。
- 請注意視覺遙測:
- 標頭徽章:
[EPHEMERAL DB / STORAGE] 💾 Local - 圖片浮水印:🏠 戳記 (右下角的
127.0.0.1暫時性磁碟徽章)。
- 標頭徽章:
🐝 加入 Live Workshop Hive 排行榜!如果你在線上,監考人員正在顯示排行榜,且你想加入排行榜,請在此新增 Cloud Run 網址:👉 在排行榜上註冊 Cloud Run

4. 💥 注意事項:無狀態震盪和「Puma 解決方法」陷阱
Cloud Run 是無狀態的無伺服器平台,當網路流量降至零時,Cloud Run 會將容器執行個體數縮減至零,以節省費用。當有新的 HTTP 要求或部署新的容器修訂版本時,Cloud Run 會啟動全新的乾淨容器映像檔。
停滯工作橫幅
在單一容器部署中建立貼文或附加圖片時,Rails 會將 ActiveJob 工作 (例如圖片尺寸分析或中繼資料索引) 加入佇列。但由於沒有人執行背景工作者,您會看到警告橫幅:

誘人的解決方法:在 Puma 中執行 Solid Queue
聰明的開發人員可能會說:「等等!Rails 8 讓我們能夠啟用
SOLID_QUEUE_IN_PUMA=true
環境變數!"
我們在 Cloud Run 上測試這項解決方法:
# Force a revision update to enable Solid Queue inside Puma
gcloud run services update blog \
--region $GOOGLE_CLOUD_REGION \
--update-env-vars SOLID_QUEUE_IN_PUMA=true
現在返回瀏覽器並重新整理頁面:
💥 無狀態衝擊:
- 好消息:Solid Queue 工作人員現在已在 Puma 內啟動!任何新工作都會立即耗用配額。
- 冷水澡 (令人驚豔的內容!):
- 由於 Cloud Run 部署了新修訂版本,因此取代了先前的容器例項!
- 您撰寫的文章和磁碟上的 SQLite 資料庫檔案已完全清除!
- 架構課程:在 Puma 中執行背景工作者會耗用寶貴的網頁執行緒 CPU/RAM,且仍無法解決持續性問題。
5. 自動化步驟 3 驗證
驗證 Cloud Run 部署作業和快訊處理程序:
just workshop-eval 3
✨ 課程:單一容器解決方法 (例如 SOLID_QUEUE_IN_PUMA) 適用於本機開發,但在現代雲端架構中:
- 媒體檔案需要 Google Cloud Storage (GCS) (步驟 4)。
- 關聯資料和工作佇列需要代管的 Google Cloud SQL (步驟 5)。
- 繁重的背景工作應放在專用的邊車容器 (步驟 6)。
6. 步驟 4:部署 3 - GCS 持久儲存空間和 POLA 警告
在這個步驟中,我們將 ActiveStorage 切換至 Google Cloud Storage (GCS),並透過 IAM 憑證 API (iam: true) 使用短期簽署網址,將媒體和檔案儲存空間與容器磁碟分離。
在進行任何設定前,請先瞭解 Rails 與 GCS 通訊時需要的三個項目:
① 專案 ID + Bucket 名稱 - Rails 必須知道要將 Blob 儲存在何處。應用程式會從環境讀取 GOOGLE_CLOUD_PROJECT,並將值區名稱衍生為 ${GOOGLE_CLOUD_PROJECT}-activestorage-prod。步驟 1 的 Terraform 已建立這個 bucket,因此不需要額外作業。
② 具備適當權限的服務帳戶:Cloud Run 會以 Compute Engine 預設服務帳戶 (PROJECT_NUMBER-compute@developer.gserviceaccount.com) 執行。這個服務帳戶需要具備儲存空間的兩項 IAM 角色:
roles/storage.objectAdmin- 上傳及提供 blobroles/iam.serviceAccountTokenCreator本身,用於呼叫 IAM 憑證signBlobAPI 並產生短期簽署網址
⚠️ 誘人的捷徑 -
public: true 如要讓 GCS 運作,最快的方法是在 storage.yml 中設定 public: true,並在 bucket 上授予 allUsers:objectViewer。圖片會立即載入,不需登入帳戶。請勿這麼做。公開 bucket 會將所有上傳的使用者媒體洩漏到開放網路,而且永遠不會停止。我們的藍圖使用 iam: true 將儲存空間完全設為私有,每個圖片網址都是由 IAM 憑證 API 即時產生的簽署權杖,並設有到期時間。
③
ACTIVE_STORAGE_SERVICE=google:這個單一環境變數會在執行階段將 Rails 從本機磁碟切換至 GCS。不必變更程式碼,也不必重新部署應用程式邏輯,只要切換一個環境變數即可。
1. 時光機回顧
將時光機推進至第 2 階段:
just workshop-rewind 2
檢查 blog/config/storage.yml,您會看到應用程式定義了三個具名的 GCS 環境 (開發 / 測試 / 正式版),全都使用相同的安全模式:
<% gcp_project = ENV.fetch("GOOGLE_CLOUD_PROJECT") { Rails.application.credentials.dig(:gcs, :project) } %>
<% gcs_signer_sa = ENV.fetch("GCS_SIGNER_SA_EMAIL") { "rails-cloudrun-sa@#{gcp_project}.iam.gserviceaccount.com" } %>
google_prod: &google_prod
service: GCS
project: <%= gcp_project %>
bucket: <%= gcp_project %>-activestorage-prod # ← derived from project ID, no extra env var!
iam: true # Sign URLs via IAM Credentials signBlob API (no private key JSON required!)
gsa_email: <%= gcs_signer_sa %>
# Convenience alias — ACTIVE_STORAGE_SERVICE=google points here
google:
<<: *google_prod

2. 授予 IAM Storage 和簽署權限
請確認 Cloud Run 執行階段服務帳戶具備簽署網址和上傳物件的權限:
export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export RUN_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
export GCS_BUCKET="${GOOGLE_CLOUD_PROJECT}-activestorage-prod"
# Grant Storage Object Admin
gcloud storage buckets add-iam-policy-binding gs://$GCS_BUCKET \
--member="serviceAccount:$RUN_SA" \
--role="roles/storage.objectAdmin"
# Grant Service Account Token Creator (required for IAM signed URLs)
gcloud iam service-accounts add-iam-policy-binding $RUN_SA \
--member="serviceAccount:$RUN_SA" \
--role="roles/iam.serviceAccountTokenCreator"
3. 第三次部署至 Cloud Run,並附加 GCS
重新部署並啟用 GCS。請確認您位於 blog/ 資料夾內 (否則 Cloud Run 會嘗試使用 Buildpacks 而非 Rails 8 Dockerfile 進行建構,導致建構失敗)。
使用 --update-env-vars (而非 --set-env-vars) 保留現有的環境變數 (主金鑰、IAP 設定等):
cd blog
gcloud run deploy blog \
--source . \
--region $GOOGLE_CLOUD_REGION \
--update-env-vars ACTIVE_STORAGE_SERVICE=google
💡 提示:如果部署失敗,請按一下終端機輸出內容中提供的 Cloud Build 記錄連結 (如下圖所示)。你也可以請 Antigravity 協助解讀及修正錯誤!

4. ✨ 倖存的圖片和雲端戳記
- 重新整理網誌網址。
- 建立名為「Surviving the Cloud」的新網誌文章,並上傳相片。
- 查看圖片右下角:
- 「🏠」貼紙已移除!
- 取而代之的是彩色 Cloud GCS 戳記 (
nanobanana_stamp_cloud.png)。
- 強制重新啟動另一個容器 (這應該會觸發第四個修訂版本):
gcloud run services update blog --region $GOOGLE_CLOUD_REGION --update-env-vars RESTART_TRIGGER=$(date +%s) - 重新整理頁面:SQLite 資料庫重設時,圖片二進位檔會安全無虞地儲存在 Google Cloud Storage!透過 CLI 驗證:
gcloud storage ls -l -r gs://$GCS_BUCKET/
以下列出您在正式版值區 (gs://$GCS_BUCKET) 的 Google Cloud Storage 瀏覽器控制台中,可能會看到的上傳 Blob 和媒體物件:

5. ⚠️ 注意事項:工作停滯警告橫幅
上傳圖片後,ActiveStorage 會將分析工作 (ActiveStorage::AnalyzeJob) 加入佇列,以擷取維度和中繼資料。
不過,Cloud Run 目前只執行一個網站容器 (puma)。在 Rails 8 中,Solid Queue 會將背景工作儲存在資料庫中,但沒有人執行 bundle exec rails solid_queue:start!
查看網誌頁面頂端:您會看到 blog/app/views/layouts/_check_stuck_jobs.html.erb 顯示的醒目警告橫幅。
我們將在步驟 6 中妥善解決這個問題,屆時我們會升級至正式版的多容器 Sidecar 架構,並使用專屬的 worker 容器獨立執行 Solid Queue,不受網路流量影響!
6. 自動化步驟 4 驗證
驗證 GCS 設定和卡住的工作遙測:
just workshop-eval 4
7. 步驟 5:飛行前檢查清單:Cloud SQL、祕密和資料庫連線
您在步驟 3 和 4 中掌握了無狀態容器和 Google Cloud Storage,而 Terraform 已在背景中完成實際工作環境後端的烹調作業!
部署完整的 3 個容器生產環境輔助架構 (步驟 6 中的「部署 4」) 之前,這個步驟會做為快速的飛行前檢查,驗證資料庫健康狀態、連線能力和密鑰存取權。
0. 啟動環境變數
直接從 Terraform 輸出內容匯出所有必要的殼層變數:
# Core project variables (should already be set from Step 0)
export GOOGLE_CLOUD_REGION="${GOOGLE_CLOUD_REGION:-europe-west1}"
# Compute runtime service account
export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export RUN_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
# Cloud SQL instance name (from Terraform output, NOT hardcoded)
export SQL_INSTANCE_NAME=$(cd iac && terraform output -raw sql_instance_name 2>/dev/null || gcloud sql instances list --format='value(name)' --limit=1)
# Database password (from Terraform output)
export DB_PASSWORD=$(cd iac && terraform output -raw db_password 2>/dev/null || echo "CHANGE_ME")
# GCS bucket names
export GCS_BUCKET="${GOOGLE_CLOUD_PROJECT}-activestorage-prod"
echo "✅ Pre-flight environment variables ready:"
echo " RUN_SA=$RUN_SA"
echo " SQL_INSTANCE_NAME=$SQL_INSTANCE_NAME"
echo " GCS_BUCKET=$GCS_BUCKET"
1. Cloud SQL 健康檢查
確認代管 PostgreSQL 執行個體已完成初始佈建,且運作正常:
echo "Checking instance: $SQL_INSTANCE_NAME"
gcloud sql instances describe $SQL_INSTANCE_NAME --format="value(state)"
輸出內容必須是 RUNNABLE。
2. (選用) 從筆記型電腦測試資料庫連線
在實際運作環境中,Cloud Run 容器會透過加密的 Sidecar Proxy (127.0.0.1:5432) 安全連線至 Cloud SQL,不會將資料庫暴露在公開網路上 (0.0.0.0/0)。
ℹ️ 附註:這個步驟完全是選用步驟!在 Cloud Run 中,官方 Cloud SQL Auth Proxy 邊車容器會自動處理 mTLS 連線。您不需要在本機安裝任何項目,即可完成研討會。
如果電腦上已安裝 cloud-sql-proxy 和 psql,並想驗證筆電的直接連線:
# Optional: test connectivity via Google-managed mTLS proxy
gcloud sql connect $SQL_INSTANCE_NAME --user=rails_user --database=rails_production
(如果系統提示輸入密碼,請輸入
$DB_PASSWORD
。否則,請直接跳到下方的步驟 3!)
3. 檢查 Terraform 佈建的密鑰
切勿在 Git 或容器環境變數中儲存純文字資料庫密碼、API 金鑰或 Rails 主金鑰。在現代雲端架構中,我們使用 Google Cloud Secret Manager 進行零信任執行階段注入。
ℹ️ Terraform 已在步驟 1 為您處理這項作業:
rails-master-key- 用於在執行階段解密 Rails 憑證。rails-db-password:與 Cloud SQL 使用者相符的加密安全隨機密碼。rails-admin-password:初始管理員使用者的安全密碼。
確認密鑰在 Secret Manager 中存在且有效:
gcloud secrets list --filter="name:rails-"

🔑 將「master.key」同步到筆電 (本機同步)
由於 blog/config/master.key 會因安全考量而遭 Git 忽略,因此本機複製版本可能還沒有這個檔案。執行這個友善的自我修復程式碼片段,從 Secret Manager 中提取密鑰,讓本機 Rails 工具可以解密憑證:
if [ ! -f blog/config/master.key ]; then
echo "📥 Pulling rails-master-key from Secret Manager to local blog/config/master.key..."
gcloud secrets versions access latest --secret=rails-master-key > blog/config/master.key
echo "🔑 Created local blog/config/master.key! 🎉"
else
echo "✅ Local blog/config/master.key already exists! 🚀"
fi
🧪 Playground:想建立自訂密鑰,瞭解有多簡單嗎?Secret Manager 不只適用於 Rails!您可以使用簡單的 CLI 指令建立任何密鑰:
echo -n "MySuperSecretValue123!" | gcloud secrets create workshop-fun-secret --data-file=-
echo "✨ Created workshop-fun-secret in Secret Manager! 🪄"
4. 驗證密鑰存取者權限
如要讓 Cloud Run 容器在啟動時將這些密鑰掛接為環境變數或磁碟區掛接點,執行階段服務帳戶 ($RUN_SA) 必須具備每個密鑰的 roles/secretmanager.secretAccessor 角色。
Terraform 已在步驟 1 中授予這項權限!驗證 rails-master-key 的 IAM 政策繫結:
# Verify that Cloud Run runtime SA has Secret Accessor role
gcloud secrets get-iam-policy rails-master-key \
--flatten="bindings[].members" \
--format="table(bindings.role,bindings.members)" \
--filter="bindings.members:$RUN_SA"
如下所示:
ROLE MEMBERS
roles/secretmanager.secretAccessor serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com

5. 自動化預檢驗證
執行自動化步驟 5 測試套件,確認資料庫可執行、密鑰不為空白,且 IAM 權限為綠色:
just workshop-eval 5
8. 步驟 6:部署 4 - 企業多容器 Sidecar (黃金標準)
現在,我們來組建最終的參考架構:多容器 Cloud Run!
1. 恢復黃金標準
將存放區設定還原為標準 main 狀態:
just workshop-restore-gold
2. 檢查 3 容器生產藍圖
開啟 blog/compose.prod.yaml 並檢查架構:

graph LR
Client[Internet / HTTPS Client] -->|Port 8080| Web[Web Container: Puma]
Web -->|Localhost:5432| Proxy[Sidecar: Cloud SQL Auth Proxy]
Worker[Worker Container: Solid Queue] -->|Localhost:5432| Proxy
Proxy -->|IAM mTLS Tunnel| CloudSQL[(Google Cloud SQL PostgreSQL)]
正式環境部署會執行三個協調式容器,共用相同的區域網路:
web:透過 Puma 在通訊埠 8080 上提供 HTTP 流量。worker:專屬背景處理容器,執行bundle exec rails solid_queue:start。cloudsql-proxy:繫結至127.0.0.1:5432的官方 Google Cloud SQL Auth Proxy Sidecar (gcr.io/cloud-sql-connectors/cloud-sql-proxy:2)。
3. 透過 Cloud Run 工作執行資料庫遷移作業
將網路流量導向新部署作業前,我們必須對 Cloud SQL 執行資料庫遷移和初始播種作業。
⚖️ 如何在容器中執行遷移作業:兩種策略
在容器化雲端環境中,執行資料庫遷移作業的常見方法有兩種:
- 選項 A:在 Docker Entrypoint 內自動執行 (
entrypoint.sh會在容器啟動時執行db:migrate)- 優點:完全自動化,開發人員不必記得執行指令。
- 缺點:在無伺服器自動調度資源中很危險!當 Cloud Run 在高流量下同時擴充至 10 個執行個體時,10 個容器會爭相並行執行遷移作業,導致資料庫鎖定、結構定義競爭條件或啟動逾時。
- 方法 B:透過 Cloud Run 作業明確設定 (我們的架構選擇)
- 優點:完全掌控、交易安全,且不會發生競爭條件。以專屬的一次性容器工作執行,完全與提供網路流量服務無關。如果遷移作業失敗,網站服務不會受到影響。
- 缺點:部署前必須觸發 CLI 工作或 CI/CD 步驟。
我們選擇選項 B,以確保生產可靠性。
⚠️ 重要:DATABASE_URL格式和 Ruby 3.4.5 RFC 3986 嚴格程度
Cloud Run 作業會使用原生 Unix Socket Proxy (/cloudsql/PROJECT:REGION:INSTANCE) 連線至 Cloud SQL。DATABASE_URL 必須使用三斜線格式:
postgresql:///rails_production?user=rails_user&password=PASS&host=/cloudsql/PROJECT:REGION:INSTANCE
# Core environment variables for database migration & initial admin seed
export GOOGLE_CLOUD_ACCOUNT="${GOOGLE_CLOUD_ACCOUNT:-$(gcloud config get-value account)}"
echo "Admin user will be seeded for: $GOOGLE_CLOUD_ACCOUNT"
# Get the latest blog image from Cloud Run (jobs don't support --source)
export BLOG_IMAGE=$(gcloud run services describe blog --region=$GOOGLE_CLOUD_REGION --format='value(spec.template.spec.containers[0].image)')
# Build the DATABASE_URL in triple-slash format (required for Unix socket proxy)
export DB_PASSWORD=$(gcloud secrets versions access latest --secret=rails-db-password)
export CLOUDSQL_CONNECTION="${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME}"
export DB_URL="postgresql:///rails_production?user=rails_user&password=${DB_PASSWORD}&host=/cloudsql/${CLOUDSQL_CONNECTION}"
# Create migration job — note ALL 4 database URL env vars and GOOGLE_CLOUD_ACCOUNT for seed user creation
gcloud run jobs create rails-migrate \
--image=$BLOG_IMAGE \
--command "bin/rails" \
--args "db:prepare" \
--set-env-vars="DATABASE_URL=${DB_URL},DATABASE_QUEUE_URL=${DB_URL},DATABASE_CACHE_URL=${DB_URL},DATABASE_CABLE_URL=${DB_URL},GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},GOOGLE_CLOUD_ACCOUNT=${GOOGLE_CLOUD_ACCOUNT}" \
--set-secrets="RAILS_MASTER_KEY=rails-master-key:latest" \
--set-cloudsql-instances="${CLOUDSQL_CONNECTION}" \
--service-account=$RUN_SA \
--region $GOOGLE_CLOUD_REGION 2>/dev/null || true
# Execute migration job
gcloud run jobs execute rails-migrate --region $GOOGLE_CLOUD_REGION --wait
💡 Rails 8 多重資料庫:應用程式會為每個 database.yml 使用 4 個資料庫 (主要、佇列、快取、連線)。所有 4 個 DATABASE_*_URL 環境變數都必須指向同一個 Cloud SQL 執行個體,否則系統不會建立 Solid Queue/Cache/Cable 資料表。
在 db:prepare 之後,載入 Solid Queue/Cache/Cable 結構定義:
# Schema load for queue, cache, and cable databases (creates solid_queue_jobs, etc.)
gcloud run jobs update rails-migrate \
--command "bash" \
--args "-c,DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:schema:load:queue db:schema:load:cache db:schema:load:cable" \
--region $GOOGLE_CLOUD_REGION
gcloud run jobs execute rails-migrate --region $GOOGLE_CLOUD_REGION --wait
上圖顯示
gcloud run jobs executions describe $JOBNAME
Google Cloud Run Jobs 控制台顯示
rails-migrate
執行成功
4. 第四次部署:透過 Docker Compose 部署多容器 Cloud Run
現在直接從 compose.prod.yaml 部署真正的 3 個容器生產堆疊 (web、worker、cloudsql-proxy):
# 1. Self-healing environment exports (guarantees no missing master key or DB password)
export RAILS_MASTER_KEY="${RAILS_MASTER_KEY:-$(cat config/master.key 2>/dev/null || gcloud secrets versions access latest --secret=rails-master-key)}"
export DB_PASSWORD=$(gcloud secrets versions access latest --secret=rails-db-password)
export CLOUDSQL_INSTANCE_CONNECTION_NAME="${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME}"
# 2. Database credentials & GCS attachments
export DB_USER="rails_user"
export DB_NAME="rails_production"
export GCS_BUCKET="${GCS_BUCKET}"
export ACTIVE_STORAGE_SERVICE="google"
export GOOGLE_CLOUD_ACCOUNT="${GOOGLE_CLOUD_ACCOUNT:-$(gcloud config get-value account)}"
export GEMINI_API_KEY="${GEMINI_API_KEY:-dummy-key}"
# 3. Deploy the full multi-container sidecar architecture to Cloud Run
gcloud run compose up compose.prod.yaml \
--region=$GOOGLE_CLOUD_REGION \
--allow-unauthenticated
Google Cloud Run 控制台:「Containers」(容器) 分頁,顯示所有三個 Sidecar 容器 (
cloudsql-proxy
、
web
、
worker
)
💡 使用者介面好奇心 / 控制台故障:在控制台下拉式選單中,您可能會發現 Cloud Run 將頂端項目標示為 Ingress container: cloudsql-proxy。別擔心!這是 Google Cloud 控制台的純視覺化 UI 故障 (追蹤記錄為 #149),是由容器依附元件排序所致。事實上,web 是唯一監聽 HTTP 通訊埠 8080 的容器,負責處理 100% 的傳入網路流量,而 cloudsql-proxy 則嚴格處理通訊埠 5432 上的私人 PostgreSQL 流量!
5. ✨ 令人驚豔的時刻:零警告和完整的雲端持續性!
深吸一口氣,然後看看螢幕:所有警示橫幅都消失了!🪄
Rails 8 正式版網誌:提供乾淨的 UI、零警告橫幅,以及持續的 Cloud SQL 狀態!
應用程式現在正式成為正式環境等級的雲端原生應用程式:
- 透過安全的 mTLS 通道,由代管的 Google Cloud SQL (PostgreSQL) 提供支援。
- 透過 IAM Blob 簽署,將媒體安全地儲存在私人 Google Cloud Storage 中。
- 沒有暫時性儲存空間警告,也沒有工作停滯警報。
開啟 Cloud Run 網址:
- 查看遙測徽章:
[CLOUD PERSISTENT 🐘 ☁️]已在資料庫種子中驗證!- 頁尾的「Google Cloud Run」和「GCS」徽章都會亮起綠燈!
- 「工作停滯」警告橫幅完全消失!
- 撰寫網誌文章和留言。
- 您可以盡情重新啟動或重新部署,因為資料、貼文、留言和資產都會永久儲存在 Cloud SQL 和 GCS 中!
6. 自動化步驟 6 驗證
驗證多重容器設定:
just workshop-eval 6
9. 步驟 7:生成式 AI 管道、Podcastifier 和 GCS 尋寶遊戲 🏴☠️
在專用容器中執行 Solid Queue,並啟用 Google Cloud Storage,即可釋放非同步生成式 AI 的潛力!
1. NanoBanana 復古封面產生器 (可立即使用的生成式 AI 管道)

好消息:程式碼集已完整實作這項功能!您不需要編寫任何程式碼,這裡的任務是觸發並觀察非同步雲端 GenAI 管道的運作情形。
🧠 背後的運作機制
- 驗證:完全不需要 API 金鑰!系統會透過 Cloud Run 執行階段服務帳戶,自動使用應用程式預設憑證 (Vertex AI 上的
roles/aiplatform.user)。(或者,如果提供GEMINI_API_KEY,則會直接呼叫 Gemini API)。 - 管道:建立文章時,如果沒有封面圖片,
after_commit回呼會將GenerateCoverImageJob排入 Solid Queue。 - 模型:提示 Google 的多模態模型 (Vertex AI 上的 Gemini 2.5 Flash Image / Imagen) 生成 1960 年代義大利電影海報 (「Locandina di un film 1960」),並附上香蕉客串和閃亮的紅寶石「8」。
- 動態出處浮水印:使用
libvips在角落動態蓋上視覺徽章 (GCS 上的彩色雲朵戳記)。 - Turbo Stream 熱切換:附加圖片後,
Turbo::StreamsChannel.broadcast_refresh_to(post)會即時更新讀者的瀏覽器,不必手動重新載入頁面!

📐 查看 Mermaid 流程圖來源
sequenceDiagram
autonumber
actor User as 👤 Reader / Author
participant Web as 🌐 Web (Puma)
participant DB as 🐘 Cloud SQL (Solid Queue)
participant Worker as ⚙️ Worker (Solid Queue)
participant Vertex as 🧠 Vertex AI (Gemini Flash / Imagen)
participant GCS as ☁️ Google Cloud Storage
User->>Web: POST /posts (Title & Body, No Cover)
Web->>DB: 1. Save Post & Enqueue GenerateCoverImageJob
Web-->>User: Redirect to Post (turbo_stream subscribed)
Worker->>DB: 2. Dequeue GenerateCoverImageJob
Worker->>Vertex: 3. Prompt for 1960s Italian Poster (ADC auth)
Vertex-->>Worker: 4. Raw Image Buffer
Worker->>Worker: 5. Stamp Provenance Watermark (libvips)
Worker->>GCS: 6. Attach Blob via ActiveStorage (IAM signBlob)
Worker->>Web: 7. Turbo::StreamsChannel.broadcast_refresh_to(post)
Web-->>User: ⚡ Live UI Hot-Swap: Poster magically appears!
🧪 立即試用!
你可以透過兩種方式觸發這項功能:
- 方法 A (網頁介面):按一下「New post」,輸入標題 (例如「Serverless Architecture with Ruby on Rails 8」),撰寫一些內容,然後將封面圖片檔案輸入留空。按一下「建立貼文」。
- 方法 B (CLI Runner):從終端機執行:
bin/rails runner 'Post.create!(title: "Serverless Architecture with Ruby on Rails 8", body: "Exploring asynchronous AI pipelines with Solid Queue and NanoBanana!")'
等待約 15 至 30 秒:在 Cloud Run 中查看 Solid Queue 工作人員記錄。復古海報就會直接顯示在貼文中!
📸 TODO(riccardo):新增網誌文章的螢幕截圖,當中包含 AI 生成的 1960 年代義大利電影海報,海報上出現香蕉和紅寶石 8 的客串畫面
2. 雙語 Podcast 製作工具任務 (反重力程式設計實作練習)
⚠️ 重要通知:與上述封面產生器不同,這項功能尚未實裝!
開啟 blog/app/jobs/podcastifier_job.rb 後,您會看到空白的架構存根:
class PodcastifierJob < ApplicationJob
queue_as :default
def perform(post_id)
# TODO(student): COMPLETE_ME — Pair program with Google Antigravity to implement
# Italian TTS podcast generation using voice 'it-IT-Wavenet-A' via Cloud TTS / ADC!
end
end
在這項實作練習中,您的任務是與 Google Antigravity 進行配對程式設計,完成這項功能:
- 提示 Antigravity:「Help me implement PodcastifierJob in
app/jobs/podcastifier_job.rb. It should fetch the Post, translate or synthesize an Italian audio overview using Google Cloud Text-to-Speech (voice ‘it-IT-Wavenet-A' with language code ‘it-IT' via ADC credentials), and attach the audio file topost.podcast_audio_itusing ActiveStorage!" - 將播放器新增至檢視畫面:在
app/views/posts/show.html.erb中,於@post.podcast_audio_it.attached?時新增 HTML5播放器 (在 時)。 - 觸發合成:儲存後,Solid Queue 會在背景非同步執行文字轉語音作業。

🎧 Podcastifier 輸出內容範例:
💡 需要協助或遇到問題嗎?我們在 solutions/podcastifier 分支中提供經過實戰測試的完整參考解決方案。您隨時可以使用 git diff solutions/podcastifier 檢查。
3. [選用] 🏴☠️ GCS 尋寶遊戲 (控制台 Blob 復原)
ℹ️ 必要條件檢查:本機
cloud-sql-proxy
需要使用 CLI:這項選用練習會透過加密的 mTLS 通道,將本機筆電直接連線至 Google Cloud SQL。檢查是否已安裝 CLI Proxy:
which cloud-sql-proxy
- 如果已安裝:請繼續下方的步驟!
- 如果尚未安裝,但您想完成這項任務:請使用下列指令安裝:
# Linux (x86_64) curl -o /tmp/cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.linux.amd64 chmod +x /tmp/cloud-sql-proxy && sudo mv /tmp/cloud-sql-proxy /usr/local/bin/cloud-sql-proxy # macOS (Homebrew) brew install cloud-sql-proxy - 其他情況:請直接跳到步驟 7 的自動驗證!
還記得您在步驟 4 中上傳的相片嗎?容器重新啟動前,暫時性 SQLite 資料庫已清除該相片。該圖片檔案仍安全地存放在您的私人 GCS 值區中,成為「孤立的 Blob」!
我們將使用 Rails 控制台搶救該附件,並附加至 Cloud SQL 貼文:
# Connect to your production database via Rails console and Cloud SQL Proxy
cloud-sql-proxy --port 5432 ${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_REGION}:${SQL_INSTANCE_NAME} &
DATABASE_URL=postgresql://rails_user:${DB_PASSWORD}@127.0.0.1:5432/rails_production bin/rails c
在 Rails 控制台中:
# 1. Search for orphaned GCS blobs in ActiveStorage
orphan_blobs = ActiveStorage::Blob.where.missing(:attachments)
puts "🏴☠️ Found #{orphan_blobs.count} orphaned GCS blobs!"
# 2. Attach the surviving blob to your latest post
if orphan_blobs.any?
post = Post.last
post.cover_image.attach(orphan_blobs.first)
post.save!
puts "🎉 Rescued blob #{orphan_blobs.first.filename} attached to '#{post.title}'!"
end
exit
📸 TODO(riccardo):新增互動式 Rails 控制台的螢幕截圖,執行 GCS Treasure Hunt 片段並搶救孤立的 ActiveStorage Blob
重新整理網誌:在步驟 4 的無狀態震盪期間上傳的相片會復活,並永久附加至 Cloud SQL 貼文!
4. 自動化步驟 7 驗證
驗證生成式 AI 工作和素材資源:
just workshop-eval 7
10. 步驟 8:選擇你的冒險旅程 / 進階任務 🏆
您已掌握標準參考架構,現在請選擇畢業任務!
🛡️ 任務 1:零信任 Google IAP (Identity-Aware Proxy)
- 難度:中等 (企業安全)
- 目標:鎖定 Cloud Run 應用程式,只允許通過驗證的 Google / Gmail 帳戶存取,完全不需要密碼登入!
- 運作方式:
- 外部 HTTPS 應用程式負載平衡器會終止 Google OAuth,並在流量抵達 Cloud Run 之前驗證身分。
- Google 會轉送已驗證的身分識別標頭 (
X-Goog-Authenticated-User-Email)。 - Rails 會透過
blog/app/controllers/concerns/iap_authenticatable.rb自動登入已驗證的 Google 身分:module IapAuthenticatable extend ActiveSupport::Concern included { before_action :authenticate_via_iap } private def authenticate_via_iap return unless (raw = request.headers["X-Goog-Authenticated-User-Email"]).present? email = raw.sub(/^accounts\.google\.com:/, "") user = User.find_or_create_by!(email_address: email) { |u| u.password = SecureRandom.hex(16) } start_new_session_for(user) unless authenticated? end end - 在
iac/iap.tf中使用enable_iap = true啟用 Terraform 模組,並指定允許使用的 Google 帳戶:iap_allowed_users = [var.google_cloud_account]

📊 任務 2:生產環境 SRE 遙測與 Cloud Logging
- 難度:中等 (可觀測性)
- 目標:將含有追蹤關聯 ID 的結構化 JSON 應用程式記錄,直接串流至 Google Cloud Logging,並透過 Cloud Error Reporting 即時掌握實際工作環境中的例外狀況。
- 運作方式:
- 設定
blog/config/environments/production.rb,以發出含有 GCP 追蹤標籤的結構化 JSON 記錄。 - 觸發有意的測試例外狀況,並立即查看 Google Cloud Error Reporting 分組和通知。
- 設定
📸 TODO(riccardo):新增 Google Cloud Error Reporting 的螢幕截圖,顯示已分組的正式版例外狀況,以及包含追蹤標籤的 Cloud Logging 結構化 JSON 酬載
🧠 任務 3:pgvector語意搜尋和 Gemini RAG
- 難度:困難 (生成式 AI 結業專案)
- 目標:使用儲存在 PostgreSQL 適用的 Cloud SQL 中的向量嵌入,從概念上搜尋文章。
- 運作方式:
- 在 Cloud SQL PostgreSQL 執行個體上啟用
vector擴充功能:CREATE EXTENSION IF NOT EXISTS vector; - 將
neighborgem 新增至blog/Gemfile,並使用 Gemini (text-embedding-004) 在Post#after_save上生成文字嵌入。 - 執行餘弦距離查詢 (
<=>),透過 Turbo Streams 支援語意搜尋列!
- 在 Cloud SQL PostgreSQL 執行個體上啟用
📸 TODO(riccardo):新增即時網誌語意搜尋列的螢幕截圖,顯示概念相符的結果和餘弦相似度分數
⚡ 任務 4:SEO 和成效稽核助理
- 難度:簡單 / 有趣 (開發人員體驗)
- 目標:使用 Antigravity 和 Speedgrapher 工具稽核 Core Web Vitals、Largest Contentful Paint (LCP) 和 Flesch-Kincaid / Fog 可讀性指數。
📸 TODO(riccardo):新增 Speedgrapher / Lighthouse 搜尋引擎最佳化和 Core Web Vitals 稽核摘要的螢幕截圖
🤔 Curiosity Box:在 Cloud Run 上取得 Live Rails 控制台
💡 問題:在傳統 VM 或 Heroku 樣式的 PaaS 平台上,開發人員通常會透過 SSH 連線至執行中的執行個體,或使用 heroku run rails console 即時檢查或修正正式環境資料。Cloud Run 原生對應項目為何?如何強化安全性?
建議原因
gcloud run exec
不適用於 Cloud Run 服務
Cloud Run 是無伺服器平台:您的容器可能會以零個、一個或數百個相同執行個體的形式執行,且沒有穩定的身分或可定址的殼層。這與 Kubernetes (kubectl exec) 的做法截然不同,在 Kubernetes 中,特定 Pod 具有可附加的具名通訊端。
# ❌ This does NOT exist for Cloud Run Services:
gcloud run services exec blog --region europe-west1 -- rails console
# ✅ This exists for Cloud Run JOBS (non-interactive, one-shot tasks):
gcloud run jobs execute my-job --region europe-west1 --wait
四種 GCP 原生模式
模式 | 指令 | 互動式? | 安全性 |
1. 本機 Proxy + 遠端資料庫 |
| ✅ 完整 | 🔒 Cloud SQL Auth Proxy mTLS |
2. Cloud Run 工作 (一次性) |
| ❌ 僅限指令碼 | 🔒 需要 IAM 角色 |
3. Web Console Gem + IAP | 瀏覽器 → | ✅ 完整 | 🛡️ IAP 零信任 |
4. Cloud Shell + Proxy | Cloud Shell → | ✅ 完整 | 🔒 Google 身分 |
模式 1 (建議用於檢查資料):執行本機 Rails 控制台,透過 Cloud SQL 驗證 Proxy 直接與 Cloud SQL PostgreSQL 通訊:
# In one terminal: start the auth proxy
cloud-sql-proxy rails8-fl-20260907b-xm8ko4:europe-west1:your-instance
# In another terminal: open a read-write or sandbox console
DATABASE_URL=postgresql://rails_user:${DB_PASSWORD}@127.0.0.1:5432/rails_production \
bin/rails console --sandbox
您可享有完整的 IRB 互動性,且完全不會暴露於網路中。Cloud SQL Proxy 會透過 Unix 網域通訊端或迴路介面使用 mTLS。
模式 3 (網頁控制台 + IAP 零信任,強化瀏覽器路徑):
web-console Gem 提供以瀏覽器為基礎的 IRB 工作階段。如果沒有保護措施,這就是重大安全漏洞,因此絕不能公開。但如果搭配 Google IAP (Quest 1),攻擊面就會縮小:只有許可清單中經過驗證的 Google 身分,才能連線至 /rails/console 端點。
# Gemfile — only load in development/staging, never production without IAP!
gem "web-console", group: :development
# config/environments/production.rb — if IAP is enabled:
config.web_console.allowed_ips = ["0.0.0.0/0"] # IAP terminates auth before us
⚠️ 模式 3 必須先啟用任務 1 (應用程式內購)。如果沒有,在公開網址上使用 /rails/console 就等同於讓伺服器機房門戶洞開。透過 IAP,這項服務會成為強大的運算子工具,並採用 Google 級別的零信任安全機制。
詢問 Antigravity: 「請說明如何執行
rails console --sandbox
使用 Cloud SQL Auth Proxy,安全地對 Cloud SQL 生產環境資料庫執行查詢。
🏆 畢業於 The Hive:經監考人員驗證的工作量證明
完成所選任務後,請前往蜂巢排行榜領取第 8 步畢業獎盃 🏆:
- 在 GitHub 上提交任務:
- 在官方存放區開啟新問題:github.com/palladius/rails8-app-on-gcp/issues/new
- 標題格式:
🎓 [Step 8 Completed]: - 在郵件內文中簡短說明您完成的作業,並附上 Cloud Run 部署作業的連結。
- 將 Cloud Run 應用程式連結至問題:
- 記下問題編號 (例如
88) 或完整網址https://github.com/palladius/rails8-app-on-gcp/issues/88。 - 將
STEP_8_GHI環境變數注入 Cloud Run 服務:gcloud run services update blog \ --region europe-west1 \ --update-env-vars STEP_8_GHI=<YOUR_ISSUE_NUMBER> - 確認
/status.json端點現在會回報任務: 您應該會看到:curl -s https://<YOUR_APP>.run.app/status.json | jq .quest{ "step_8_completed": true, "ghi_issue": 88, "ghi_url": "https://github.com/palladius/rails8-app-on-gcp/issues/88" }
- 記下問題編號 (例如
- 通過監考人員審查並解鎖金盃:
- 查看蜂巢排行榜:你的資料列現在會顯示琥珀色徽章:
⏳ GHI #XX review pending。 - 請工作坊監考人員 (@palladius、@emilianodellacasa 或 @ricc) 檢查您的部署作業。
- 只要監考人員在你的問題上留言
LGTM,The Hive 就會自動將你的進度提升至 8/8 🏆,並顯示金色/紫色發光徽章,以及直接連結至結業專題問題的永久獎盃!
- 查看蜂巢排行榜:你的資料列現在會顯示琥珀色徽章:
11. 結論和清除作業 🎓
恭喜!🎉
您已在 Google Cloud Platform 上建構及部署適用於企業的 Rails 8 應用程式,並可投入實際工作環境:
- 零分支進展:透過
main上的 Time-Machine 疊加層,掌握現代工作流程。 - Cloud Storage:可擴充的私人物件儲存空間,並透過 IAM Blob 簽署 (
iam: true)。 - Cloud SQL:透過 Cloud SQL Auth Proxy mTLS 管道保護代管 PostgreSQL。
- Secret Manager:完全不會洩漏純文字憑證或
.env檔案。 - Cloud Run 多重容器:隔離的 Puma 網頁、Solid Queue 工作站和 Proxy Sidecar。
- 生成式 AI:使用 Vertex AI 製作復古海報和 TTS 播客。
- 任務:零信任 IAP、SRE 可觀測性,以及 pgvector 嵌入項目!
🧹 清除資源
如要避免在完成研討會後持續產生費用,請務必清理 Google Cloud 資源!請選擇最適合您環境的方法:
方法 1:Terraform 拆除 (建議 / 預設 🟢)
如果您使用現有或共用的 Google Cloud 雲端專案,其中有其他資源或資料,請選取這個選項。這項指令只會清除研討會期間佈建的資源 (Cloud Run、Cloud SQL、Secret Manager、GCS bucket),專案中的其他資源則不受影響。
cd iac
terraform destroy -auto-approve
選項 2:徹底刪除專案 (不留任何痕跡 🌪️)
如果您已建立專屬研討會專案 (例如使用研討會抵免額或沙箱),並想確保完全清除所有內容 (包括記錄、中繼資料、服務帳戶和帳單連結),請使用這個選項。
gcloud projects delete $GOOGLE_CLOUD_PROJECT --quiet