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 來查看可用專案的清單。