前一天把雲端比喻成網站準備入住的新店面。現在店面選好了,接下來就要真的搬家:先接好網路與資料庫,再搬進後端 API,最後把前端網頁的大門打開。
本篇會把 Vue、ASP.NET Core API 與 SQL Server 部署到 Google Cloud。完成後,使用者可以從瀏覽器開啟網站、登入系統,並且真的把資料寫進雲端資料庫。
這次部署會分成三條主線:
/api 請求代理給後端。API 得先找得到資料庫,前端也得知道 API 住在哪裡。所以不能像搬箱子一樣想搬哪箱就搬哪箱,而是要照著「資料庫 → 後端 API → 前端網頁」的相依順序進行。
*本文需要在 Google Cloud 建立專案並連結付款帳戶。Cloud SQL 等資源建立後可能持續產生費用,請先確認自己可以接受後再繼續。
本文專心處理 Google Cloud 的操作。後端與前端所需的 Dockerfile、Cloud Build 設定、Nginx 設定與正式環境參數,都視為已在部署前完成並驗證。這些檔案就像搬家前已經打包好的箱子;本篇要處理的,是雲端資源的建立、設定、部署與驗收。
還有一條邊界需要先說清楚。Day26 提到的「Cloud Scheduler 定時啟動 Cloud Run Job」是到期提醒功能的雲端改造目標,但目前後端程式仍由 Hangfire 常駐排程。本篇只部署網站主線,不把尚未改造的排程工作說成已上線;API 部署時會先關閉 ReminderJobs,等專用 Job 完成後,再由 Cloud Scheduler 啟動新的提醒流程。
| 本機內容 | Google Cloud 上的位置 | 本篇對應章節 |
|---|---|---|
| SQL Server | Cloud SQL for SQL Server | 資料庫部署 |
| EF Core Migration | Cloud Run Job | 資料庫部署 |
| ASP.NET Core API | Cloud Run service | 後端 API 部署 |
| Vue 3 網頁與 Nginx | Cloud Run service | 前端網頁部署 |
| Docker 映像 | Artifact Registry | 共用的部署倉庫 |
| 連線字串、JWT Key | Secret Manager | 機密設定 |
以下流程圖分成兩列。第一列先準備共用資源與資料庫,第二列才輪到後端、前端與瀏覽器驗收。每一格都是下一格的前置條件;若某一格失敗,先留在原地查清楚,不要急著往後按「建立」。

先把三條部署路線共用的專案、帳單、API 與區域設定好。這一段很像搬家前先確認地址、簽好租約、接上水電;地基沒有處理好,後面的服務再多也站不穩。
在頁面最上方的 Project 選擇器確認:
專案名稱:ProjectManagementWeb
Project ID:solid-terra-509706-e5
專案名稱和 Project ID 是兩個不同欄位。這次畫面上顯示的名稱是 ProjectManagementWeb,真正會出現在 Service Account、映像路徑與資源識別資訊裡的 Project ID,則是 solid-terra-509706-e5。後面的設定若需要填寫 Project ID,不能直接拿專案名稱代替。
接著前往:
主選單
→ Billing
→ My projects
確認 solid-terra-509706-e5 已連結有效的 Billing Account。本文不放付款帳戶與信用卡畫面,避免把帳務資訊一併公開。
進入帳單總覽後,左上角會顯示目前選取的帳單帳戶,中間則是費用摘要。只要能正常開啟總覽,並在「我的專案」中看到 solid-terra-509706-e5 已連結,就表示這個 Project 可以建立需要計費的雲端資源。

啟用 API 本身不會建立 Cloud SQL,但後面按下「Create instance」後,Cloud SQL 就會開始產生運算與儲存費用。
| API | 在這次部署中的用途 |
|---|---|
| Cloud SQL Admin API | 建立與管理 Cloud SQL for SQL Server |
| Compute Engine API | 支援 VPC 與部分網路資源 |
| Service Networking API | 建立 Private Services Access |
| Cloud Run Admin API | 執行 API、前端網頁與 Migration Job |
| Artifact Registry API | 儲存 Docker 映像 |
| Secret Manager API | 保存連線字串與機密設定 |
| Cloud Build API | 從原始碼或 Dockerfile 建置映像 |
先點選左上角的導覽選單,依序進入:
導覽選單
→ API 和服務
→ 程式庫

進入 API 程式庫後,在「搜尋 API 和服務」欄位輸入要啟用的 API 名稱。例如先搜尋 Cloud SQL Admin API,再從搜尋結果點進服務詳細頁面。

確認服務名稱後按下「啟用」。其餘 API 也按照相同步驟逐一處理。

畫面顯示「API 已啟用」,只代表這個 Project 取得使用服務的入場券。它不會自動幫忙建立 Cloud SQL 或 Cloud Run,也不代表計費資源已經開始運作。
這次主要使用台灣的 asia-east1,讓 Cloud Run、Artifact Registry 與 Cloud SQL 盡量住在同一個 Region。把倉庫、店面與資料庫安排在同一個區域,連線較單純,也比較不容易產生沒必要的跨區流量。
| 資源 | 本次使用的名稱 |
|---|---|
| VPC | projectmanagementweb-vpc |
| Cloud SQL instance | projectmanagementweb-sql |
| Database | ProjectManagementWeb |
| Artifact Registry repository | projectmanagementweb |
| Migration Job | database-migrator |
| 後端 Cloud Run service | api |
| 前端 Cloud Run service | projectmanagement-web |
部分 Google Cloud 資源建立後不能直接修改 Region,因此按下「建立」前,記得確認設定為 asia-east1。
Cloud SQL 使用 Private IP 時,不會自動和 Cloud Run 變成鄰居。我們要先準備一條不經公開網際網路的內部通道,讓 VPC 和 Cloud SQL 所在的 Google 代管服務網路連起來。官方名稱是 Private Services Access,完整概念可參考 Google Cloud Private Services Access。
在分配私人 IP 範圍之前,專案裡要先有一個 VPC。VPC 可以先想成雲端裡的私有社區;之後會讓 Cloud Run 透過這個社區的內部道路前往 Cloud SQL。點選左上角的導覽選單,進入「虛擬私有雲網路 → 虛擬私有雲網路」。

進入網路清單後,按下頁面上方的「建立虛擬私有雲網路」。

在建立頁面填入這次使用的網路設定:
| 欄位 | 本次設定 |
|---|---|
| 名稱 | projectmanagementweb-vpc |
| 說明 | ProjectManagementWeb Cloud Run and Cloud SQL private network |
| MTU | 自動設定 |
| 子網路建立模式 | 自訂 |
| 私人 IPv6 位址設定 | 不啟用 |

接著在「新的子網路」中建立 Cloud Run Direct VPC egress 使用的子網路:
| 欄位 | 本次設定 |
|---|---|
| 名稱 | projectmanagementweb-asia-east1 |
| 區域 | asia-east1 |
| IP 堆疊類型 | IPv4(單一堆疊) |
| 主要 IPv4 範圍 | 使用不與其他子網路或稍後 PSA 範圍重疊的私人 CIDR |
其餘選項維持本次畫面中的預設值:動態轉送模式選「單一地區」,最佳路徑選取模式選「舊版(預設)」。檢查名稱、Region 與 IPv4 範圍後,按下頁面底部的「建立」。等 projectmanagementweb-vpc 出現在網路清單後,再繼續分配私人服務使用的 IP 範圍。
VPC 是我們可以管理的網路,Cloud SQL 則住在 Google 管理的網路裡。接下來要先替 Google 代管服務預留一段不會和現有子網路打架的 IP 範圍,再用它建立兩邊的私人連線。
依照目前的 Google Cloud 中文介面,先點選左上角的導覽選單,再依序進入:
導覽選單
→ 虛擬私有雲網路
→ 虛擬私有雲網路
→ 選擇 projectmanagementweb-vpc
→ 私人服務連線
→ 分配的服務 IP 範圍
→ 分配 IP 範圍
第一個「虛擬私有雲網路」是導覽選單中的服務分類,第二個則是進入服務後,左側功能列中的網路清單。選擇 projectmanagementweb-vpc 後,畫面上方會出現「私人服務連線」分頁;切換到「分配的服務 IP 範圍」,再按下「分配 IP 範圍」。
填入:
| 欄位 | 建議值 |
|---|---|
| Name | google-managed-services-projectmanagementweb-vpc |
| Description | Cloud SQL private services access |
| IP range allocation | Automatic |
| Prefix length | 16 |
接著點擊 Allocate。

/16 可為未來其他 Region 或 Google 代管服務保留空間;Cloud SQL 每個 Region/資料庫類型至少需要可用的 /24 範圍。
私人 IP 範圍分配完成後,仍留在 projectmanagementweb-vpc 的「私人服務連線」頁面,接著依序操作:
私人服務連線
→ 私人服務連線
→ 建立連線
第一個「私人服務連線」是 VPC 詳細資料上方的功能分頁,第二個則是頁面內用來查看現有私人連線的子分頁。
填入:
| 欄位 | 設定 |
|---|---|
| 已連線的服務供應商 | Google Cloud Platform |
| 連線名稱 | servicenetworking-googleapis-com,由 Google Cloud 自動產生 |
| 已指派的分配範圍 | google-managed-services-projectmanagementweb-vpc |
展開「已指派的分配範圍」,勾選前一節建立的 IP 範圍,先按下「確定」,再按下「連線」。

建立完成後,連線清單應顯示:
servicenetworking-googleapis-com
這條私人連線建立後,可供同一個 VPC 中其他支援私人服務存取權的 Google 服務共用,因此不要在日後隨意刪除。

Private Services Access 只是把 Cloud SQL 所在的 Google 代管網路接到 VPC,像是已經鋪好社區外的連絡道。Migration Job 與後端 API 還要設定 Direct VPC egress,才知道該從這條路前往 Cloud SQL 的 Private IP。
這次在 Migration Job 與後端 API 都選擇:
| 欄位 | 設定 |
|---|---|
| Network | projectmanagementweb-vpc |
| Subnet | projectmanagementweb-asia-east1 |
| Network tags | 留白 |
| Traffic routing | 只將私人 IP 的流量傳送到 VPC |
稍後建立 Migration Job 與 API 時,都要套用這組網路設定,否則連線 Cloud SQL 時可能發生逾時。
這一部分會建立 Cloud SQL instance、空白 Database 與兩組資料庫帳號,再透過 Migration Job 建立 ProjectManagementWeb 所需的 Schema 和初始資料。可以把 instance 想成整棟資料庫大樓,Database 則是這棟大樓裡專門給 ProjectManagementWeb 使用的房間。
這次沒有把本機開發資料整批搬上雲端,而是先建立一個空白 Database,再由 EF Core Migration 照著程式裡的版本記錄,建立資料表、關聯與初始資料。這比較像照施工圖布置一個全新空間,不是把舊房間裡的家具全部搬過來。
Database Migration Service(DMS)用來把既有資料從來源資料庫搬到目標資料庫;EF Core Migration 則依照程式專案中的 Migration 建立或更新 Schema。本篇採用後者。若正式環境還要保留本機資料,必須另外規劃資料遷移、停機時間與遷移後驗證。
前往「SQL → 建立執行個體 → 選擇 SQL Server」,依序填入:
| 欄位 | 本次設定 |
|---|---|
| Instance ID | projectmanagementweb-sql |
| Edition | Enterprise |
| Database version | SQL Server 2022 Express |
| Region | asia-east1 |
| Zonal availability | Single zone,asia-east1-c |
| Machine | 1 vCPU、3.75 GB 記憶體 |
| Storage | 10 GB |
| Connectivity | Private IP |
| VPC | projectmanagementweb-vpc |
| Public IP | 關閉 |
| Collation | Chinese_Taiwan_Stroke_BIN |
| Time zone | UTC |
這是教學與展示用途的低規格配置。正式上線時,仍要依流量、備份、可用性與復原目標重新評估。
建立完成後,執行個體清單會顯示 projectmanagementweb-sql 為可用狀態:

Cloud SQL 執行個體建立完成後,點選左上角的導覽選單,依序進入「資料庫 → Cloud SQL」。

從執行個體清單點進 projectmanagementweb-sql,再選擇左側功能列的「資料庫」。剛建立完成時,清單中只有 master、model、msdb 與 tempdb 等 SQL Server 系統資料庫;不要直接把應用程式資料放進這些資料庫。按下頁面上方的「建立資料庫」,開始建立 ProjectManagementWeb 專用的資料庫。

右側會開啟「建立資料庫」面板,在「資料庫名稱」輸入:
ProjectManagementWeb
畫面也會提示名稱必須遵守 SQL Server ID 規則。確認拼字與大小寫後,按下「建立」。

建立作業完成後,回到資料庫清單,確認 ProjectManagementWeb 已經出現。此時 Database 仍是空的,資料表會在執行 Migration Job 後建立。

接著在 Cloud SQL 的「使用者」頁面建立兩組 SQL Server 帳號:
| 帳號 | 用途 | 權限原則 |
|---|---|---|
pmw_migrator |
執行 EF Core Migration 與 Hangfire Schema 初始化 | 可建立或修改 Schema,只在部署時使用 |
pmw_app |
API 平常讀寫業務資料 | 只保留執行階段需要的讀寫權限 |
把帳號拆開,就像裝潢工人與店員不該共用同一把萬能鑰匙。pmw_migrator 只在需要改變結構時出場;API 平常使用的 pmw_app 只能讀寫業務資料,就算 API 發生漏洞,也不會順便送出改資料表結構的權限。
在 Cloud SQL「使用者」頁面建立 SQL Server login 後,還要進入 ProjectManagementWeb Database 建立對應的 user,並加入正確的 database role。本機環境的 docker/sql/init.sql 使用相同原則:pmw_migrator 加入 db_owner,pmw_app 只加入 db_datareader 與 db_datawriter。不要因為 login 已建立,就以為 Database 內的權限也自動跟著完成。
實際密碼請使用隨機高強度字串,而且不要放進文章截圖、Git 或 Dockerfile。
Secret Manager 像一個上鎖的鑰匙櫃。應用程式記住的是 Secret 名稱,不是直接把密碼貼進設定檔或容器映像。
前往「安全性 → Secret Manager」,分別建立:
pmw-db-migrator-connection
pmw-db-app-connection
第一個 Secret 保存 pmw_migrator 的連線字串,交給 Migration Job;第二個保存 pmw_app 的連線字串,交給後端 API。建立 Secret 時才輸入完整連線字串,文章與截圖只保留 Secret 名稱,不公開內容。

建立 Secret 不代表 Cloud Run 自動擁有讀取權。還要建立專給 Migration Job 使用的 pmw-migrator Service Account,再到 pmw-db-migrator-connection 的「權限」頁面,只授予它 Secret Manager Secret Accessor 角色。Service Account 像服務在雲端上的員工證:有鑰匙櫃,還是得有正確的員工證才拿得到鑰匙。

Artifact Registry 與 Cloud Build 的名稱很接近,而且不一定會直接顯示在左側主選單。先釐清兩者的責任:
| 服務 | 這次負責的工作 |
|---|---|
| GitHub Repository | 保存 ASP.NET Core 原始碼、Dockerfile 與部署當時使用的 cloudbuild.yaml |
| Cloud Build | 讀取 GitHub 原始碼,執行容器建置與推送指令 |
| Artifact Registry | 保存建置完成的 api 與 database-migrator Docker 映像 |
Cloud Build 像打包工廠,負責把原始碼與 Dockerfile 做成容器映像;Artifact Registry 則是倉庫,只負責收好建置完成的映像。先建立空倉庫,不會自動變出貨物。Cloud Build 實際成功執行並推送後,裡面才會出現可供 Cloud Run 部署的映像。
最快的方法是使用 Google Cloud Console 頁面上方的搜尋列:
Artifact Registry 或 Cloud Build。如果搜尋結果不容易辨認,也可以走完整產品清單:
導覽選單
→ 查看所有產品
→ CI/CD
→ Artifact Registry 或 Cloud Build
在「所有產品」的 CI/CD 分類中,可以同時看到 Cloud Build 與 Artifact Registry。名稱左側的星號可以把服務加入收藏,之後就會比較容易從導覽選單開啟。

進入 Artifact Registry 後,選擇左側的「存放區」,再按下「建立存放區」。這次建立的是保存容器映像的 Docker 存放區:
| 中文介面欄位 | 本次設定 |
|---|---|
| 名稱 | projectmanagementweb |
| 格式 | Docker |
| 模式 | 標準 |
| 位置類型 | 區域 |
| 區域 | asia-east1 |
| 加密 | Google 代管的加密金鑰 |
| 不可變更的映像檔標記 | 停用 |
建立頁面可能會詢問是否啟用安全漏洞掃描。這不是完成本篇部署的必要條件,而且畫面會提示可能產生額外費用;是否啟用應依專案的安全與成本需求決定。
按下「建立」後,進入 projectmanagementweb 存放區。剛建立完成時沒有 api 或 database-migrator,這是正常狀態,因為 Cloud Build 還沒有執行。

用前面的搜尋方式開啟 Cloud Build,選擇左側的「存放區」。這次使用第 2 代 Cloud Build Repository,操作順序是:
Cloud Build
→ 存放區
→ 第 2 代
→ 連結存放區
依照畫面完成 GitHub 授權,選擇 GitHub 帳號,再連接後端原始碼存放區:
JJDing-Louis/ProjectManagementWeb_BackEnd
這裡連接的是 GitHub 原始碼 Repository,不是前面建立的 Artifact Registry Docker 存放區。完成後,Cloud Build 的存放區清單會顯示 GitHub 供應商與後端 Repository。

接著選擇 Cloud Build 左側的「觸發條件」,按下「建立觸發條件」。本次不是每次 push 都自動建置,而是先建立一個可以手動執行的觸發條件:
| 中文介面欄位 | 本次設定 |
|---|---|
| 名稱 | manual-build-backend-images |
| 區域 | asia-east1(台灣) |
| 說明 | 手動觸發 / Manual invocation |
| 事件 | 手動叫用 |
| 來源存放區服務 | Cloud Build 存放區 |
| 存放區 | JJDing-Louis/ProjectManagementWeb_BackEnd |

往下找到「設定」,選擇:
| 中文介面欄位 | 本次設定 |
|---|---|
| 類型 | Cloud Build 設定檔(YAML 或 JSON) |
| 位置 | 存放區 |
| Cloud Build 設定檔位置 | /cloudbuild.yaml |

/cloudbuild.yaml 前面的 / 代表檔案位於 Repository 根目錄。這是本次部署當下使用的設定;如果日後移動或重新命名檔案,觸發條件也要一起更新,否則 Cloud Build 會找不到設定檔。
部署當時的 cloudbuild.yaml 會依後端 Dockerfile 建立兩個用途不同的映像:
api:部署為長時間接收 HTTP 請求的 Cloud Run service。database-migrator:部署為執行完成就停止的 Cloud Run Job。建立觸發條件後,回到「觸發條件」清單,找到 manual-build-backend-images 並按下「執行」。選擇這次要建置的 Git revision 後送出,再到 Cloud Build 的「記錄」查看進度。
建置狀態變成成功後,還要檢查兩個地方:
cloudbuild.yaml 的錯誤。projectmanagementweb 存放區,確認 api 與 database-migrator 兩個映像都出現。

確認兩個映像都已出現在 Artifact Registry 後,再建立 Migration Job。若 Cloud Build 顯示成功但存放區沒有映像,先查看建置記錄中的映像路徑與推送步驟,再檢查 Cloud Build 的執行身分是否有寫入這個存放區的權限。
前往「Cloud Run → Jobs → 部署容器」,建立 database-migrator,並選擇 Artifact Registry 中的 database-migrator 映像。這次配置為 1 CPU、512 MiB 記憶體,Task timeout 設為 15 分鐘,執行身分則選擇剛才建立的 pmw-migrator Service Account。

在變數與 Secret 設定中加入:
| 名稱 | 來源或值 |
|---|---|
ConnectionStrings__DefaultConnection |
參照 pmw-db-migrator-connection Secret |
PMW_MIGRATION_DEFAULT_TIME_ZONE_ID |
Asia/Taipei |
Hangfire__PrepareSchemaIfNecessary |
true |

網路部分開啟 Direct VPC egress,選擇 projectmanagementweb-vpc 與 projectmanagementweb-asia-east1,流量路由選擇只傳送私人 IP 流量。

部署完成後按下「執行」。這個映像會先執行 dotnet ef database update,再初始化 Hangfire 所需的 Schema。Job 成功完成後就停止,不會像 Cloud Run service 一樣持續提供 HTTP 服務。
本次執行紀錄 database-migrator-mpfx5 顯示一個 Task 成功、Exit code 為 0,約 13 秒完成。

Job 顯示成功後,開啟 Cloud SQL Studio,連進 ProjectManagementWeb 並依序檢查:
__EFMigrationsHistory 是否有 Migration 紀錄。


最後用 pmw_app 帳號測試 API 所需的讀寫操作,並確認它不能修改 Schema。完成這項檢查後,資料庫部署就告一段落。
資料庫可用後,接著把 API 部署到 Cloud Run。完成標準是 Cloud Run Revision 正常啟動、/health 回應成功,而且 API 可以讀寫 Cloud SQL。
本篇使用前面 Cloud Build 產生的 api 映像。後端需要的 Forwarded Headers、CORS、CSRF、Cookie 安全設定及容器設定,都視為已在部署前完成;Schema 更新也已交由 database-migrator Job 處理,API 啟動時不會再次執行 Migration。
後端 API 與 Migration Job 由同一次 Cloud Build 產生。它們使用同一份原始碼,但工作壽命不同:API 像每天營業的店員,會持續接收 HTTP 請求;Migration Job 像裝潢工班,建好 Schema 就離開。前一節已確認 api 與 database-migrator 都在 Artifact Registry 中,因此不需要從本機再次執行 docker push。
日後若有新版後端程式,先把變更推送到 GitHub,再重新執行 manual-build-backend-images。Cloud Build 成功後,回到 Cloud Run 部署新的 api 映像版本。
人會用帳號登入 Google Cloud,雲端服務彼此合作時也需要可辨識的身分。Service Account 就是 API 的員工證。員工證上不該印著「所有門都可以開」,只要給 API 執行當下工作所需的權限。
先從左上角的導覽選單進入:
導覽選單
→ IAM 與管理
→ 服務帳戶

進入「服務帳戶」頁面後,按下「建立服務帳戶」,並填入:
| 欄位 | 本次設定 |
|---|---|
| 服務帳戶名稱 | pmw-api |
| 服務帳戶 ID | pmw-api |
| 說明 | ProjectManagementWeb Cloud Run API runtime identity |
確認後按下「建立並繼續」。這裡不直接授予範圍過大的 Project 角色;建立完成後,再針對 API 需要讀取的 Secret 個別授權。最後產生的 Service Account 電子郵件為:
pmw-api@solid-terra-509706-e5.iam.gserviceaccount.com
在建立 Cloud Run service 前,先到下列 Secret 的「權限」頁面,把 Secret Manager Secret Accessor 授予 pmw-api:
pmw-db-app-connection
pmw-jwt-private-key
pmw-db-migrator-connection 不在這份清單中,因為它只提供給 Migration Job 使用。
從導覽選單進入「Cloud Run → 服務」。這個頁面會列出目前 Project 中所有持續接收 HTTP 請求的 Cloud Run service;前面建立的 database-migrator 則會出現在「工作」頁面。

進入服務清單後,按下頁面上方的「部署容器」。如果 Project 還沒有任何 Cloud Run service,也可以按畫面中央的「建立服務」。

在「建立服務」頁面選擇「依據現有的容器映像檔部署單一修訂版本」,再從 Artifact Registry 選擇 projectmanagementweb/api 映像。接著填入:
| 欄位 | 本次設定 |
|---|---|
| Service name | api |
| Region | asia-east1 |
| Container port | 8080 |
| Billing | Request-based |
| Minimum instances | 0 |
| Service Account | pmw-api |
| Authentication | 允許未經驗證的叫用 |
前端 Nginx 會從另一個 Cloud Run service 轉送瀏覽器的 API 請求,因此這裡允許未經驗證的叫用。這句話容易誤會:它只是讓網路請求到得了 Cloud Run 大門,不是讓任何人直接變成系統管理員。API 內部的登入、授權、CSRF 與輸入驗證仍會照常執行。
在環境變數與 Secret 中設定正式環境需要的值,包括:
ASPNETCORE_ENVIRONMENT。ConnectionStrings__DefaultConnection,來源為 pmw-db-app-connection。Cors__AllowedOrigins__0,值為前端 Cloud Run 實際使用的 origin。ReminderJobs__Enabled=false,在專用的雲端排程 Job 完成前,不讓 request-based Cloud Run service 假裝是會常駐的排程主機。若前端 service 還沒建立,此時還不知道它的最終 origin。可以先完成 API 部署,等前端取得 Cloud Run 網址後,回到 API 補上 Cors__AllowedOrigins__0、部署新 Revision,再進行整合驗收。
網路部分和 Migration Job 相同,使用 Direct VPC egress 連到 projectmanagementweb-vpc,並只將私人 IP 流量送進 VPC。
部署完成後,Cloud Run 會提供 asia-east1.run.app 網址。這次服務的主要網址為:
https://api-956023471429.asia-east1.run.app

確認設定後按下「建立」。部署完成時,服務狀態會轉為綠色,並顯示目前正在接收流量的 Revision。

/health,再測需要資料庫的 APIAPI Revision 顯示綠色後,先開啟:
https://api-956023471429.asia-east1.run.app/health
/health 像先問店員「你有沒有上班?」,能回應只能證明應用程式活著。接著還要呼叫一支會讀取資料庫的 API,才能證明店員拿得到後方倉庫的資料。兩項檢查都通過,再繼續部署前端。若檢查失敗,可從 Cloud Run Log 依序確認 Secret 權限、連線字串、Direct VPC egress、Cloud SQL Private IP 與資料庫帳號。
後端通過檢查後,就可以把它的 Cloud Run URL 填入前端容器的 BACKEND_ORIGIN。這裡的 Nginx 像服務台:瀏覽器只會對前端同網域下的 /api/v1 提出請求,Nginx 看到 /api/ 開頭的路徑後,再從容器內部把請求轉給後端。
這裡假設前端 Repository 已有可用的 Dockerfile 與 Nginx 設定:Vue production build 會被包進容器、Vue Router 重新整理時會回到 index.html,而 /api/** 會代理到 $BACKEND_ORIGIN。本篇直接使用這份已驗證的部署設定連接 Cloud Run。
前往「Cloud Run → Services → Connect repository」,選擇使用 Cloud Build,接著連接:
JJDing-Louis/ProjectManagementWeb_FrontEnd

建置設定使用 ^main$ 作為 Branch regex,Build type 選擇 Dockerfile,路徑填 /Dockerfile。服務設定如下:
| 欄位 | 設定 |
|---|---|
| Service name | projectmanagement-web |
| Region | asia-east1 |
| Container port | 8080 |
| Billing | Request-based |
| Minimum instances | 0 |
| Authentication | 允許未經驗證的叫用 |
| Ingress | 全部 |

在環境變數加入 BACKEND_ORIGIN,值填部署後端時取得的 origin。只填到網域,不加 /api/v1,結尾也不要再加 /:
BACKEND_ORIGIN=https://實際的後端-Cloud-Run-網址
下圖保留部署當時使用的有效後端網址。若日後重建後端服務,請以 Cloud Run 當下顯示的網址為準。

這個網站要公開展示,因此 Ingress 選擇「全部」,並允許未經驗證的叫用。設定完成後按下「建立」,等待 Cloud Build 建置映像並完成 Cloud Run Revision 部署。
部署成功後,Cloud Run 提供的前端網址為:
https://projectmanagement-web-956023471429.asia-east1.run.app
先開啟登入頁,再使用預先建立的帳號登入。登入後能顯示使用者與專案資料,表示前端、Nginx 代理、後端 API 與 Cloud SQL 已經接通。



三個部分都部署完成後,不要只看 Cloud Run 上的綠色狀態。那像是搬家公司通知「箱子已送達」,還不代表水龍頭、門鎖與收銀機都能用。回到瀏覽器,依序檢查:
/sign-in,確認沒有 404。/api/v1/security/csrf-token。/api/v1/auth/me。PMW-REFRESH Cookie 的 HttpOnly、Secure、SameSite=Lax 與 Path=/api/v1/auth。Cloud Run 設為 Minimum instances 0 時,沒有請求可以縮到零,但 Cloud SQL instance 不會因為沒有人使用就自動消失,仍可能持續產生運算與儲存費用。Artifact Registry 的映像儲存、Cloud Build 的建置時間、Secret Manager 的存取與 Cloud Logging 的用量,也各有自己的計費方式。
展示期間可以設定 Budget 與 Alert 追蹤費用。Budget Alert 只會發出通知,不會自動停止服務。
部署完成後,再做一次最小權限檢查:
pmw-api 只能讀取 API 實際需要的 Secret。pmw-db-migrator-connection。pmw_app 不具備修改 Schema 的高權限。若網站只用於短期展示,可以依照下列順序收尾:
Private Services Access 或 VPC 可能同時被其他資源使用,刪除前要先檢查相依關係,不要只因為本篇部署結束就直接移除。
這次的建立順序是:先準備 VPC 與 Private Services Access,再建立 Cloud SQL、空白 Database、帳號與 Secret,接著透過 Cloud Build 產生映像,並用 Cloud Run Job 建立 Schema。資料庫驗證完成後,依序部署後端 API 與前端網頁,最後從瀏覽器完成登入、讀取、寫入與登出測試。
把順序排清楚後,每一段都有明確的輸入與檢查點。之後需要重新部署時,也可以沿著同一條路線判斷要更新映像、Cloud Run Revision,還是資料庫 Migration,而不必重建整套環境。
網站搬上雲端不是故事結束。使用者開始操作後,新想法與修正一定會再出現。下一篇會把鏡頭拉回開發流程,看看需求改變時,如何請 Agent 讀懂現況、開發修改,並保留可審查的變更記錄。