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」訊息。接著,您將瞭解如何呼叫 https://curlmyip.org,以擷取前端服務的 IP 位址。
課程內容
- 如何僅允許從虛擬私有雲傳送至 Cloud Run 服務的流量
- 如何在 Cloud Run 服務 (例如前端) 上設定輸出功能,以與僅限內部輸入的 Cloud Run 服務 (例如後端) 進行通訊,同時維持前端服務的公開網際網路存取權。
2. 設定和需求
必要條件
- 您已登入 Cloud 控制台。
- 您先前已部署第 2 代函式。舉例來說,您可以按照部署 Cloud 函式 2 第 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 服務,以便透過虛擬私有雲與後端服務進行通訊。
為此,您必須將虛擬私有雲直接輸出新增至前端 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
啟用私人 Google 存取權
接著執行下列指令,在子網路上啟用私人 Google 存取權:
gcloud compute networks subnets update $SUBNET_NAME \ --region=$REGION \ --enable-private-ip-google-access
您可以執行下列指令,確認私人 Google 存取權是否已啟用:
gcloud compute networks subnets describe $SUBNET_NAME \ --region=$REGION \ --format="get(privateIpGoogleAccess)"
為 run.app 網址建立 Cloud DNS 區域
最後,為 run.app 網址建立 Cloud DNS 區域,讓 Google Cloud 將這些網址視為內部 IP 位址。
在前一個步驟中,您將虛擬私有雲輸出功能設為僅限 private-ranges-only 時,這代表前端服務的傳出連線只有在目的地為內部 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
請確認您並未新增「https://」傳送至 DNS A 記錄 - 設定區域後,如果您在嘗試存取後端時收到 404 錯誤,可以等待 Global 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
來查看可用專案的清單。