1. 簡介
總覽
為了保護服務和應用程式的網路流量,許多機構在 Google Cloud 上使用邊界控管機制,以防止資料竊取。虛擬私有雲網路是實體網路的虛擬版本,而且已導入 Google 的正式環境網路。虛擬私有雲網路可為 Compute Engine 虛擬機器 (VM) 執行個體提供連線、為內部應用程式負載平衡器提供原生內部直通式網路負載平衡器和 Proxy 系統、使用 Cloud Interconnect 的 Cloud VPN 通道和 VLAN 連結連線至地端部署網路,以及將 Google Cloud 外部負載平衡器的流量分配給後端。
與 VM 不同,Cloud Run 服務預設不會與任何特定的虛擬私有雲網路建立關聯。本程式碼研究室說明如何變更輸入 (傳入連線) 設定,只讓來自虛擬私有雲的流量可以存取 Cloud Run 服務 (例如後端服務)。另外,本程式碼研究室將說明如何讓第二服務 (例如前端服務) 透過虛擬私有雲存取後端 Cloud Run 服務。
在這個範例中,後端 Cloud Run 服務會傳回「hello world」。前端 Cloud Run 服務會在 UI 中提供收集網址的輸入欄位,接著,前端服務會向該網址 (例如後端服務) 發出 GET 要求,使此服務成為服務要求,而非透過瀏覽器對服務要求。當前端服務成功抵達後端時,瀏覽器就會顯示「hello world」訊息。
課程內容
- 如何僅允許從虛擬私有雲傳送至 Cloud Run 服務的流量
- 如何在 Cloud Run 服務中設定輸出,以便與僅限內部輸入的 Cloud Run 服務進行通訊
2. 設定和需求
必要條件
- 您已登入 Cloud 控制台。
- 先前已部署 Cloud Run 服務。舉例來說,您可以按照從原始碼部署網路服務的快速入門導覽課程著手。
啟用 Cloud Shell
- 在 Cloud 控制台中,按一下「啟用 Cloud Shell」圖示
。
如果您是第一次啟動 Cloud Shell,系統會顯示中繼畫面,說明這項服務的內容。如果系統顯示中繼畫面,請按一下「繼續」。
佈建並連線至 Cloud Shell 只需幾分鐘的時間。
這個虛擬機器已載入所有必要的開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。在本程式碼研究室中,您的大部分作業都可透過瀏覽器完成。
連線至 Cloud Shell 後,您應會發現自己通過驗證,且專案已設為您的專案 ID。
- 在 Cloud Shell 中執行下列指令,確認您已通過驗證:
gcloud auth list
指令輸出
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- 在 Cloud Shell 中執行下列指令,確認 gcloud 指令知道您的專案:
gcloud config list project
指令輸出
[core] project = <PROJECT_ID>
如果尚未設定,請使用下列指令進行設定:
gcloud config set project <PROJECT_ID>
指令輸出
Updated property [core/project].
3. 建立 Cloud Run 服務
設定環境變數
您可以設定將在本程式碼研究室中使用的環境變數。
REGION=<YOUR_REGION, e.g. us-central1> FRONTEND=frontend BACKEND=backend
建立後端 Cloud Run 服務
首先,建立原始碼的目錄,並以 cd 指向該目錄。
mkdir -p internal-codelab/frontend internal-codelab/backend && cd internal-codelab/backend
接著,建立含有以下內容的 package.json
檔案:
{ "name": "backend-service", "version": "1.0.0", "description": "", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.18.1" } }
接著,建立含有以下內容的 index.js
來源檔案。此檔案包含服務的進入點,且包含應用程式的主要邏輯。
const express = require('express'); const app = express(); app.use(express.urlencoded({ extended: true })); app.get('/', function (req, res) { res.send("hello world"); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, () => { console.log(`helloworld: listening on port ${port}`); });
最後,部署 Cloud Run 服務並執行下列指令。
gcloud run deploy $BACKEND --source . --allow-unauthenticated --region $REGION
建立前端 Cloud Run 服務
前往前端目錄。
cd ../frontend
接著,建立含有以下內容的 package.json
檔案:
{ "name": "frontend", "version": "1.0.0", "description": "", "scripts": { "start": "node index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "axios": "^1.6.6", "express": "^4.18.2" } }
接著,建立含有以下內容的 index.js
來源檔案。此檔案包含服務的進入點,且包含應用程式的主要邏輯。
const express = require("express"); const app = express(); const port = 8080; const path = require('path'); const axios = require('axios'); // serve static content (index.html) using // built-in middleware function in Express app.use(express.static('public')); app.use(express.urlencoded({ extended: true })); // this endpoint receives a URL in the post body // and then makes a get request to that URL // results are sent back to the caller app.post('/callService', async (req, res) => { const url = req.body.url; let message = ""; try { console.log("url: ", url); const response = await axios.get(url); message = response.data; } catch (error) { message = error.message; console.error(error.message); } res.send(` ${message} <p> </p> `); }); app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
為 index.html 檔案建立公開目錄
mkdir public touch public/index.html
並更新 index.html,加入以下內容:
<html> <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous" ></script> <body> <div style="margin-top: 100px; margin-left: 100px"> <h1>I'm the Frontend service on the Internet</h1> <form hx-trigger="submit" hx-post="/callService" hx-target="#message"> <label for="url"> URL:</label> <input style="width: 308px" type="text" id="url" name="url" placeholder="The backend service URL" required /> <button hx-indicator="#loading" type="submit">Submit</button> <p></p> <span class="htmx-indicator" id="loading"> Loading... </span> <div id="message" style="white-space: pre-wrap"></div> <p></p> </form> </div> </body> </html>
最後,部署 Cloud Run 服務並執行下列指令。
gcloud run deploy $FRONTEND --source . --allow-unauthenticated --region $REGION
呼叫後端服務
確認您已成功部署兩項 Cloud Run 服務。
在網路瀏覽器中開啟前端服務的網址。
在文字方塊中,輸入後端服務的網址。請注意,這項要求會從前端 Cloud Run 執行個體轉送至後端 Cloud Run 服務,而不是從瀏覽器轉送。
其中會顯示「Hello World」。
4. 僅為內部輸入設定後端服務
執行下列 gcloud 指令,只允許來自虛擬私有雲網路內的流量存取您的後端服務。
gcloud run services update $BACKEND --ingress internal --region $REGION
如要確認後端服務只能接收來自虛擬私有雲的流量,請重新嘗試從前端服務呼叫後端服務。
這次您會看到「要求失敗,狀態碼為 404」
您收到這則錯誤訊息,是因為前端 Cloud Run 服務傳出要求 (即輸出) 會先傳送至網際網路,因此 Google Cloud 不知道要求的來源。
在下一節中,您將會設定前端服務來存取虛擬私有雲,這樣 Google Cloud 就會知道要求來自虛擬私有雲,並判定為內部來源。
5. 設定前端服務來存取虛擬私有雲
在本節中,您將設定前端 Cloud Run 服務,以便透過虛擬私有雲與後端服務進行通訊。
為此,您需要將虛擬私有雲直接輸出新增至前端 Cloud Run 執行個體,為服務提供可在虛擬私有雲中使用的內部 IP。接下來,您會設定輸出,讓前端服務的所有輸出連線都會傳送至虛擬私有雲。
首先,請執行下列指令,啟用直接虛擬私有雲輸出功能:
gcloud beta run services update $FRONTEND \ --network=default \ --subnet=default \ --vpc-egress=all-traffic \ --region=$REGION
現在,您可以確認前端服務有權存取虛擬私有雲:
gcloud beta run services describe $FRONTEND \ --region=$REGION
輸出內容應該會與
VPC access: Network: default Subnet: default Egress: all-traffic
現在再試一次,從前端服務呼叫後端服務。
這次會顯示「Hello World」。
注意事項:所有輸出作業均已轉送至虛擬私有雲,因此前端服務將無法連上網際網路。舉例來說,您的前端服務如果嘗試存取 https://curlmyip.org/,就會逾時。
6. 恭喜!
恭喜您完成本程式碼研究室!
建議您詳閱 Cloud Run 說明文件,以及如何為 Cloud Run 服務設定私人網路。
涵蓋內容
- 如何僅允許從虛擬私有雲傳送至 Cloud Run 服務的流量
- 如何在 Cloud Run 服務中設定輸出,以便與僅限內部輸入的 Cloud Run 服務進行通訊
7. 清除所用資源
為避免產生意外費用 (舉例來說,如果 Cloud Run 服務意外叫用次數超過免費方案的每月 Cloud Run 叫用分配數量),您可以刪除 Cloud Run 或刪除步驟 2 中建立的專案。
如要刪除 Cloud Run 服務,請前往 Cloud Run Cloud 控制台 (https://console.cloud.google.com/run),然後刪除 $FRONTEND 和 $BACKEND 服務。
如果選擇刪除整個專案,您可以前往 https://console.cloud.google.com/cloud-resource-manager,選取您在步驟 2 建立的專案,然後選擇「刪除」。如果刪除專案,您必須變更 Cloud SDK 中的專案。您可以執行 gcloud projects list
來查看可用專案的清單。