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

1. 簡介

總覽

為確保服務和應用程式的網路流量安全,許多機構會在 Google Cloud 上使用虛擬私有雲 (VPC) 網路,並透過周邊控制項防止資料竊取。虛擬私有雲網路是實體網路的虛擬版本,建構於 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」訊息。

課程內容

  • 如何只允許虛擬私有雲流量傳送至 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

如要確認後端服務只能接收來自虛擬私有雲的流量,請再次嘗試從前端服務呼叫後端服務。

這次您會看到「Request failed with status code 404」(要求失敗,狀態碼為 404)

發生這個錯誤的原因是前端 Cloud Run 服務的傳出要求 (即輸出) 會先傳送至網際網路,因此 Google Cloud 不知道要求來源。

在下一節中,您將設定前端服務來存取虛擬私有雲,讓 Google Cloud 知道要求來自虛擬私有雲,並將其視為內部來源。

5. 設定前端服務以存取虛擬私有雲

在本節中,您將設定前端 Cloud Run 服務,透過 VPC 與後端服務通訊。

如要這麼做,您需要在前端 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 服務,請前往 https://console.cloud.google.com/run 的 Cloud Run Cloud 控制台,然後刪除 $FRONTEND 和 $BACKEND 服務。

如要刪除整個專案,請前往 https://console.cloud.google.com/cloud-resource-manager,選取您在步驟 2 中建立的專案,然後選擇「刪除」。刪除專案後,您必須在 Cloud SDK 中變更專案。如要查看所有可用專案的清單,請執行 gcloud projects list