如何設定 Cloud Run 服務以使用直接虛擬私有雲輸出功能存取內部 Cloud Run 服務

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 Shell

  1. 在 Cloud 控制台中,按一下「啟用 Cloud Shell」圖示 d1264ca30785e435.png

cb81e7c8e34bc8d.png

如果您是第一次啟動 Cloud Shell,系統會顯示中繼畫面,說明這項服務的內容。如果系統顯示中繼畫面,請按一下「繼續」

d95252b003979716.png

佈建並連線至 Cloud Shell 只需幾分鐘的時間。

7833d5e1c5d18f54.png

這個虛擬機器已載入所有必要的開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。在本程式碼研究室中,您的大部分作業都可透過瀏覽器完成。

連線至 Cloud Shell 後,您應會發現自己通過驗證,且專案已設為您的專案 ID。

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