Claude apps gateway:在 Google Cloud 上部署
在 Google Cloud 上部署 Claude apps gateway 的完整示例:Cloud Run 或 GKE、Cloud SQL for PostgreSQL、Secret Manager、服务账号认证 Agent Platform,含 API 启用、服务账号、镜像、数据库、gateway.yaml、密钥、部署、Terraform 参考与排障。
本页逐步演示在 Google Cloud 上运行 Claude apps gateway 的一种方式。这套配置是客户自管基础设施的工作示例,不是受支持的生产部署:先用它了解各部分如何组合,再按你自己的环境调整(与平台无关的要求见部署指南)。
示例以 Google Cloud 的 Agent Platform 作为模型上游,计算用 Cloud Run 或 GKE。Google Workspace 是示例身份提供商(IdP),但任何兼容 OpenID Connect(OIDC)的 IdP 都可以,只有 oidc 块会变(每个 IdP 的细节见 IdP 设置)。
你要构建什么
部署由这些组成:
- 运行网关容器的 Cloud Run 服务或 GKE Deployment
- 存放网关镜像的 Artifact Registry 仓库
- 网关存储用的 Cloud SQL for PostgreSQL 实例,只有私有 IP
- 存放
gateway.yaml、JWT 签名密钥、OIDC 客户端密钥和 Postgres URL 的 Secret Manager 密钥 - 带
roles/aiplatform.user的服务账号,在 Cloud Run 上直接附加,或在 GKE 上通过 Workload Identity 绑定 - 你自己提供的 HTTPS 前端:Cloud Run 前面的内部 Application Load Balancer(本演练为它配置网关但不创建它),或 GKE 上类别为
gce-internal的内部 GKE Ingress
前提
- 启用了计费、并有权创建上述资源的 GCP 项目
- 用
gcloud auth login认证的gcloudCLI,以及本地安装的 Docker - GKE 轨道:
kubectl,以及位于下面演练所创建 VPC 上的 GKE 集群 - 在发布所需模型的区域里对 Model Garden 里你需要的 Claude 模型的访问权限
- 重定向 URI 为
https://<gateway-host>/oauth/callback的 Google Workspace OAuth 2.0 web 应用客户端(见 IdP 设置) - 网关的 TLS 主机名,通常是指向负载均衡器的内部 DNS 名
先设置一次项目和区域:
export PROJECT_ID=<your-project>
export REGION=us-east5 # Model Garden 里发布你所需 Claude 模型的区域
gcloud config set project "$PROJECT_ID"部署网关
下面的步骤用 gcloud 命令创建完整部署。
第 1 步:启用 API
启用演练用到的服务 API:
gcloud services enable \
aiplatform.googleapis.com \
artifactregistry.googleapis.com \
sqladmin.googleapis.com \
secretmanager.googleapis.com \
iamcredentials.googleapis.com \
iam.googleapis.com \
compute.googleapis.com \
servicenetworking.googleapis.com \
run.googleapis.com \
container.googleapis.com需要哪些 API 取决于部署路径:compute 和 servicenetworking 用于私有 IP 的 Cloud SQL 路径;run 只用于 Cloud Run;container 只用于 GKE。
第 2 步:创建服务账号并授予 IAM
网关以一个专用服务账号运行,它有调用 Google Cloud Agent Platform 的权限;它用密码用户经 VPC 访问 Cloud SQL,所以不需要 Cloud SQL 的 IAM 角色:
gcloud iam service-accounts create claude-gateway --display-name="Claude apps gateway"
SA="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:${SA}" --role="roles/aiplatform.user" --condition=None然后在 Model Garden 里为项目启用 Claude 模型;模型发布在特定区域,所以要查看每个模型卡。
第 3 步:构建镜像并推送到 Artifact Registry
按容器镜像要求构建镜像,使用 linux-x64 glibc 二进制,并推送:
gcloud artifacts repositories create claude-gateway \
--repository-format=docker --location="$REGION"
gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet
# Cloud Run 要求 linux/amd64。--provenance=false 避免产生
# Cloud Run 拒绝的 buildx OCI 镜像索引。
docker build --platform=linux/amd64 --provenance=false \
-t "${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>" .
docker push "${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>"第 4 步:创建 Cloud SQL for PostgreSQL
通过 Private Services Access 在 VPC 上创建实例,使它没有公网 IP;这同样满足强制执行 constraints/sql.restrictPublicIp 的项目:
VPC=cc-gateway-vpc
gcloud compute networks create "$VPC" --subnet-mode=custom
gcloud compute networks subnets create cc-gateway-subnet \
--network="$VPC" --region="$REGION" --range=10.0.0.0/24
# Private Services Access:每个 VPC 一次性设置
gcloud compute addresses create "google-managed-services-${VPC}" \
--global --purpose=VPC_PEERING --prefix-length=16 --network="$VPC"
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges="google-managed-services-${VPC}" --network="$VPC"
gcloud sql instances create claude-gateway-db \
--database-version=POSTGRES_16 --tier=db-g1-small --region="$REGION" \
--network="projects/${PROJECT_ID}/global/networks/${VPC}" --no-assign-ip
gcloud sql databases create claude_gateway --instance=claude-gateway-db
PGPASS="$(openssl rand -hex 24)"
gcloud sql users create gateway --instance=claude-gateway-db --password="$PGPASS"
PRIVATE_IP="$(gcloud sql instances describe claude-gateway-db \
--format='value(ipAddresses[0].ipAddress)')"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${PRIVATE_IP}:5432/claude_gateway?sslmode=require"Cloud Run 或 GKE 运行时必须在这个 VPC 上,或被路由进这个 VPC。
第 5 步:编写 gateway.yaml
upstreams 块以 auth: {} 指向 Google Cloud Agent Platform,所以网关经 Application Default Credentials 从运行时服务账号认证(每个字段见配置参考)。两个 listen 字段描述网关前面是什么:public_url 是外部的 https:// 源,任何非环回绑定都必须设置,网关只用这个值构造 IdP 的 redirect_uri 和它的发现文档,从不用 X-Forwarded-* 头;trusted_proxies 是前端的源范围,网关只在 TCP 对端在该列表里时才采信 X-Forwarded-For,并沿链越过受信任的跳数,使每 IP 登录速率限制和审计事件记录开发者 IP 而不是负载均衡器的。要把 trusted_proxies 设成与你的前端匹配。类别为 gce 的外部 GKE Ingress 不在列表里:它会创建公网转发规则地址,而 /login 的私有网络检查会拒绝它。
| 前端 | trusted_proxies |
|---|---|
| 直接访问 Cloud Run,没有负载均衡器 | [169.254.0.0/16] |
| Cloud Run 前面的内部 Application Load Balancer | 169.254.0.0/16 加上你的 proxy-only 子网的 CIDR |
GKE 内部 Ingress,类别 gce-internal | 你的 proxy-only 子网的 CIDR |
下面的例子使用 Cloud Run 前面的内部负载均衡器的值:
listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [169.254.0.0/16, <your-proxy-only-subnet-cidr>]
oidc:
issuer: https://accounts.google.com
client_id: <your-oauth-client-id>
client_secret: ${OIDC_CLIENT_SECRET} # GKE:${file:/secrets/oidc-client-secret}
allowed_email_domains: [example.com]
# Google 忽略 offline_access;下面这些产生刷新令牌:
scopes: [openid, profile, email]
extra_auth_params: { access_type: offline, prompt: consent }
session:
jwt_secret: ${GATEWAY_JWT_SECRET} # GKE:${file:/secrets/jwt-secret}
store:
postgres_url: ${GATEWAY_POSTGRES_URL} # GKE:${file:/secrets/postgres-url}
# readiness_grace_seconds: 300 # 在 Cloud SQL 故障转移期间
# 继续通过就绪探针
upstreams:
- provider: vertex
region: <your-region> # 必须与 $REGION 一致
project_id: <your-project>
auth: {} # 经运行时服务账号的 ADC注意:Google 的 id_token 不带 groups 声明。要在 managed.policies 里用基于组的策略并以 Google Workspace 作为 IdP,要配置 oidc.google_groups:它用带域范围委派的服务账号,通过 Admin SDK Directory API 查找每个用户的组;没有它时,改按 email_domain 匹配。
第 6 步:把密钥存进 Secret Manager
创建四个密钥,并把 roles/secretmanager.secretAccessor 授予 claude-gateway 服务账号:
| 密钥 | 来源 |
|---|---|
gateway-jwt-secret | openssl rand -base64 32 |
gateway-oidc-client-secret | Google Cloud Console → OAuth 客户端 |
gateway-postgres-url | Cloud SQL 步骤里的 $GATEWAY_POSTGRES_URL |
gateway-config | 上一步的完整 gateway.yaml |
密钥如何到达容器取决于轨道:在 GKE 上,它们通过 Secret Manager CSI driver 挂载为文件,gateway.yaml 引用 ${file:/secrets/...};在 Cloud Run 上(它无法把多个密钥挂载到同一个目录),gateway.yaml 挂载为文件,另外三个注入为环境变量,所以 gateway.yaml 改为引用 ${GATEWAY_JWT_SECRET}、${OIDC_CLIENT_SECRET} 和 ${GATEWAY_POSTGRES_URL}。
第 7 步:部署
Cloud Run 轨道。 下面的命令在内部负载均衡器后面为生产部署:
gcloud run deploy claude-gateway \
--image="${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>" \
--region="$REGION" \
--service-account="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \
--min-instances=1 \
--max-instances=8 \
--timeout=3600 \
--ingress=internal \
--network="$VPC" --subnet=cc-gateway-subnet --vpc-egress=private-ranges-only \
--set-secrets=/etc/claude/gateway.yaml=gateway-config:latest,GATEWAY_JWT_SECRET=gateway-jwt-secret:latest,OIDC_CLIENT_SECRET=gateway-oidc-client-secret:latest,GATEWAY_POSTGRES_URL=gateway-postgres-url:latest \
--no-invoker-iam-check经 --network、--subnet 和 --vpc-egress=private-ranges-only 的直接 VPC 出口让服务直接到达 Cloud SQL 私有 IP。每个实例最多持有 store.max_connections 个 Postgres 连接(默认五个),所以要让最大实例数乘以 store.max_connections 低于你的 Cloud SQL 层级的连接限制(参考资产为此把 db-g1-small 层级的实例数上限设为 8)。到 Google Cloud Agent Platform 端点和 accounts.google.com 的公网出口直接走互联网而不经 VPC,所以不需要 Cloud NAT。
调用方 IAM 检查必须开放或禁用:网关运行自己的 OIDC,它的客户端不带 GCP 令牌,所以 Cloud Run 的调用方检查必须放行未认证的请求;请求到达容器后由网关的 OIDC 登录来认证,allowed_email_domains 限制哪些域名可以登录。有两个标志可放行未认证请求:--no-invoker-iam-check 禁用该检查,没有 allUsers 绑定需要管理,并且在 Domain Restricted Sharing 下也能用;--allow-unauthenticated 把 run.invoker 角色授予 allUsers,在你的组织不允许 --no-invoker-iam-check 时使用。经 --ingress 的入口限制是与调用方检查独立的另一层,保持设置它以把服务限制在你的企业网络里。
默认情况下 Cloud Run 的 *.run.app URL 解析到公网地址,而 /login 的私有网络检查会拒绝它。有两种拓扑能给开发者一个可私有解析的主机名,Cloud Run 都不会替你创建:内部 Application Load Balancer(本页 gateway.yaml 假定的拓扑)——在服务前面创建带内部 DNS 名和证书的内部 Application Load Balancer,并把 listen.public_url 设为该主机名;internal 入口设置已经放行来自内部 Application Load Balancer 的流量,internal-and-cloud-load-balancing 还会放行外部 Application Load Balancer(其公网地址会被 /login 私有网络检查拒绝),所以本页没有任何拓扑需要它;仅内部入口、没有负载均衡器——保持部署命令不变,把 listen.public_url 留作 *.run.app URL(下面参考资产里的默认值);要让 *.run.app 私有解析,你的网络团队必须已经运营着 Google API 的 Private Service Connect 端点、把 *.run.app 解析到它的 Cloud DNS 私有区域,以及到该端点的本地路由。Google 的 Cloud Run 私有网络指南涵盖两种选择都需要的基础设施;在网关用私有主机名提供服务后验证登录,在那之前,从 Cloud Run 里的日志确认容器已启动。第一次登录之前,把 OAuth 客户端的授权重定向 URI 更新为 <public_url>/oauth/callback;更改 public_url 之后要重新部署,因为网关只用该设置构造公开源,忽略 X-Forwarded-Host 和 X-Forwarded-Proto;只有设了 listen.trusted_proxies 时才采信 X-Forwarded-For 作为客户端 IP。
GKE 轨道。 集群必须在 Cloud SQL 步骤创建的 $VPC 上,pod 才能到达数据库的私有 IP:单靠 VPC 对等不行,因为 Cloud SQL 私有 IP 本身就是一个对等网络,而对等是非传递的。要在该 VPC 上创建新集群,给 gcloud container clusters create 传 --network="$VPC" --subnetwork=cc-gateway-subnet。在集群及其节点池上启用 Workload Identity,然后把 Google 服务账号绑定到 Kubernetes 服务账号,使 pod 继承它的凭据:
gcloud container clusters update <cluster> --region="$REGION" \
--workload-pool="${PROJECT_ID}.svc.id.goog"
# 在 Standard 集群上,现有节点池也需要 GKE_METADATA;
# Autopilot 默认启用它。
gcloud container node-pools update <pool> --cluster=<cluster> \
--region="$REGION" --workload-metadata=GKE_METADATA
kubectl create namespace claude-gateway
kubectl create serviceaccount gateway -n claude-gateway
gcloud iam service-accounts add-iam-policy-binding \
"claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[claude-gateway/gateway]"
kubectl annotate serviceaccount gateway -n claude-gateway \
iam.gke.io/gcp-service-account="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com"按「Kubernetes 部署」把网关部署为标准的 Deployment 加 Service 和类别为 gce-internal 的内部 Ingress,带有:serviceAccountName: gateway;Secret Manager CSI driver 把密钥挂载在 /secrets;就绪探针指向 GET /readyz。给网关 Service 附加带调高 timeoutSec 的 BackendConfig:GKE Ingress 背后的负载均衡器后端服务默认超时 30 秒,会切断长时间的流式响应。不要在 Workload Identity 集群上应用阻止 169.254.169.254 的出口 NetworkPolicy:pod 必须能到达元数据服务器获取凭据,那里的防御靠网关内置的 SSRF 防护。网关会在启动时记录一条警告,说元数据端点可达并建议应用出口 NetworkPolicy;在 Workload Identity 下这条警告是预期的,因为 pod 需要该端点。
第 8 步:把网关 URL 推送到开发者机器
网关现在运行了,但在网关 URL 到达开发者机器之前,他们无法从 /login 到达它。把完整的托管设置片段(带 forceLoginMethod、forceLoginGatewayUrl 和选择加入的 parentSettingsBehavior: "merge")经 MDM 部署到每台设备;登录选择器里没有供开发者手动选择的网关选项。
Terraform 参考
参考部署资产把本页的 Cloud Run 轨道自动化;配置和镜像资产适用于两条轨道:
setup.sh:幂等的gcloud供应器,走完整的 Cloud Run 路径,从启用 API 一直到首次部署terraform/:同样的部署写成基础设施即代码,用于全新部署:先对 Artifact Registry 仓库做有目标的 apply,再构建并推送镜像,然后做完整 applygateway.yaml.example和用于 distroless 运行时镜像的Dockerfile
这些资产默认把 Cloud Run 入口设为 internal,与本页的部署命令一致;该设置在服务前面有或没有内部 Application Load Balancer 时都能工作,资产同样不创建负载均衡器。资产还默认把调用方层设为 allUsers 的 run.invoker 授予,而不是 --no-invoker-iam-check,与本页演练相反;两者都行,选择取决于你组织的策略约束。这些资产作为工作示例提供,不是受支持的生产制品:要按你的环境审查并调整。
排障
网关启动和登录错误见与平台无关的排障表;下面的条目是 Google Cloud 特有的。
| 症状 | 原因 | 修复 |
|---|---|---|
Cloud Run 在到达容器之前返回 403 Forbidden | 调用方 IAM 检查仍然启用 | 用 --no-invoker-iam-check 部署,或用 --allow-unauthenticated 把 run.invoker 角色授予 allUsers |
--no-invoker-iam-check 被拒绝,提示 invoker_iam_disabled is not currently available | 被 constraints/run.managed.requireInvokerIam 阻止 | 用 --allow-unauthenticated;如果经 constraints/iam.allowedPolicyMemberDomains 的 Domain Restricted Sharing 也阻止了它,就用 GKE 轨道,它在网络层暴露网关而没有 allUsers 绑定 |
部署时出现 Container manifest type … must support amd64/linux | 镜像是在非 amd64 主机上构建的,或 buildx 产生了 OCI 镜像索引 | 用 --platform=linux/amd64 --provenance=false 构建 |
| 在 Cloud Run 上网关启动因 Postgres 连接超时错误退出 | 服务没有接入 VPC,或 Cloud SQL 在该 VPC 上没有私有 IP | 用 --network 和 --subnet 部署以启用直接 VPC 出口,并用 --no-assign-ip 和指向同一个 VPC 的 --network 创建 Cloud SQL 实例 |
Google Cloud Agent Platform 请求返回 403 PERMISSION_DENIED | 运行时没有使用 claude-gateway 服务账号,或项目没有在 Model Garden 里启用该模型 | 在 Cloud Run 上设 --service-account,或在 GKE 上绑定 Workload Identity,并在目标区域的 Model Garden 里启用每个 Claude 模型 |
| 流式响应在固定时长后被切断 | 前端请求超时:GKE Ingress 背后的负载均衡器后端服务默认 30 秒,Cloud Run 默认 300 秒 | 在 GKE 上附加带调高 timeoutSec 的 BackendConfig,或在 Cloud Run 上用 --timeout=3600 部署 |
下一步
- 配置参考:每个
gateway.yaml选项,包括managed.policies和telemetry - 部署与运维:IdP 设置、健康检查、JWT 密钥轮换、升级和安全模型
- Claude apps gateway 概览:快速开始和连接开发者