如何配置 Cloud Run 服务以使用直接 VPC 出站流量访问内部 Cloud Run 服务

1. 简介

概览

为了保护服务和应用的网络流量,许多组织将 Google Cloud 上的虚拟私有云 (VPC) 网络与边界控制结合使用来防止数据渗漏。VPC 网络是 Google 生产网络内部实现的物理网络的虚拟版本。VPC 网络为 Compute Engine 虚拟机 (VM) 实例提供连接性,为内部应用负载平衡器提供原生内部直通式网络负载平衡器和代理系统,使用 Cloud VPN 隧道和 Cloud Interconnect 的 VLAN 连接来与本地网络建立连接,并将来自 Google Cloud 外部负载平衡器的数据流量分配给后端。

与虚拟机不同,默认情况下,Cloud Run 服务不与任何特定 VPC 网络关联。此 Codelab 演示了如何更改入站流量(入站连接)设置,以便只有来自 VPC 的流量才能访问 Cloud Run 服务(例如后端服务)。此外,此 Codelab 还向您展示了如何让第二个服务(例如前端服务)通过 VPC 访问后端 Cloud Run 服务。

在此示例中,后端 Cloud Run 服务返回 hello world。前端 Cloud Run 服务在界面中提供一个输入字段来收集 网址。然后,前端服务向该网址(例如后端服务)发出 GET 请求,因此这会成为服务到服务的请求(而不是浏览器到服务的请求)。当前端服务可以成功访问后端时,浏览器中会显示消息 hello world。

学习内容

  • 如何仅允许来自 VPC 的流量访问 Cloud Run 服务
  • 如何在 Cloud Run 服务上配置出站流量,以与仅允许内部入站流量的 Cloud Run 服务通信

2. 设置和要求

前提条件

激活 Cloud Shell

  1. 在 Cloud 控制台中,点击 激活 Cloud Shell d1264ca30785e435.png

cb81e7c8e34bc8d.png

如果您是首次启动 Cloud Shell,系统会显示一个中间屏幕,其中介绍了 Cloud Shell 的用途。如果您看到了中间屏幕,请点击继续

d95252b003979716.png

预配和连接到 Cloud Shell 只需花几分钟时间。

7833d5e1c5d18f54.png

此虚拟机加载了所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。 只需使用一个浏览器即可完成本 Codelab 中的大部分工作。

连接到 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 服务

设置环境变量

您可以设置将在本 Codelab 中使用的环境变量。

REGION=<YOUR_REGION, e.g. us-central1>
FRONTEND=frontend
BACKEND=backend

创建后端 Cloud Run 服务

首先,为源代码创建一个目录,然后进入该目录。

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 服务。

在 Web 浏览器中打开前端服务的网址。

在文本框中,输入后端服务的网址。请注意,此请求是从前端 Cloud Run 实例路由到后端 Cloud Run 服务,而不是从浏览器路由。

您将看到“hello world”。

4. 将后端服务设置为仅允许内部入站流量

运行以下 gcloud 命令,以仅允许来自 VPC 网络的流量访问后端服务。

gcloud run services update $BACKEND --ingress internal --region $REGION

如需确认后端服务只能接收来自 VPC 的流量,请再次尝试从前端服务调用后端服务。

这次您将看到“Request failed with status code 404”

您之所以收到此错误,是因为前端 Cloud Run 服务的出站请求(即出站流量)首先会发送到互联网,因此 Google Cloud 不知道请求的来源。

在下一部分中,您将配置前端服务以访问 VPC,以便 Google Cloud 知道请求来自 VPC,该 VPC 被识别为内部来源。

5. 配置前端服务以访问 VPC

在本部分中,您将配置前端 Cloud Run 服务,以通过 VPC 与后端服务通信。

为此,您需要向前端 Cloud Run 实例添加直接 VPC 出站流量,以便为您的服务提供在 VPC 中使用的内部 IP。然后,您将配置出站流量,以便前端服务的所有出站连接都将发送到 VPC。

首先,运行以下命令以启用直接 VPC 出站流量:

gcloud beta run services update $FRONTEND \
--network=default \
--subnet=default \
--vpc-egress=all-traffic \
--region=$REGION

现在,您可以确认前端服务有权访问 VPC:

gcloud beta run services describe $FRONTEND \
--region=$REGION

您应该会看到类似于以下内容的输出

VPC access:
    Network:         default
    Subnet:          default
    Egress:          all-traffic

现在,再次尝试从前端服务调用后端服务。

这次您将看到“hello world”。

注意:由于所有出站流量都已路由到 VPC,因此前端服务将无法访问互联网。 例如,如果前端服务尝试访问 https://curlmyip.org/,则会超时。

6. 恭喜!

恭喜您完成此 Codelab!

我们建议您查看 Cloud Run 文档以及如何 为 Cloud Run 服务配置专用网络

所学内容

  • 如何仅允许来自 VPC 的流量访问 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 查看所有可用项目的列表。