代理网关从代理运行时到 VPC 网络的出站流量

1. 简介

此 Codelab 探讨了 AI 代理通过专用 VPC 网络访问远程端点的代理网关出站流量治理。以出站流量(代理到任意位置)模式运行的代理网关支持使用 Private Service Connect (PSC) 接口与 VPC 网络建立专用连接。在此场景中,Agent Runtime 使用 Agent Gateway 通过 VPC 网络中 Google API 的 Private Service Connect (PSC) 端点访问 Cloud Run 上托管的远程可流式传输的 HTTP MCP 服务器

从独立聊天机器人到具有多智能体工作流的自主系统,AI 应用可以动态调用外部工具。为代理提供受控的数据库查询、网页内容提取和操作执行权限,对于确保代理的安全性和高效性至关重要。不过,在企业环境中,确保代理工具执行的安全性是一项挑战。如果代理具有直接网络访问权限,提示注入攻击或模型幻觉可能会导致代理泄露敏感数据或运行破坏性命令。

为了大规模管理自主智能体,Agent Gateway 提供了一个集中式零信任强制执行点,直接集成到 Agent Platform 架构中。Agent Gateway 不依赖于每个应用或代理代码特有的自定义实现,而是在平台级强制执行网络路由、代理治理和运行时安全性。

当代理网关以出站流量(代理到任何位置)模式运行时,它会代理配置为使用该网关的代理的所有出站请求。每个代理工作负载请求都使用唯一的代理身份进行身份验证,并使用 Identity-Aware Proxy (IAP) 政策进行授权。MCP 工具调用会经过动态解码和检查,以确保政策得到执行。安全管理员可以集中强制执行精细的权限,以确保代理只能调用经过批准的端点和方法。

构建内容

  • 出站(代理到任意目的地)模式下的代理网关
  • Identity-Aware Proxy (IAP) 授权扩展程序
  • 具有代理身份的 Agent Runtime ADK 代理
  • Cloud Run 可流式传输的 HTTP MCP 服务器
  • 包含 Google API 和自定义 MCP 端点的 Agent Registry
  • 使用 IAM 访问权限控制的授权政策
  • VPC 网络资源、Cloud DNS 区域和 Google API 的 PSC 端点
  • 用于代理网关出站流量的 PSC 网络连接
  • Cloud Next Generation Firewall (NGFW) 防火墙政策规则

figure1

图 1. Codelab 架构

学习内容

  • 如何在结构化配置序列中部署代理网关
  • 如何配置具有专用 VPC 网络连接的代理网关
  • 如何将授权政策从试运行模式过渡到强制执行模式
  • 如何在代理注册表中注册 Google API 端点和 MCP 服务器
  • 如何审核代理网关日志以验证出站流量治理
  • 如何使用 Cloud NGFW 日志验证代理网关出站流量

所需条件

  • 启用了结算功能的 Google Cloud 项目
  • 用于预配网络服务、BigQuery 数据集和 Agent Platform 资源的 IAM 权限
  • 安装了 Google Cloud CLI(gcloudbq 组件)的 POSIX 兼容 shell(bashzsh
  • 命令行工具:gitcurljq (JSON 处理器)、Python 3 和 uv (Python 软件包管理器)

2. 概念

部署序列

此 Codelab 遵循结构化的配置顺序,以便代理在初始化时能够访问必要的资源,并且可以在应用强制执行的访问权限政策之前确认连接。

此 Codelab 使用以下部署顺序:

  1. DRY_RUN 模式启动:确保代理请求成功到达网关并顺利通过(仅限审核模式)。
  2. 注册所有服务:在代理注册表中注册所有代理组件,并确认代理工具访问权限。使用 Cloud Logging 和代理网关可观测性信息中心查看对代理注册表的查询。
  3. 创建授权政策:创建并应用授权政策,以允许从代理到 MCP 服务器和端点的出站流量。
  4. 切换到 ENFORCED 模式:更新授权扩展程序政策,以强制执行政策并阻止未经授权的出站流量。

figure2

图 2. 部署序列

出站流量路由拓扑

Agent Gateway 出站 (agent-to-anywhere) 充当智能体工作负载的集中式零信任出站代理。当代理向外部工具或 API 发出请求时,代理网关会拦截出站请求,并根据治理政策对其进行评估,然后再将其路由到目的地。在 Google Cloud Agent Platform 架构中,Agent Gateway 提供数据平面连接,以实现以下功能:

  • 专用网络 (VPC):出站流量的目标是内部企业 API、微服务、托管在专用 VPC 中的数据库,以及可通过混合连接访问的本地或跨云网络。
  • 外部网络(互联网):以第三方 Web 服务、SaaS API 或公共端点为目标的出站流量。
  • AI 服务和 Agent Platform:出站流量,目标是受管理的 Google Cloud API、基础模型、Google MCP 端点(例如 BigQuery)和平台治理服务(Agent Registry 和 IAP 政策)。

figure3

图 3. Agent Gateway 出站流量路由拓扑

此 Codelab 的重点是 Agent Runtime 中自定义代理的出站流量,该流量以部署在 VPC 网络中的 Google API PSC 端点为目标。此架构通过使用 PSC 网络附加项来实现代理网关出站流量,从而实现对 Cloud Run 上托管的 MCP 服务器的私密访问。

VPC 连接

代理网关出站流量(从代理到任何位置)能够通过在部署 YAML 配置文件中指定 networkConfig: egress: networkAttachment: [URI]连接到 VPC 网络。此设置用于在代理网关上配置路由规则,以通过 PSC 网络连接将解析为专用 IP 地址的流量发送到 VPC 网络。

figure4

图 4. 代理网关 VPC 连接

代理网关能够使用 Cloud DNS 对等互连来解析指定目标项目和 VPC 网络的专用 DNS 主机名。dnsPeeringConfig: domains: [NAME] 配置中列出的任何专用区域都将使用目标项目中的 Cloud DNS 记录。任何其他 DNS 查询都将使用默认的 VPC Cloud DNS 公共解析器。

概念部分到此结束,接下来进入设置部分。

3. 设置

所需 IAM 角色

您必须拥有以下角色,才能在此 Codelab 中创建资源:

类别

所需 IAM 角色 (ID)

说明

API 管理

roles/serviceusage.serviceUsageAdmin

启用 Google Cloud API 服务

网络和网关

roles/networkservices.admin

配置代理网关

Service Extensions

roles/serviceextensions.admin

配置路由扩展程序

网络安全

roles/networksecurity.admin

部署授权政策

Agent Registry

roles/agentregistry.admin

目录允许的主机

Vertex AI 和代理

roles/aiplatform.admin

部署 Agent Runtime 工作负载

出站流量政策

roles/iap.admin

应用 roles/iap.egressor 项政策

Cloud Storage

roles/storage.admin

管理部署预演存储分区

日志和审核

roles/logging.viewer

检查轨迹和审核日志

Cloud Run Developer

roles/run.developer

部署 Cloud Run 服务

Artifact Registry Reader

roles/artifactregistry.reader

拉取容器映像

Compute Network Admin

roles/compute.networkAdmin

管理 VPC 网络资源

DNS Administrator

roles/dns.admin

管理 Cloud DNS 区域和记录

或者,您也可以使用广泛的基本角色(例如 roles/admin)或旧版角色 roles/owner

访问您的项目

本 Codelab 使用单个 Google Cloud 项目。配置步骤使用 gcloud CLI 和 Linux shell 命令。

首先,访问 Google Cloud 项目命令行:

设置项目 ID

gcloud config set project SET_YOUR_PROJECT_ID_HERE

验证会话

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

设置 shell 环境变量

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
export MREGION="us"
echo ${SLUG}
echo ${REGION}
echo ${MREGION}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-ata"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export RE_AGENT_NAME="agent-weather"
export RE_AGENT_ID_SET="principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJ_NO}"
export STAGING_BUCKET="agent-staging-${PROJ_NO}"
export MCP_NAME="mcp-weather"
export MCP_DISPLAY_NAME="${MCP_NAME}-${PROJ_NO}.${REGION}.run.app"
export MCP_URL="https://${MCP_DISPLAY_NAME}/mcp"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${RE_AGENT_NAME}
echo ${RE_AGENT_ID_SET}
echo ${STAGING_BUCKET}
echo ${MCP_NAME}
echo ${MCP_DISPLAY_NAME}
echo ${MCP_URL}
# create local dir for config files
mkdir -p cfg

如果您运行的是自行管理的 Google Cloud SDK 安装(即在 Cloud Shell 外部),请将组件更新到最新版本。

# update gcloud cli
gcloud components update

启用 API 服务

# enable google apis (agent platform bundle, part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iamconnectors.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (agent platform bundle, part 2)
gcloud services enable \
  securitycenter.googleapis.com \
  saasservicemgmt.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com
# enable google apis (all the rest)
gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  dns.googleapis.com

设置部分到此结束…接下来请前往网络部分。

4. 网络

VPC 网络使用自定义模式进行部署,以创建支持 PSC 网络连接常规子网,用于代理网关和 Cloud Run 直接 VPC 出站流量。

Google API 的 PSC 端点使用单个 /32全局内部 IPv4 地址进行部署,以支持对 Cloud Run 的专用内部访问。此 IP 地址不是从任何区域级子网分配的。

创建网络

创建全球 VPC 网络。

# create vpc network
gcloud compute networks create vnet-${SLUG} --subnet-mode=custom

为代理网关 PSC 网络连接和 Cloud Run 直接 VPC 出站流量创建子网。

# create subnet for agent gateway psc na
gcloud compute networks subnets create subnet-${REGION}-agw \
  --network=vnet-${SLUG} \
  --range=192.168.10.0/28 \
  --region=${REGION} \
  --enable-private-ip-google-access

# create subnet for cloud run dvpc egress
gcloud compute networks subnets create subnet-${REGION}-crun \
  --network=vnet-${SLUG} \
  --range=10.10.10.0/24 \
  --region=${REGION} \
  --enable-private-ip-google-access

创建防火墙规则

创建防火墙政策和规则,以允许所有出站流量并启用日志记录。此指标将用于监控从代理网关到 VPC 网络的出站流量。

# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG} --global
# create fw policy rule
gcloud compute network-firewall-policies rules create 1001 \
  --description="allow all out and log" \
  --firewall-policy=fw-policy-${SLUG} \
  --global-firewall-policy \
  --action=allow \
  --direction=EGRESS \
  --layer4-configs=all \
  --dest-ip-ranges=0.0.0.0/0 \
  --enable-logging
# associate fw policy to vpc network
gcloud compute network-firewall-policies associations create \
  --name=fw-policy-bind-${SLUG} \
  --firewall-policy=fw-policy-${SLUG} \
  --network=vnet-${SLUG} \
  --global-firewall-policy

创建 PSC 网络连接

创建一个 PSC 网络连接,并将其配置为自动接受新的提供方连接。

# create psc network attachment
gcloud compute network-attachments create psc-na-${REGION}-agw \
  --region=${REGION} \
  --subnets=subnet-${REGION}-agw \
  --connection-preference=ACCEPT_AUTOMATIC

验证 PSC 网络连接

# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw --region=${REGION}
# fetch psc na uri
export PSC_NA_URI=$(gcloud compute network-attachments describe psc-na-${REGION}-agw \
  --region=${REGION} \
  --format="value(selfLink.scope(v1))")
echo ${PSC_NA_URI}

创建 PSC 端点

为全球 PSC 端点创建 IP 地址预留。此处使用的地址不得来自现有子网,也不得与现有子网重叠。

# set env var for psc ep ip address
export PSC_EP_IP="240.0.0.10"
echo ${PSC_EP_IP}
# reserve internal global ipv4 address
gcloud compute addresses create ip-psc2gapis \
  --global \
  --purpose=PRIVATE_SERVICE_CONNECT \
  --addresses=${PSC_EP_IP} \
  --network=vnet-${SLUG}

使用 all-apis 软件包为 Google API 创建 PSC 端点,该软件包包含 Cloud Run (run.app)。

# create psc endpoint for google apis
gcloud compute forwarding-rules create psc2gapis \
  --global \
  --network=vnet-${SLUG} \
  --address=ip-psc2gapis \
  --target-google-apis-bundle=all-apis

验证 PSC 端点

# show psc endpoint details
gcloud compute forwarding-rules describe psc2gapis --global

创建 DNS 区域和记录

run.app 网域创建专用 Cloud DNS 托管式可用区。

# create private dns zone
gcloud dns managed-zones create priv-zone-run \
  --description="private zone for cloud run" \
  --dns-name="run.app." \
  --visibility=private \
  --networks=vnet-${SLUG}

*.run.app. 创建指向 PSC 端点 IP 地址的通配符 DNS A 记录。

# create dns record
gcloud dns record-sets create *.run.app \
  --zone=priv-zone-run \
  --type=A \
  --ttl=300 \
  --rrdatas=${PSC_EP_IP}

创建 Cloud DNS 政策以启用 DNS 查询日志记录。

# create dns policy (logging)
gcloud dns policies create dns-policy-${SLUG} \
  --description="dns logging for vnet-${SLUG}" \
  --networks=vnet-${SLUG} \
  --enable-logging

创建 Cloud NAT

创建 Cloud Router 和 Cloud NAT 以支持 Cloud Run 直接 VPC 出站流量,从而使 MCP 服务器能够通过 VPC 网络访问外部天气数据。

# create router for nat
gcloud compute routers create cr-nat-${SLUG}-${REGION} \
  --network=vnet-${SLUG} \
  --asn=16550 \
  --region=${REGION}
# create nat gateway
gcloud compute routers nats create nat-${SLUG}-${REGION} \
  --router=cr-nat-${SLUG}-${REGION} \
  --region=${REGION} \
  --auto-allocate-nat-external-ips \
  --nat-all-subnet-ip-ranges

网络部分到此结束,接下来进入网关部分。

5. 网关

在强制执行访问权限控制之前,请先部署代理网关,并在 DRY_RUN 模式下配置授权扩展程序。这样一来,出站工具调用便可成功,同时记录评估结果以供审核。

在此示例中,代理网关使用区域注册表来支持区域代理运行时资源,并为 VPC 网络 PSC 网络连接和 DNS 对等互连配置指定 networkConfig 字段。

创建网关

# create new agent gateway config file (with network settings)
cat > cfg/${AGW_NAME}-networkConfig.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: ${PSC_NA_URI}
  dnsPeeringConfig:
    domains:
      - run.app.
    targetProject: ${PROJ_ID}
    targetNetwork: projects/${PROJ_ID}/global/networks/vnet-${SLUG}
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}-networkConfig.yaml" \
  --location=${REGION}

验证网关

# show agent gateway details
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION}
# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw --region=${REGION}

检查是否存在已接受的连接端点。

connectionEndpoints:
- ipAddress: 192.168.10.2
  projectIdOrNum: '[AGW_PROJECT_NO]'
  status: ACCEPTED
  subnetwork: https://www.googleapis.com/compute/v1/projects/${PROJ_ID}/regions/${REGION}/subnetworks/subnet-${REGION}-agw

网关部分到此结束…接下来进入授权部分。

6. 授权

Identity-Aware Proxy (IAP) 的代理网关授权扩展程序是一种服务扩展程序,用于委托所有代理平台通信的授权决策。

  1. 委托流程:当代理尝试调用外部端点或 MCP 服务器时,它会将请求路由到代理网关。网关不进行本地访问权限评估,而是使用授权扩展程序向 IAP 评估服务发送回调。IAP 会根据代理注册表中目标资源的 IAM 政策评估代理(基于 SPIFFE)身份。IAP 会向网关返回 ALLOWDENY 判定结果,网关会转发流量或使用 HTTP 403 Forbidden 状态代码阻止流量。
  2. 强制执行模式:在 DRY_RUN 模式下,IAP 会评估请求并在 Cloud Logging 中记录决策,而不会阻止流量。在 ENFORCE 模式下,来自未经授权的代理或发送到未注册的目标的任何请求都会立即被阻止。
  3. 绑定层:服务扩展程序使用配置了 REQUEST_AUTHZ 配置文件的授权政策连接到 Agent Gateway。

授权扩展程序

授权扩展程序用于指定安全提供程序服务,其中包含连接设置和其他参数,用于定义该服务的运行方式以及如何与 Agent Gateway 集成。

创建授权扩展程序

# create authz extension config file (dry run mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-iap-dryrun.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap-dryrun
service: iap.googleapis.com
failOpen: true
timeout: 1s
metadata:
  iamEnforcementMode: "DRY_RUN"
  iapPolicyVersion: "V1"
EOF

导入授权扩展程序

# import authz extension file (create extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap-dryrun \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap-dryrun.yaml \
  --location=${REGION}

验证授权扩展程序

# show authz extension details
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-iap-dryrun \
  --location=${REGION}

授权政策

授权政策将安全提供程序 (IAP) 绑定到目标网关资源,并指定拦截配置文件 (REQUEST_AUTHZ)。

创建授权政策

# create authz policy config file (attach dry-run authz extension)
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap-dryrun"
EOF

绑定授权政策

# import authz policy config file (enable authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION}

验证授权政策

# show authz policy details
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION}

授权部分到此结束…接下来进入代码库部分。

7. 代码库

此 Codelab 中使用的代理、MCP 服务器和端点注册代码维护在远程 Google Cloud GitHub 代码库中。以下步骤会将代码库克隆到本地,将必要的文件复制到当前工作目录结构,然后清理临时文件。

系统会为代理运行时创建一个存储暂存桶,以便上传、构建和部署打包的代理应用代码及其依赖项制品。

提取远程制品

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_egress_vpc
# copy agent runtime and endpoint definitions to working project dir
cp -r temp_agw_cuj_arun_egress_vpc/codelabs/agw-cuj-arun-egress-vpc/agent-weather ./agent-weather
cp -r temp_agw_cuj_arun_egress_vpc/codelabs/agw-cuj-arun-egress-vpc/mcp-weather ./mcp-weather
# remove temporary directory
rm -rf temp_agw_cuj_arun_egress_vpc

创建暂存存储分区

# create storage bucket for agent deployment artifacts
gcloud storage buckets create gs://${STAGING_BUCKET} --location=${REGION}
# verify staging bucket url
gcloud storage buckets list --format="value(storage_url)"

代码库部分到此结束…接下来是注册表部分。

8. 注册表

Agent Registry 是 AI 生态系统中所有代理、MCP 服务器和端点 (API) 的集中式清单。它还是一个目录,可用于列出、搜索和发现其他已注册的工具和服务,以供 AI 应用使用。代理网关使用注册表数据模型作为治理框架来强制执行出站访问权限控制。系统会根据绑定到注册表资源的政策来检查主账号调用代理的 IAM 角色授予情况。

为了引导环境并使用各种 Google API 和服务支持代理,请创建一个捆绑了本实验所需的核心 API 接口的注册表端点服务。这样一来,您就可以更轻松地管理大多数代理会使用的常见工具集的治理政策。

注册端点

# create endpoint service (with multiple entries)
gcloud agent-registry services create core-gapi-services \
  --location=${REGION} \
  --display-name="gapi.core.services" \
  --description="core apis and services" \
  --endpoint-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com

验证端点注册

# list registry regional endpoints
gcloud agent-registry endpoints list --location=${REGION} \
  --flatten="interfaces[]" \
  --format="table(displayName, name.basename():label=ENDPOINT_ID, interfaces.url:label=URL)"

注册部分到此结束…接下来进入 MCP 服务器部分。

9. MCP 服务器

授予部署所需的 IAM 权限

向默认计算服务账号授予权限,以支持从源代码部署 Cloud Run 服务。

# grant cloud run builder role to default compute sa
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --role="roles/run.builder"

验证 IAM 权限

# show iam policy on project for default compute sa
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members=serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

部署 MCP 服务器

# deploy cloud run service
gcloud -q run deploy ${MCP_NAME} \
  --source=mcp-weather/server \
  --region=${REGION} \
  --no-allow-unauthenticated \
  --min-instances=1 \
  --network=vnet-${SLUG} \
  --subnet=subnet-${REGION}-crun \
  --vpc-egress=all-traffic \
  --startup-probe=httpGet.path=/warmup

授予入站流量的 IAM 权限

用户访问权限的绑定政策

向用户账号(本人)授予调用 Cloud Run 托管的 MCP 服务器的权限。这是运行 toolspec 生成脚本所必需的。

# grant run invoker role to user account on cloud run service level
gcloud run services add-iam-policy-binding ${MCP_NAME} \
  --region=${REGION} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/run.invoker"

验证 IAM 权限

# show iam policy on cloud run service for invoker role
gcloud run services get-iam-policy ${MCP_NAME} \
  --region=${REGION} \
  --flatten="bindings[].members" \
  --filter="bindings.role=roles/run.invoker" \
  --format="table(bindings.members:label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

为 MCP 调用创建自定义 SA

创建自定义服务账号,用于调用 Cloud Run 托管的 MCP 服务器。代理将使用此服务账号提取有效的 Google 签名 OIDC ID 令牌,以访问 Cloud Run。

# create custom sa for mcp server invocation
gcloud iam service-accounts create sa-${MCP_NAME}-invoker \
  --display-name="custom sa to invoke ${MCP_NAME}"
# set env var for service account identity
export MCP_INVOKER_SA="sa-${MCP_NAME}-invoker@${PROJ_ID}.iam.gserviceaccount.com"
echo ${MCP_INVOKER_SA}

为服务账号绑定政策

# grant run invoker role to custom sa on cloud run service level
gcloud run services add-iam-policy-binding ${MCP_NAME} \
  --role="roles/run.invoker" \
  --member="serviceAccount:${MCP_INVOKER_SA}" \
  --region=${REGION}

验证 IAM 权限

# show iam policy on cloud run service for invoker role
gcloud run services get-iam-policy ${MCP_NAME} \
  --region=${REGION} \
  --flatten="bindings[].members" \
  --filter="bindings.role=roles/run.invoker" \
  --format="table(bindings.members:label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

授予 IAM 身份验证权限

默认计算服务账号授予服务账号模拟角色。这样一来,在应用默认凭据 (ADC) 下运行的代理就可以代表自定义调用方服务账号生成 Google 签名的 OIDC ID 令牌,以对 Cloud Run 的调用进行身份验证。

用于身份验证的绑定政策

# grant oidc token creator role to default compute service account
gcloud iam service-accounts add-iam-policy-binding ${MCP_INVOKER_SA} \
  --role="roles/iam.serviceAccountOpenIdTokenCreator" \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com"

# grant service account token creator role to default compute service account
gcloud iam service-accounts add-iam-policy-binding ${MCP_INVOKER_SA} \
  --role="roles/iam.serviceAccountTokenCreator" \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com"

验证 IAM 权限

# show iam policy for the custom service account
gcloud iam service-accounts get-iam-policy ${MCP_INVOKER_SA} \
  --flatten="bindings[].members" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

创建 MCP toolspec

手动注册 MCP 服务器时,必须添加工具规范文件。toolspec.json 文件列出了 MCP 服务器提供的所有工具,并允许其他用户在注册后发现这些工具。

test_mcp.py 脚本(包含在 mcp-weather/ 代码库中)通过连接到目标 MCP 服务器(通过 SSE 或 HTTP)并调用 list_tools() 来检索服务器公开的所有可用工具,从而生成 toolspec.json 文件。

# fetch oidc token for cloud run authentication
export OIDC_TOKEN=$(gcloud auth print-identity-token)
# generate toolspec for registry ingestion
uv --directory mcp-weather run test_mcp.py --toolspec=include --token="${OIDC_TOKEN}"

查看生成的 toolspec 输出,其中显示了代理网关可用于出站政策评估和强制执行的 MCP 属性。

验证 MCP toolspec

# show toolspec mcp attributes
jq '.tools[] | {name, isReadOnly, isDestructive, isIdempotent, isOpenWorld}' mcp-weather/toolspec.json

更新 Cloud Run 入站流量

更改 Cloud Run 服务的入站流量设置,使其仅接受来自 VPC 网络的入站请求。这样可防止直接公开访问 MCP 端点,并仅允许内部资源访问该端点。VPC 网络中以 Google API 的 PSC 端点为目标的流量将被视为内部入站流量并允许通过。

# update cloud run service config
gcloud run services update ${MCP_NAME} \
  --region=${REGION} \
  --ingress=internal

验证 Cloud Run 入站流量

# show cloud run service details
gcloud run services describe ${MCP_NAME} --region=${REGION} | grep -iE 'ingress|egress'

注册 MCP 服务器

现在,有了 toolspec,请向区域性代理注册表注册 MCP 服务器。

# register mcp server
gcloud agent-registry services create ${MCP_NAME} \
  --location=${REGION} \
  --display-name="${MCP_DISPLAY_NAME}" \
  --description="mcp server for weather info" \
  --mcp-server-spec-type=tool-spec \
  --mcp-server-spec-content=mcp-weather/toolspec.json \
  --interfaces=url=${MCP_URL},protocolBinding=JSONRPC

验证 MCP 注册

# list registry regional mcp servers
gcloud agent-registry mcp-servers list --location=${REGION} --format="table(displayName, interfaces.url)"
# show mcp server registry details
gcloud agent-registry services describe ${MCP_NAME} --location=${REGION}
# fetch mcp server registry id
export MCP_REGISTRY_ID=$(gcloud agent-registry services describe ${MCP_NAME} \
  --location=${REGION} \
  --format="value(registryResource.basename())")
echo ${MCP_REGISTRY_ID}

MCP 服务器部分到此结束…接下来是 ADK 智能体部分。

10. ADK 代理

部署到 Agent Runtime 的 agent-weather ADK 代理在部署脚本中配置了以下设置,以便与 Agent Platform 集成:

  • "identity_type": types.IdentityType.AGENT_IDENTITY 为代理预配唯一的 基于 SPIFFE 的主账号身份
  • "agent_gateway_config": { "agent_to_anywhere_config": {"agent_gateway": } } 将所有出站的智能体发起的流量定向到 Agent Gateway,以进行政策评估和执行

代理还配置为使用 MCP 服务器,方法是传递用于获取 OIDC ID 令牌的网址和服务账号。

部署代理

# deploy agent runtime workload
uv --directory agent-weather run python3 deploy_agent.py \
  --project=${PROJ_ID} \
  --region=${REGION} \
  --src-dir=./agent \
  --staging-bucket=${STAGING_BUCKET} \
  --display-name="${RE_AGENT_NAME}" \
  --description="agent for weather info" \
  --mcp-server-url="${MCP_URL}" \
  --mcp-invoker-sa="${MCP_INVOKER_SA}" \
  --enable-telemetry \
  --enable-agent-identity \
  --agent-gateway-egress=${AGW_URI} \
  --allow-token-sharing

验证部署

提取部署生命体征

# fetch agent runtime resource (reasoning engine) id
export RE_ENGINE_ID=$(curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" | \
  jq -r --arg name "${RE_AGENT_NAME}" '.reasoningEngines[] | select(.displayName==$name) | .name | split("/") | last')
echo ${RE_ENGINE_ID}
# fetch agent runtime (reasoning engine) agent identity
export RE_AGENT_IDENTITY=$(gcloud agent-registry agents list \
  --location=${REGION} --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(attributes.'agentregistry.googleapis.com/system/RuntimeIdentity'.principal)")
echo ${RE_AGENT_IDENTITY}
# fetch agent registry uid for agent
export RE_AGENT_REGISTRY_ID=$(gcloud agent-registry agents list \
  --location=${REGION} \
  --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(uid)")
echo ${RE_AGENT_REGISTRY_ID}

验证注册表条目

# show agent resource registry details
gcloud agent-registry agents describe ${RE_AGENT_REGISTRY_ID} --location=${REGION}

验证网关配置

# show agent config details (gateway config)
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq '{displayName: .displayName, name: .name, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

授予 IAM 身份验证权限

如果代理配置为使用代理身份,则会从联合 Security Token Service (STS) 接收由 sts.googleapis.com(SPIFFE 身份提供方)签名的令牌(JWT-SVID 格式)。

不过,内置的 Cloud Run IAM 调用方检查仅信任并可以验证由主要 Google OIDC 签发者 (accounts.google.com) 签名的标准 OIDC ID 令牌。

因此,自定义服务账号用于在运行时通过服务账号模拟将代理身份获得的联合 STS 令牌 (JWT-SVID) 交换为标准 Google (OIDC) 令牌。

为自定义服务账号创建所需的角色绑定。

用于身份验证(冒充)的绑定政策

# grant service account token creator role to agent principal identity
gcloud iam service-accounts add-iam-policy-binding ${MCP_INVOKER_SA} \
  --role="roles/iam.serviceAccountTokenCreator" \
  --member="${RE_AGENT_IDENTITY}"

# grant service account openid token creator role to agent principal identity
gcloud iam service-accounts add-iam-policy-binding ${MCP_INVOKER_SA} \
  --role="roles/iam.serviceAccountOpenIdTokenCreator" \
  --member="${RE_AGENT_IDENTITY}"

验证 IAM 权限

# show iam policy for custom service account
gcloud iam service-accounts get-iam-policy ${MCP_INVOKER_SA} \
  --flatten="bindings[].members" \
  --format="table(bindings.members.sub('^.*locations/', '.../locations/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

授予运行时 IAM 权限

为运行时授予 IAM 权限后,代理便可使用其基于 SPIFFE 的正文代理身份访问 AI 平台服务和其他核心 Google API 及服务。这些政策通过 IAM 在资源级层强制执行。

为代理身份创建所需的角色绑定。

用于代理访问的绑定政策

# grant ai platform user role
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/aiplatform.user"

# grant agent default access role
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/aiplatform.agentDefaultAccess"

# grant agent registry viewer role
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/agentregistry.viewer"
# grant cloud logging writer role
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/logging.logWriter"

# grant cloud monitoring writer role
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/monitoring.metricWriter"

# grant browser role for resource manager lookup
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/browser"

验证 IAM 权限

# show iam policy on project for agent identity
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_IDENTITY}" \
  --format="table(bindings.members.sub('^.*locations/', '.../locations/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

ADK 代理部分到此结束,接下来进入政策部分。

11. 政策

代理治理授权政策允许流量从 AI 代理通过代理网关路由到最终资源目的地(例如,用于检索数据的专用 MCP 服务器端点)。当流量到达目的地后,常规的服务级或应用级权限会授权数据访问。

授予 IAM 角色以实现代理网关出站流量可启用访问政策,允许代理主账号身份通过代理网关访问已注册的目标资源。政策在代理网关级别强制执行,并使用 Identity-Aware Proxy (IAP) IAM 政策进行验证。

授予 IAM 角色以实现代理网关出站流量

代理网关会检查传入的请求,以验证调用代理身份是否对目标资源具有 iap.webServiceVersions.egressViaIAP 权限(由角色 roles/iap.egressor 授予)。

向所选目标资源上的代理身份或代理运行时主账号集授予 roles/iap.egressor 权限,即可允许此出站流量。在此 Codelab 中,该角色应用于主账号 agent,从而向特定的 Agent Runtime 代理身份授予权限。

IAM 政策会绑定到代理注册表数据模型中定义的目标资源。iap_web/agentRegistry 资源表示 IAM 层次结构中的“注册表级”级别。其中,agentRegistryagentsmcpServersendpoints 的各个子资源的父级。在此 Codelab 中,IAM 政策绑定到 mcpServerendpoint 级别,从而将权限应用于特定的子资源。

配置代理到 MCP 服务器的政策

创建代理与 MCP 服务器之间的政策

# fetch active etag on policy for mcp server
export IAP_ETAG=$(gcloud beta iap web get-iam-policy \
  --resource-type=agent-registry --region=${REGION} \
  --mcp-server=${MCP_REGISTRY_ID} --format="value(etag)")
echo ${IAP_ETAG}

注意: 由于尚未定义任何政策,etag: 应返回默认值 ACAB

# create policy config file
cat > cfg/iap-policy-agent-to-mcp.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ]
    }
  ],
  "etag": "${IAP_ETAG}"
}
EOF

将代理的政策绑定到 MCP 服务器

# import policy file
gcloud beta iap web set-iam-policy cfg/iap-policy-agent-to-mcp.json \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID} \
  --region=${REGION}

验证 IAM 权限

# show iap iam policy on mcp server for agent
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID}

政策部分到此结束…接下来是审核部分。

12. 审核

Agent Gateway 授权扩展程序以 DRY_RUN 模式运行。系统会观察并记录出站请求,但不会阻止这些请求。在切换到 ENFORCED 模式之前分析日志,可确认需要配置哪些政策才能允许预期流量。

测试代理查询

打开浏览器窗口,进入 Google Cloud 控制台界面,然后依次前往:

  • Agent Platform → Agents → Deployments → agent-weather
  • 选择实验平台标签页

或者,通过以下终端命令进行回显,然后点击链接跳转到代理游乐场

# playground
echo "https://console.cloud.google.com/agent-platform/runtimes/locations/${REGION}/agent-engines/${RE_ENGINE_ID}/playground?project=${PROJ_ID}"

提交一些测试查询。

  • What is the weather like in London?
  • How warm is it in New York?

验证查询是否返回有效响应。

分析审核日志

查看日志并检查试运行政策评估。

# show gateway logs for endpoints and authz status (duplicates removed)
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND httpRequest.requestUrl:*" \
  --project=${PROJ_ID} \
  --limit=50 \
  --freshness=24h \
  --format="table( \
    timestamp.date(tz=LOCAL):label=TIMESTAMP, \
    httpRequest.status:label=STATUS, \
    jsonPayload.authzPolicyInfo.result:label=AUTHZ, \
    httpRequest.requestUrl.sub('(https?://[^/:]+).*', '\1'):label=ENDPOINT \
  )" | awk 'NR==1 {print; next} ! seen[$2,$3,$4]++'
TIMESTAMP            STATUS  AUTHZ    ENDPOINT
YYYY-MM-DDTHH:MM:SS  200     ALLOWED  https://cloudresourcemanager.googleapis.com
YYYY-MM-DDTHH:MM:SS  200     ALLOWED  https://${REGION}-aiplatform.googleapis.com
YYYY-MM-DDTHH:MM:SS  200     ALLOWED  https://iamcredentials.googleapis.com
YYYY-MM-DDTHH:MM:SS  200     ALLOWED  https://mcp-weather-${PROJ_NO}.${REGION}.run.app
YYYY-MM-DDTHH:MM:SS  200     ALLOWED  https://telemetry.googleapis.com
YYYY-MM-DDTHH:MM:SS  202     ALLOWED  https://mcp-weather-${PROJ_NO}.${REGION}.run.app

配置代理到端点政策

由于 agent-weather 的核心 Google API 已在注册表中注册为集体端点服务 (gapi.core.services),因此只需一项政策即可授予代理端点访问的出站权限。

创建从代理到端点的政策

# set var for endpoint display name
export ENDPOINT_DISPLAY_NAME="gapi.core.services"
echo ${ENDPOINT_DISPLAY_NAME}
# fetch endpoint registry id
export ENDPOINT_REGISTRY_ID=$(gcloud agent-registry services list \
  --location=${REGION} \
  --filter="displayName=${ENDPOINT_DISPLAY_NAME}" \
  --format="value(registryResource.basename())")
echo ${ENDPOINT_REGISTRY_ID}
# create policy config file
cat > cfg/iap-policy-agent-to-ep.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ]
    }
  ]
}
EOF

将代理的政策绑定到端点

# import policy file (bind iam policy)
gcloud -q beta iap web set-iam-policy cfg/iap-policy-agent-to-ep.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_REGISTRY_ID} \
  --region=${REGION}

验证 IAM 权限

# show iap iam policy on endpoint for agent
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_REGISTRY_ID}

审核部分到此结束…接下来进入强制执行部分。

13. 强制执行

智能体能够以 DRY_RUN 模式通过网关成功发出请求。我们分析了访问日志和端点,创建了一组治理政策,允许代理发出必要的出站流量。现在,通过更新授权政策,将代理网关 IAP 授权扩展程序过渡到 ENFORCE 模式。

更新授权扩展程序

创建授权扩展程序

# create authz extension config file (enforced mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-iap-enforced.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap-enforced
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V1"
EOF

导入授权扩展程序

# import authz extension file (create extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap-enforced \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap-enforced.yaml \
  --location=${REGION}

验证授权扩展程序

# list authz extensions (imported configs)
gcloud service-extensions authz-extensions list --location=${REGION}

更新授权政策

创建授权政策

# re-write authz policy config file (pointing to enforced authz extension)
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap-enforced"
EOF

绑定授权政策

# import authz policy config file (enable new authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION}

验证授权政策

# show authz policy details (verify enforced mode)
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION}

验证强制执行情况

返回控制台界面中的代理 Playground

  • 点击“+ 新会话”按钮,即可创建新的聊天会话

提交其他测试查询...

  • What is the current temperature in Los Angeles?

观察到传递给 Gemini 基础模型 (${REGION}-aiplatform.googleapis.com/...) 和 MCP 服务器端点 (mcp-weather-${PROJ_NO}.${REGION}.run.app/mcp) 的代理请求带有 HTTP 200 OK 状态代码。

分析网关日志

# show gateway logs for http requests
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND httpRequest.requestUrl:*" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=IAP_AUTHZ,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    httpRequest.requestUrl:label=URL)"

检查是否有请求被屏蔽。在网关审核日志中查找任何被拒绝的出口尝试,这些尝试具有 HTTP 403 Forbidden 状态代码。

# show gateway logs for authz denies
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND (jsonPayload.authzPolicyInfo.result=\"DENIED\" OR httpRequest.status=403)" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.enforcedGatewaySecurityPolicy.serverNameIndication:label=SNI,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ_RESULT,
    jsonPayload.authzPolicyInfo.policies[0].name.basename():label=DENYING_POLICY
  )"

查看代理网关 MCP 日志,并注意如何检查 MCP 应用载荷 (JSON-RPC) 以公开协议元数据,包括正在执行哪些工具。

# show gateway logs for mcp requests with method and tool name
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND jsonPayload.agentGatewayInfo.mcpInfo.method:*" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=IAP_AUTHZ,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    jsonPayload.agentGatewayInfo.mcpInfo.parameter:label=TOOL_NAME,
    httpRequest.requestUrl:label=URL
  )"

分析防火墙日志

查看网络防火墙政策规则优先级 1001 的日志。

# show firewall logs for rule 1001
gcloud logging read \
  "jsonPayload.rule_details.priority=1001" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    jsonPayload.disposition:label=ACTION,
    jsonPayload.connection.src_ip:label=SRC_IP,
    jsonPayload.connection.dest_ip:label=DEST_IP,
    jsonPayload.connection.dest_port:label=DEST_PORT,
    jsonPayload.vpc.subnetwork_name:label=SUBNET
  )"

请注意不同流量路径的网络级视图:

  • 代理网关使用 subnet-${REGION}-agw 与内部 VPC 网络资源通信
  • Cloud Run 托管的 MCP 服务器使用 subnet-${REGION}-crun 调用外部天气 API
TIMESTAMP            ACTION   SRC_IP        DEST_IP        DEST_PORT  SUBNET
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   188.40.99.226  443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   202.61.206.6   443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   188.40.99.226  443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   202.61.206.6   443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   202.61.206.6   443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  10.10.10.16   202.61.206.6   443        subnet-${REGION}-crun
YYYY-MM-DDTHH:MM:SS  ALLOWED  192.168.10.2  240.0.0.10     443        subnet-${REGION}-agw
YYYY-MM-DDTHH:MM:SS  ALLOWED  192.168.10.2  240.0.0.10     443        subnet-${REGION}-agw
YYYY-MM-DDTHH:MM:SS  ALLOWED  192.168.10.2  240.0.0.10     443        subnet-${REGION}-agw

分析 DNS 日志

查看专用 DNS 区域 (priv-zone-run) 解析的请求的 DNS 查询日志。

gcloud logging read \
  "resource.type=\"dns_query\" AND resource.labels.target_name=\"priv-zone-run\"" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    jsonPayload.queryName:label=QUERY_NAME,
    jsonPayload.queryType:label=TYPE,
    jsonPayload.rdata:label=RESPONSE_RDATA,
    jsonPayload.sourceNetwork:label=NETWORK,
    resource.labels.source_type:label=SRC_TYPE
  )"

注意:Cloud Run 服务 (mcp-weather...run.app) 的 DNS 查找源自使用 DNS 对等互连的 VPC 网络,并成功解析为 Google API 的内部 PSC 端点 (240.0.0.10)。

TIMESTAMP            QUERY_NAME                                     TYPE  RESPONSE_RDATA                                                     NETWORK   SRC_TYPE
YYYY-MM-DDTHH:MM:SS  mcp-weather-${PROJ_NO}.${REGION}.run.app.  A     mcp-weather-${PROJ_NO}.${REGION}.run.app. 300     IN      a       240.0.0.10  vnet-${SLUG}  peering-zone
YYYY-MM-DDTHH:MM:SS  mcp-weather-${PROJ_NO}.${REGION}.run.app.  A     mcp-weather-${PROJ_NO}.${REGION}.run.app. 300     IN      a       240.0.0.10  vnet-${SLUG}  peering-zone
YYYY-MM-DDTHH:MM:SS  mcp-weather-${PROJ_NO}.${REGION}.run.app.  A     mcp-weather-${PROJ_NO}.${REGION}.run.app. 300     IN      a       240.0.0.10  vnet-${SLUG}  peering-zone
YYYY-MM-DDTHH:MM:SS  mcp-weather-${PROJ_NO}.${REGION}.run.app.  A     mcp-weather-${PROJ_NO}.${REGION}.run.app. 300     IN      a       240.0.0.10  vnet-${SLUG}  peering-zone

“强制执行”部分到此结束,接下来进入清理部分。

14. 清理

# delete agent runtime
curl -s -X DELETE "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}?force=true" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)"

# next
# delete agent resources
gcloud -q storage rm --recursive gs://${STAGING_BUCKET}

for ROLE in \
  "roles/aiplatform.user" \
  "roles/aiplatform.agentDefaultAccess" \
  "roles/agentregistry.viewer" \
  "roles/logging.logWriter" \
  "roles/monitoring.metricWriter" \
  "roles/browser"; do
  gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
    --member="${RE_AGENT_IDENTITY}" \
    --role="${ROLE}"
done

# next
# delete mcp (cloud run) resources
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --role="roles/run.builder"

gcloud -q run services delete ${MCP_NAME} --region=${REGION}

gcloud -q artifacts repositories delete cloud-run-source-deploy --location=${REGION}

gcloud -q iam service-accounts delete sa-${MCP_NAME}-invoker@${PROJ_ID}.iam.gserviceaccount.com

gcloud -q agent-registry services delete ${MCP_NAME} --location=${REGION}

# next
# delete gateway resources (policy binding must be deleted synchronously first)
gcloud -q beta network-security authz-policies delete ${AGW_NAME}-authz-policy-profile-iap --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap-dryrun --location=${REGION} --async

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap-enforced --location=${REGION} --async

gcloud -q network-services agent-gateways delete ${AGW_NAME} --location=${REGION} --async

# next
# remove policies
export IAP_ETAG=$(gcloud beta iap web get-iam-policy --resource-type=agent-registry --endpoint=${ENDPOINT_REGISTRY_ID} --region=${REGION} --format="value(etag)")
cat > cfg/iap-policy-empty.json << EOF
{
  "bindings": [],
  "etag": "${IAP_ETAG}"
}
EOF

gcloud -q beta iap web set-iam-policy cfg/iap-policy-empty.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_REGISTRY_ID} \
  --region=${REGION}

# next
# delete dns resources
gcloud -q dns record-sets delete *.run.app --type=A --zone=priv-zone-run

gcloud -q dns managed-zones delete priv-zone-run

gcloud -q dns policies update dns-policy-${SLUG} --networks=""

gcloud -q dns policies delete dns-policy-${SLUG}

# next
# delete router (includes nat) and psc resources
gcloud -q compute routers delete cr-nat-${SLUG}-${REGION} --region=${REGION}

gcloud -q compute forwarding-rules delete psc2gapis --global

gcloud -q compute addresses delete ip-psc2gapis --global

# next
# delete firewall resources
gcloud -q compute network-firewall-policies associations delete --name=fw-policy-bind-${SLUG} --firewall-policy=fw-policy-${SLUG} --global-firewall-policy

gcloud -q compute network-firewall-policies rules delete 1001 --firewall-policy=fw-policy-${SLUG} --global-firewall-policy

gcloud -q compute network-firewall-policies delete fw-policy-${SLUG} --global

# next
# delete network resources
gcloud -q compute networks subnets delete subnet-${REGION}-agw --region=${REGION}

gcloud -q compute networks subnets delete subnet-${REGION}-crun --region=${REGION}

gcloud -q compute network-attachments delete psc-na-${REGION}-agw --region=${REGION}

gcloud -q compute networks delete vnet-${SLUG}

# next
# clean up local working directories and generated configs
rm -rf cfg agent-weather mcp-weather

# end

清理部分到此结束…接下来是总结

15. 总结

恭喜!您已成功部署代理网关并管控出站 AI 代理流量!

cosmopup

Cosmopup 认为 Codelab 非常棒!

接下来会发生什么?

欢迎使用此反馈表单提出任何意见、问题或更正。

谢谢!