如何在 Cloud Run 上部署生成式界面网站

1. 简介

概览

在本实验中,您将构建并部署一个网站,其内容由 Google 的 Gemini 大语言模型即时生成。该网站将是一个简单的“自选冒险”式主题探索导航器,每次点击都会根据您的选择生成一个包含新链接的新页面。您将使用 Node.js 和 Fastify 构建此应用,使用 Vertex AI SDK 调用 Gemini,将其作为可用于生产用途的安全服务部署到 Cloud Run,并使用 Identity-Aware Proxy (IAP) 对其进行保护。

您将执行的操作

  • 创建使用 Vertex AI 的 Node.js Fastify 应用。
  • 无需 Dockerfile 即可从源代码将应用部署到 Cloud Run。
  • 使用 Identity-Aware Proxy (IAP) 保护 Cloud Run 端点。

学习内容

  • 如何使用 Vertex AI SDK for Node.js 生成内容。
  • 如何将 Node.js 应用部署到 Cloud Run。
  • 如何使用 IAP 保护 Cloud Run 应用。

2. 项目设置

  1. 如果您还没有 Google 账号,则必须先创建一个 Google 账号
    • 请改用个人账号,而非工作账号或学校账号。工作账号和学校账号可能存在限制,导致您无法启用本实验所需的 API。
  2. 登录 Google Cloud 控制台
  3. 在 Cloud 控制台中启用结算功能
    • 完成本实验的 Cloud 资源费用应低于 1 美元。
    • 您可以按照本实验末尾的步骤删除资源,以避免产生更多费用。
    • 新用户符合参与 $300 USD 免费试用计划的条件。
  4. 创建新项目或选择重复使用现有项目。
    • 如果您看到有关项目配额的错误,请重复使用现有项目或删除现有项目以创建新项目。

3. 打开 Cloud Shell Editor

  1. 点击此链接可直接前往 Cloud Shell 编辑器
  2. 如果系统在今天任何时间提示您进行授权,请点击授权继续。点击以授权 Cloud Shell
  3. 如果终端未显示在屏幕底部,请打开它:
    • 点击查看
    • 点击终端在 Cloud Shell 编辑器中打开新终端
  4. 在终端中,使用以下命令设置项目:
    • 格式:
      gcloud config set project [PROJECT_ID]
      
    • 示例:
      gcloud config set project lab-project-id-example
      
    • 如果您不记得项目 ID,请执行以下操作:
      • 您可以使用以下命令列出所有项目 ID:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      在 Cloud Shell 编辑器终端中设置项目 ID
  5. 您应会看到以下消息:
    Updated property [core/project].
    
    如果您看到 WARNING 并被问到 Do you want to continue (Y/n)?,则很可能是您输入的项目 ID 有误。按 n,按 Enter,然后尝试再次运行 gcloud config set project 命令。
  1. 设置 GOOGLE_CLOUD_PROJECT 环境变量
    export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
    

4. 启用 API

在终端中,启用以下 API:

gcloud services enable \
  run.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iap.googleapis.com

如果系统提示您进行授权,请点击授权以继续。点击以授权 Cloud Shell

此命令可能需要几分钟时间才能完成,但最终应会生成类似如下所示的成功消息:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

5. 准备 Node.js 项目

  1. 创建一个名为 gen-ui-on-cloudrun 的文件夹,用于存储要部署的源代码:
    mkdir gen-ui-on-cloudrun && cd gen-ui-on-cloudrun
    
  2. 初始化 Node.js 项目:
    npm init -y
    
  3. 运行以下命令,将项目配置为使用 ES 模块并定义启动脚本:
    npm pkg set type="module"
    
  4. 为 Web 服务器安装 fastify,为 Vertex AI SDK 安装 @google/genai
    npm install fastify @google/genai
    

6. 创建应用代码

  1. 创建并打开一个新的 index.ts 文件,用于存放应用源代码:
    cloudshell edit ~/gen-ui-on-cloudrun/index.ts
    
    cloudshell edit 命令会在终端上方的编辑器中打开 index.ts 文件。
  2. index.ts 文件中添加以下生成式界面服务器源代码:
    import fastifyLib from 'fastify';
    import { GoogleGenAI } from '@google/genai';
    
    const fastify = fastifyLib({ logger: true });
    
    const ai = new GoogleGenAI({
        vertexai: true,
        project: process.env.GOOGLE_CLOUD_PROJECT,
        location: process.env.GOOGLE_CLOUD_LOCATION || 'europe-west1',
    });
    
    const SYSTEM_INSTRUCTION = `The user should have submitted an html page and the id of the element just clicked.
    Given the next page description, create a new webpage with a link back to "Start Over" (the / route), a brief overview of the topic, and a list of clickable link elements related to the page.
    When an element is clicked, the webpage should link to the base route / with the nextPageDescription as a query string parameter.
    All information needed to generate the next page should be included in the nextPageDescription without additional context.
    Each nextPageDescription should be less than 1500 characters.
    
    Example:
    If the current HTML page is for a small pet store, it might include a link to an "About" page.
    The href for the about page link should be /?nextPageDescription=about%20page%20for%20small%20pet%20store%20website
    
    All responses should be valid HTML without markdown backticks.`;
    
    interface QueryParams {
        nextPageDescription?: string;
    }
    
    fastify.get<{ Querystring: QueryParams }>('/', async (request, reply) => {
        const {
            nextPageDescription = 'A web page with interesting fun facts where I can select a fact to learn more about that topic.'
        } = request.query;
    
        try {
            const response = await ai.models.generateContent({
                model: 'gemini-2.5-flash',
                contents: nextPageDescription,
                config: {
                    systemInstruction: SYSTEM_INSTRUCTION,
                    temperature: 0.9,
                }
            });
    
            reply.type('text/html; charset=utf-8').send(response.text);
        } catch (error: any) {
            request.log.error(error);
            reply.status(500).send('An error occurred calling the AI.');
        }
    });
    
    const start = async () => {
        try {
            await fastify.listen({ port: Number(process.env.PORT) || 8080, host: '0.0.0.0' });
        } catch (err) {
            fastify.log.error(err);
            process.exit(1);
        }
    };
    
    start();
    

此代码设置了一个 Web 服务器,用于监听根路径 (/) 上的 HTTP GET 请求。当收到请求时,它会使用 nextPageDescription 查询参数(或默认值)作为通过 Vertex AI 向 Gemini 2.5 Flash 模型发出的提示。该模型由 SYSTEM_INSTRUCTION 指示返回包含链接的 HTML 页面,其中每个链接都包含用于生成后续页面的 nextPageDescription

7. 创建服务账号

您需要为 Cloud Run 服务创建一个服务账号,以便向 Vertex AI API 进行身份验证。

  1. 创建名为 gen-navigator-sa 的服务账号:
    gcloud iam service-accounts create gen-navigator-sa --display-name="Generative Navigator Service Account"
    
  2. 向服务账号授予使用 Vertex AI 的权限:
    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member="serviceAccount:gen-navigator-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role="roles/aiplatform.user"
    

8. 部署到 Cloud Run

现在,您可以直接从源代码将应用部署到 Cloud Run,而无需 Dockerfile。

  1. 运行 gcloud 命令以部署应用:
    cd ~/gen-ui-on-cloudrun
    gcloud beta run deploy generative-web-navigator \
        --source . \
        --no-build \
        --base-image=nodejs24 \
        --command="node" \
        --args="index.ts" \
        --region=europe-west1 \
        --no-allow-unauthenticated \
        --iap \
        --service-account="gen-navigator-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --set-env-vars GOOGLE_CLOUD_PROJECT="$GOOGLE_CLOUD_PROJECT",GOOGLE_CLOUD_LOCATION="europe-west1"
    
    我们在此处使用了一些重要标志:
    • --source . --no-build --base-image=nodejs24:此标志会指示 Cloud Run 部署当前目录中的源代码,跳过构建阶段,并使用预构建的 Node.js 24 基本映像运行应用。
    • --no-allow-unauthenticated:这样可确保只有经过身份验证的用户才能访问该服务。
    • --iap:此权限可让 Identity-Aware Proxy (IAP) 管理对应用的访问权限。借助 IAP,您可以根据用户身份和情境来控制访问权限,而不仅仅是根据 IP 地址。
  2. 几分钟后,您会看到类似以下内容的消息:
    Service [generative-web-navigator] revision [generative-web-navigator-12345-abc] has been deployed and is serving 100 percent of traffic.
    

您已部署应用,但仍需配置 IAP 以允许访问。

9. 配置 IAP 访问权限

在 Cloud Run 上启用 IAP 后,IAP 会拦截所有请求,并要求用户先通过身份验证并获得授权,然后才能访问您的服务。为此,您需要授予两项权限:

  • 允许 IAP 服务本身调用您的 Cloud Run 服务。
  • 允许您(或其他用户/群组)通过 IAP 访问应用。
  1. 获取您的项目编号,这是标识 IAP 服务代理所需的:
    export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
    
  2. 向 IAP 服务代理授予 Cloud Run 服务的 roles/run.invoker 角色。这样,IAP 就可以在对用户进行身份验证和授权后调用您的服务。
    gcloud run services add-iam-policy-binding generative-web-navigator \
        --region=europe-west1 \
        --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-iap.iam.gserviceaccount.com" \
        --role="roles/run.invoker"
    
  3. 为您的用户账号授予 roles/iap.httpsResourceAccessor 角色。这样一来,您就可以访问受 IAP 保护的 HTTPS 资源。
    gcloud beta iap web add-iam-policy-binding \
        --resource-type=cloud-run \
        --region=europe-west1 \
        --service=generative-web-navigator \
        --member="user:$(gcloud config get-value account)" \
        --role="roles/iap.httpsResourceAccessor"
    

10. 测试应用

  1. 获取已部署服务的网址:
    gcloud run services describe generative-web-navigator --format='value(status.url)' --region=europe-west1
    
  2. 复制该网址,然后在网络浏览器中打开。由于该服务受 IAP 保护,如果您尚未登录,系统会提示您使用 Google 账号登录。通过身份验证后,您应该会看到第一个自动生成的页面。
  3. 点击任意链接即可前往新页面,该页面将由 AI 根据您点击的链接生成!

您已完成!您已成功将生成式界面网站部署到 Cloud Run,并使用 IAP 对其进行了保护。

11. 总结

恭喜!您已使用 Cloud Run、Vertex AI 和 IAP 成功部署并保护了生成式界面网站。

(可选)清理

如果您想清理已创建的内容,可以删除云项目,以免产生额外费用。

虽然 Cloud Run 不会对未使用的服务收费,但您仍然可能需要为您创建的 build 工件支付存储费用。删除 Cloud 项目后,系统即会停止对该项目中使用的所有资源计费。

如果您愿意,可以删除项目:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

您可能还需要从 Cloud Shell 磁盘中删除不必要的资源。您可以:

  1. 删除 Codelab 项目目录:
    rm -rf ~/gen-ui-on-cloudrun
    
  2. 警告!接下来执行的操作无法撤消!如果您想删除 Cloud Shell 中的所有内容以释放空间,可以删除整个主目录。请务必将要保留的所有内容保存到其他位置。
    sudo rm -rf $HOME