1. 簡介
總覽
為確保服務和應用程式的網路流量安全,許多機構會在 Google Cloud 上使用虛擬私有雲 (VCP) 網路,並搭配周邊控制項,防止資料竊取。虛擬私有雲網路是實體網路的虛擬版本,建構於 Google 的正式環境網路之內,虛擬私有雲網路可為 Compute Engine 虛擬機器 (VM) 執行個體提供連線能力,並為內部應用程式負載平衡器提供原生內部直通式網路負載平衡器和 Proxy 系統,還可透過 Cloud VPN 通道和 Cloud Interconnect 的 VLAN 連結連線至地端部署網路,以及將 Google Cloud 外部負載平衡器的流量分配至後端。
與 VM 不同,Cloud Run 服務預設不會與任何特定虛擬私有雲網路建立關聯。本程式碼實驗室會示範如何變更 Ingress (連入連線) 設定,只允許來自虛擬私有雲的流量存取 Cloud Run 服務 (例如後端服務)。此外,本程式碼實驗室也會說明如何讓第二項服務 (例如前端服務) 透過 VPC 存取後端 Cloud Run 服務,同時繼續存取公開網際網路。
在本範例中,後端 Cloud Run 服務會傳回「hello world」。前端 Cloud Run 服務會在 UI 中提供輸入欄位,用於收集網址。接著,前端服務會對該網址發出 GET 要求 (例如後端服務),因此這項要求是服務對服務要求 (而非瀏覽器對服務要求)。如果前端服務可以順利連上後端,瀏覽器就會顯示「hello world」訊息。接著,您會瞭解如何呼叫 https://curlmyip.org,擷取前端服務的 IP 位址。
課程內容
- 如何只允許虛擬私有雲流量傳送至 Cloud Run 服務
- 如何設定 Cloud Run 服務 (例如前端) 的輸出流量,與僅限內部輸入流量的 Cloud Run 服務 (例如後端) 通訊,同時維持前端服務的公開網際網路存取權。
2. 設定和需求
必要條件
- 您已登入 Cloud Console。
- 您先前已部署第 2 代函式。舉例來說,您可以按照部署 Cloud Functions 第 2 代快速入門導覽課程操作。
啟用 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 服務
設定環境變數
您可以設定本程式碼實驗室全程都會使用的環境變數。
PROJECT_ID=<YOUR_PROJECT_ID> REGION=<YOUR_REGION, e.g. us-central1> FRONTEND=frontend-with-internet BACKEND=backend SUBNET_NAME=default
建立後端 Cloud Run 服務
首先,請建立原始碼的目錄,然後 cd 到該目錄。
mkdir -p egress-private-codelab/frontend-w-internet egress-private-codelab/backend && cd egress-private-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-w-internet
接著,請建立 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",
"htmx.org": "^1.9.10"
}
}
接著,請使用下列內容建立 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 Request Tester service on the Internet</h1>
<form hx-trigger="submit" hx-post="/callService" hx-target="#zen">
<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="zen" 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 服務。
在網路瀏覽器中開啟前端服務的網址,例如 https://frontend-your-hash-uc.a.run.app/
在文字方塊中輸入後端服務的網址。請注意,這項要求是從前端 Cloud Run 執行個體轉送至後端 Cloud Run 服務,而非從瀏覽器轉送。
您會看到「hello world」
4. 僅限內部輸入設定後端服務
您可以執行下列 gcloud 指令,將 Cloud Run 服務併入私人網路。
gcloud run services update $BACKEND --ingress internal --region $REGION
如果您嘗試從前端服務呼叫後端服務,會收到 404 錯誤。前端 Cloud Run 服務的外送連線 (或輸出) 會先連上網際網路,因此 Google Cloud 不知道要求來源。
5. 設定前端服務以存取虛擬私有雲
在本節中,您將設定前端 Cloud Run 服務,透過 VPC 與後端服務通訊。
如要這麼做,您必須在前端 Cloud Run 服務中新增直接虛擬私有雲輸出流量,確保服務可以連線至虛擬私有雲網路的內部 IP 位址。接著,您將設定輸出,確保只有傳至私人 IP 的要求會轉送至虛擬私有雲。這樣一來,前端仍可連上公用網際網路。詳情請參閱說明文件,瞭解如何接收來自其他 Cloud Run 服務的要求。
設定直接虛擬私有雲輸出
首先,請執行下列指令,在前端服務上使用直接虛擬私有雲輸出流量:
gcloud beta run services update $FRONTEND \ --network=$SUBNET_NAME \ --subnet=$SUBNET_NAME \ --vpc-egress=private-ranges-only \ --region=$REGION
現在可以確認前端服務是否能存取虛擬私有雲:
gcloud beta run services describe $FRONTEND \ --region=$REGION
畫面會顯示類似以下的輸出:
VPC access:
Network: default
Subnet: default
Egress: private-ranges-only
啟用 Private Google Access
接著,請執行下列指令,在子網路啟用 Private Google Access:
gcloud compute networks subnets update $SUBNET_NAME \ --region=$REGION \ --enable-private-ip-google-access
如要確認 Private Google Access 是否已啟用,請執行下列指令:
gcloud compute networks subnets describe $SUBNET_NAME \ --region=$REGION \ --format="get(privateIpGoogleAccess)"
為 run.app 網址建立 Cloud DNS 區域
最後,為 run.app 網址建立 Cloud DNS 區域,讓 Google Cloud 將這些網址視為內部 IP 位址。
在上一個步驟中,您已將直接虛擬私有雲輸出流量設定為僅限私人範圍。也就是說,如果目的地是內部 IP,前端服務的輸出連線只會連往虛擬私有雲網路。不過,後端服務會使用解析為公開 IP 的 run.app 網址。
在這個步驟中,您將為 run.app 網址建立 Cloud DNS 區域,以便解析為 private.googleapis.com IP 位址範圍,這些位址會視為內部 IP 位址。現在,對這些範圍的任何要求都會透過虛擬私有雲網路傳輸。
如要執行這項操作,請參閱:https://cloud.google.com/run/docs/securing/private-networking#from-other-services
# do not include the https:// in your DNS Name # for example: backend-<hash>-uc.a.run.app DNS_NAME=<your backend service URL without the https://> gcloud dns --project=$PROJECT_ID managed-zones create codelab-backend-service \ --description="" \ --dns-name="a.run.app." \ --visibility="private" \ --networks=$SUBNET_NAME gcloud dns --project=$PROJECT_ID record-sets create $DNS_NAME. \ --zone="codelab-backend-service" \ --type="A" \ --ttl="60" \ --rrdatas="199.36.153.8,199.36.153.9,199.36.153.10,199.36.153.11"
現在嘗試連線至網站的後端服務時,會看到傳回「hello world」。
當您嘗試使用 https://curlmyip.org/ 連上網際網路時,會看到自己的 IP 位址。
6. 疑難排解
如果設定不正確,可能會收到下列錯誤訊息。
- 如果收到錯誤訊息
getaddrinfo ENOTFOUND backend-your-hash-uc.a.run.app,請確認您未在 DNS A 記錄中加入「https://」 - 如果在設定可用區後嘗試存取後端時收到 404 錯誤,您可以等待全域 run.app 記錄的快取過期 (例如 6 小時),也可以執行下列指令建立新修訂版本 (因此清除快取):
gcloud beta run services update $FRONTEND --network=$SUBNET_NAME --subnet=$SUBNET_NAME --vpc-egress=private-ranges-only --region=$REGION
7. 恭喜!
恭喜您完成本程式碼研究室!
建議您參閱 Cloud Run 的私有網路說明文件。
涵蓋內容
- 如何只允許虛擬私有雲流量傳送至 Cloud Run 服務
- 如何設定 Cloud Run 服務 (例如前端) 的輸出流量,與僅限內部輸入流量的 Cloud Run 服務 (例如後端) 通訊,同時維持前端服務的公開網際網路存取權。
8. 清理
為避免產生意外費用 (例如,如果這個 Cloud Run 服務的叫用次數不慎超過免費層級的每月 Cloud Run 叫用次數配額),您可以刪除 Cloud Run 服務,或刪除在步驟 2 中建立的專案。
如要刪除 Cloud Run 服務,請前往 Cloud Run Cloud 控制台 (https://console.cloud.google.com/functions/),然後刪除您在本程式碼研究室中建立的 $FRONTEND 和 $BACKEND 服務。
如要刪除整個專案,請前往 https://console.cloud.google.com/cloud-resource-manager,選取您在步驟 2 中建立的專案,然後選擇「刪除」。刪除專案後,您必須在 Cloud SDK 中變更專案。如要查看所有可用專案的清單,請執行 gcloud projects list。