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 控制台。
- 您之前已部署了 Cloud Run 服务。例如,您可以按照快速入门:部署 Web 服务开始操作。
激活 Cloud Shell
- 在 Cloud Console 中,点击激活 Cloud Shell
。
如果这是您第一次启动 Cloud Shell,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续。
预配和连接到 Cloud Shell 只需花几分钟时间。
这个虚拟机装有所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。
在连接到 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 服务
设置环境变量
您可以设置要在整个 Codelab 中使用的环境变量。
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 命令,以仅允许来自您的 VPC 网络的流量访问您的后端服务。
gcloud run services update $BACKEND --ingress internal --region $REGION
如需确认您的后端服务只能接收来自 VPC 的流量,请再次尝试从前端服务调用后端服务。
这次您会看到“请求失败,状态代码 404”
您收到此错误是因为前端 Cloud Run 服务出站请求(即出站流量)首先传输到互联网,因此 Google Cloud 不知道请求的来源。
在下一部分中,您将配置前端服务以访问 VPC,以便 Google Cloud 知道请求来自被识别为内部来源的 VPC。
5. 配置前端服务以访问 VPC
在本部分中,您将配置前端 Cloud Run 服务,以通过 VPC 与后端服务通信。
为此,您需要将直接 VPC 出站流量添加到前端 Cloud Run 实例,为您的服务提供可在 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 服务,请前往 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
来查看所有可用项目的列表。