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

1. 简介

概览

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

您将执行的操作

  • 创建一个使用 Vertex AI 的 Node.js Fastify 应用。
  • 直接使用源代码将应用部署到 Cloud Run,无需 Dockerfile。
  • 使用 Identity-Aware Proxy (IAP) 保护 Cloud Run 端点的安全。

学习内容

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

2. 项目设置

  1. 如果您还没有 Google 账号,则必须创建一个 Google 账号
    • 请使用个人账号,而不是组织或学校账号。组织和学校账号可能会受到限制,导致您无法启用本实验所需的 API。
  2. 登录 Google Cloud 控制台
  3. 在 Cloud 控制台中启用结算功能
    • 完成本实验的 Cloud 资源费用应低于 1 美元。
    • 您可以按照本实验末尾的步骤删除资源,以避免产生进一步的费用。
    • 新用户有资格享受 300 美元的免费试用额度
  4. 创建新项目,或选择重复使用现有项目。
    • 如果您看到有关项目配额的错误,请重复使用现有项目或删除现有项目以创建新项目。

3. 打开 Cloud Shell Editor

  1. 点击此链接可直接前往 Cloud Shell Editor
  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-west4',
    });
    
    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 查询参数(或默认值)作为 Gemini 2.5 Flash 模型的提示,并通过 Vertex AI 将其发送给模型。模型会按照 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-west4 \
        --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-west4"
    
    我们在这里使用了几个重要的标志:
    • --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-west4 \
        --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-west4 \
        --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-west4
    
  2. 复制该网址,然后在 Web 浏览器中打开它。由于该服务受到 IAP 的保护,因此,如果您尚未登录,系统会提示您使用 Google 账号登录。进行身份验证后,您应该会看到第一个自动生成的页面。
  3. 点击任意链接即可前往新页面,该页面将由 AI 根据您点击的链接生成!

您已完成所有操作!您已成功将生成式界面网站部署到 Cloud Run,并使用 IAP 保护其安全。

11. 总结

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

(可选)清理

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

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

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

gcloud projects delete $GOOGLE_CLOUD_PROJECT

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

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