Google Cloud 入门:部署一个需要身份验证的 Cloud Run 服务
Google Cloud 提供按需使用的计算、存储、数据库和网络服务。你可以租用一台虚拟机,也可以只提交一个应用,让平台负责启动实例和处理请求。开始时不必记住产品目录;先弄清楚资源放在哪里、谁可以操作、费用记到哪里。
本文用一个返回 JSON 的 Python HTTP 应用贯穿这些概念:先在本机运行,再部署为需要身份验证的 Cloud Run 服务,最后更新、回滚和清理。它不需要数据库、Kubernetes 或服务账号密钥。
本机步骤不会调用 Google Cloud;安装工具、下载 Python、依赖和容器镜像仍可能需要网络。云端命令是供读者按需执行的操作说明,会登录账号、上传代码、修改资源并可能产生费用。只想理解流程时,做到本机检查即可。
1. 先理解项目、位置和权限
资源属于哪个项目?
Google Cloud 的行政层级是组织(organization)、可选的文件夹(folder)、项目(project),再到具体资源。组织通常代表公司或机构,文件夹用于分组和委派管理。上层 IAM 授权和组织政策会影响下层资源;项目不是天然与公司政策隔离的沙盒。个人练习不必先创建组织或文件夹。资源层级文档
项目集中管理资源、设置、权限和元数据。以下几个名称容易混淆:
结算账号决定谁付款,不决定谁有权调用 API。API 是程序操作云服务的接口;在项目中启用某个 API,只是使该服务可用,并没有授予任意调用者权限。
资源部署在哪里?
区域(region)是地理部署位置,可用区(zone)是区域内的部署分区。Compute Engine 虚拟机通常需要选可用区;本文 Cloud Run 服务选择区域。资源的范围由产品决定,不能一概而论。Google Cloud 概览
选择 REGION 时,同时考虑用户位置、数据驻留要求、依赖服务位置、产品可用性和区域价格。把数据库放到另一区域,可能增加延迟和数据传输费用;不要假设所有产品在所有区域都可用。
谁在操作?
IAM(Identity and Access Management,身份与访问管理)回答“谁能对哪个资源做什么”。主体(principal)可以是人、群组、服务账号或联合身份;角色(role)是一组权限;授权范围可以是项目,也可以是某个具体服务。项目级授权影响范围通常比单个服务大。
服务账号是应用使用的身份,不是一台服务器,也不是必须下载的 JSON 文件。人负责部署、构建系统负责制作镜像、应用负责运行,这三者应使用不同的权限。优先采用完成任务所需的预定义角色,不用 Owner 或 Editor 作为通用排错手段。Cloud Run 服务身份
2. 选择计算和数据产品
先按需要承担的运维工作选择计算服务。以下是起点,不是产品能力的完整比较;产品类别见 Google Cloud 概览。
容器镜像把应用和运行依赖打包;运行中的实例是镜像启动后的进程环境。“无状态”指一次请求的正确性不依赖某台实例里留下的文件或内存。本文应用只返回固定内容,适合 Cloud Run。
数据产品按数据模型选,不要因为都叫“存储”就互相替换:
本教程不添加数据库。先把部署和身份链路跑通,再根据真实的数据需求选择产品。
3. 把登录、应用凭据和运行身份分开
CLI(命令行界面)让你通过输入命令操作软件,gcloud 就是 Google Cloud 的命令行工具。身份验证证明“你是谁”,IAM 授权决定“你能做什么”。成功登录不等于有部署权限。Google 的应用默认凭据(ADC)文档明确区分 CLI 凭据与应用凭据。
ADC 是客户端库寻找凭据的约定:依次检查相应环境配置、本地 ADC 文件、附加服务账号的凭据。不要把“本地 ADC 登录成功”当成 CLI 当前账号已切换,也不要把 gcloud auth login 当成所有本地应用都能自动访问云端。
工作负载身份联合让外部程序用受信任身份提供方的身份换取短期凭据,再访问被授权的资源,或在授权范围内模拟服务账号。它减少长期密钥的维护和泄漏风险;面向人的 Workforce Identity Federation 是另一个概念。
不要把凭据 JSON、令牌或服务账号私钥写进源码、镜像或日志。这个练习不需要创建或下载服务账号密钥。
4. 在本机创建并检查应用
准备工具和独立目录
使用已安装的 Python 3.12、uv 和 curl。下面是 Bash/zsh 风格命令;Windows 可在 WSL 中使用,不要原样当成 PowerShell 语法执行。uv 的环境、锁文件和运行方式也见 Python 环境管理。
在新的空目录中开始,避免稍后把现有私有项目上传到云端:
mkdir cloud-run-hello
cd cloud-run-hello
uv init --bare --python 3.12
uv add 'flask~=3.0' 'gunicorn~=23.0'
Flask 负责 HTTP 路由,Gunicorn 是承接请求并运行 Python 应用的服务器。依赖约束参考 Google 的 Python 快速入门,不代表“最新版本”建议。保留 pyproject.toml 和 uv.lock;uv 管理 .venv,锁文件记录具体解析出的依赖版本。
创建 main.py:
from flask import Flask
app = Flask(__name__)
@app.get("/")
def hello():
return {"message": "Hello from Cloud Run", "version": "v1"}, 200
在第一个终端运行:
uv run gunicorn --bind 127.0.0.1:8080 main:app
main:app 指 main.py 中名为 app 的对象。第二个终端执行:
curl --fail --silent --show-error http://127.0.0.1:8080/
预期是 HTTP 200,以及包含 message 和 "version":"v1" 的 JSON;空格和键顺序不重要。连接被拒绝时,先看第一个终端是否还在运行、端口是否正确。这里的请求只到本机,不涉及云端账号。检查完按 Ctrl+C 停止服务器,后面代理也会使用 8080 端口。
明确容器如何构建
构建包(buildpacks)会识别应用语言,从源码构建容器镜像。这里创建 Dockerfile,让 Cloud Build 明确使用 uv 安装锁定依赖,而不依赖构建包是否认识 uv 项目:
FROM python:3.12-slim-trixie
COPY --from=ghcr.io/astral-sh/uv:0.12.13 /uv /uvx /bin/
WORKDIR /app
ENV UV_NO_DEV=1
COPY pyproject.toml uv.lock main.py ./
RUN uv sync --locked
ENV PATH="/app/.venv/bin:$PATH"
CMD ["sh", "-c", "exec gunicorn --bind 0.0.0.0:${PORT:-8080} main:app"]
镜像标签来自 uv 的 Docker 集成示例,并非对最新版的承诺。标签仍可能被重新指向;生产环境若需要更强的可复现性,应核验后固定镜像摘要。uv sync --locked 检查锁文件是否与项目一致,构建时不会悄悄改写锁文件。这个简单项目不声明打包后端。
Cloud Run 容器约定要求入口进程监听 0.0.0.0 和平台提供的 PORT。本机测试的 127.0.0.1 不能直接搬进云端容器。TLS 是加密 HTTPS 连接的协议,它在容器外终止,不需要给这个应用塞入证书;exec 让停止信号传给 Gunicorn。
容器可写文件系统占用实例内存且不持久,实例停止后不能依赖文件仍存在。上传文件应存到合适的持久存储。保留最小实例数也不能把本地目录变成持久磁盘。启动失败时先检查 Gunicorn 是否安装、端口监听是否正确,再看启动日志。
分别创建 .dockerignore 和 .gcloudignore,两份文件都写入:
.git/
.venv/
__pycache__/
*.pyc
.env
.env.*
*.pem
*.key
*credentials*.json
*service-account*.json
.gcloudignore 控制源代码上传,.dockerignore 控制 Docker 构建上下文;后者不能单独阻止源码上传。部署命令参考说明了源目录和忽略文件的处理。只在此目录保留教程文件,上传前检查文件清单;这些规则不是任意目录“没有秘密”的保证。不要排除 Dockerfile、main.py、pyproject.toml 或 uv.lock。
5. 决定是否进行付费云端步骤
需要一个专用于练习的现有项目、已关联且允许使用的结算账号、合适的区域,以及管理员协助启用 API 和授权。公司项目可能限制区域、构建来源、镜像仓库和服务账号使用,先确认政策,不要绕过它。
先设置费用提醒
在 Cloud Billing 中为练习项目设置预算、提醒阈值和能收到通知的联系人。普通提醒型预算只发通知,不会按金额自动停止服务。
截至 2026-09-12,Google 也提供可选的支出上限预算,预算入口将其标为预览功能。其范围是每份月度预算中的一个项目和一个符合条件的服务,支持范围包括 Cloud Run。它不是整个结算账号的精确账单上限:新用量可能被阻止,但进行中的请求、计费延迟和持续保留资源仍可能产生应付费用。不要假设限制 Cloud Run 就同时限制了 Cloud Build、镜像存储或数据库。
Cloud Run 价格文档区分区域、计费模式和额外费用。构建、Artifact Registry 镜像、相关存储、出站流量和额外网络资源可能另行收费;免费用量按结算账号汇总,而不是每建一个项目就重新获得一份。本教程不承诺全程免费,也不依赖试用额度。
后面的 --min=0 允许缩容到零,但不保证立即销毁空闲实例或完全没有费用。--max=2 是容量限制,不是金额上限。配额限制的是某类资源或请求用量,也不能代替预算管理。
安装 CLI 并选定上下文
按操作系统使用 Google Cloud CLI 官方安装说明。CLI 自身的 Python 环境与应用的 uv 环境是两回事。
先替换所有占位符,再执行以下云端步骤:
export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="YOUR_SUPPORTED_REGION"
export SERVICE="tutorial-hello"
export RUNTIME_SA_NAME="tutorial-hello-runtime"
export RUNTIME_SA_EMAIL="${RUNTIME_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud auth login
gcloud config set project "$PROJECT_ID"
gcloud init 也可以引导初始登录和配置,不必把它与上面的登录重复执行。CLI 配置只是默认参数,不是安全隔离边界。下面的资源操作仍显式指定项目和区域;有多个账号时,执行前在 CLI 或控制台确认当前身份。本文不需要 gcloud auth application-default login。
6. 由管理员准备身份和构建资源
从源码部署会经过 Cloud Build 构建镜像,再把镜像存入 Artifact Registry,最后创建 Cloud Run 修订版。修订版(revision)是服务的一份不可变部署配置。部署者、构建账号和运行账号的权限用途不同。从源码部署的角色要求
Google 管理的服务代理(service agents)负责平台内部操作,不能与构建账号或应用身份混为一谈,也不要为了排错随意改动它们的授权。查看日志可能还需要日志读取权限;最小部署角色不代表拥有全部运维权限。
以下设置由获授权的管理员执行。不要重复创建已存在的同名资源,也不要把教程命名当成资源确实独占的证明:
gcloud services enable run.googleapis.com cloudbuild.googleapis.com \
artifactregistry.googleapis.com --project "$PROJECT_ID"
gcloud iam service-accounts create "$RUNTIME_SA_NAME" \
--display-name="Tutorial hello runtime" --project "$PROJECT_ID"
在项目的 Cloud Build 设置中确认实际默认构建账号,再填写 BUILD_SA_EMAIL。当前源码部署文档描述的默认值是 Compute Engine 默认服务账号,但历史配置和组织政策可能不同,不能靠项目年代猜邮箱。本例使用项目实际默认构建身份;设置这个 shell 变量只用于下面的授权,不会替换默认构建身份。
export DEPLOYER_MEMBER="user:YOUR_EMAIL"
export BUILD_SA_EMAIL="YOUR_CONFIRMED_BUILD_SERVICE_ACCOUNT_EMAIL"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="$DEPLOYER_MEMBER" --role=roles/run.sourceDeveloper
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="$DEPLOYER_MEMBER" --role=roles/serviceusage.serviceUsageConsumer
gcloud iam service-accounts add-iam-policy-binding "$RUNTIME_SA_EMAIL" \
--project "$PROJECT_ID" --member="$DEPLOYER_MEMBER" \
--role=roles/iam.serviceAccountUser
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:${BUILD_SA_EMAIL}" --role=roles/run.builder
记录哪些授权是本次新增的。IAM 授权生效可能需要等待,先确认主体和范围正确再重试,不要直接改授 Editor。运行身份的 Service Account User 允许部署者使用该身份,不等于给应用本身赋予项目管理权。服务身份配置
源码部署可能创建或复用 Artifact Registry 仓库,并保留构建源码等辅助资源。让管理员确认仓库创建、构建和存储政策允许此流程;记录部署实际使用的仓库、镜像和源码位置,不假定自动仓库只属于这个示例。
7. 部署并通过身份验证调用
在 cloud-run-hello 目录中,由已获授权的部署者执行:
gcloud run deploy "$SERVICE" \
--source . \
--project "$PROJECT_ID" \
--region "$REGION" \
--service-account "$RUNTIME_SA_EMAIL" \
--port 8080 \
--min=0 \
--max=2 \
--no-allow-unauthenticated \
--invoker-iam-check
这一步上传源码并进行云端构建,可能计费。存在 Dockerfile 时,--source 使用它;没有时才使用 Google 构建包。--min、--max 是服务级设置,与修订版级的 --min-instances、--max-instances 不同。部署参数参考
两个访问参数分别拒绝匿名授权并启用调用者 IAM 检查。这里的“私有”是需要 IAM 身份验证,并不表示服务只有私有网络地址或限制了网络入口。显式设置访问策略可能需要超出最小源码部署角色的服务 IAM 管理权限;应由管理员授权或执行相关操作,不要以开放匿名访问解决权限不足。
构建失败时查看本次 Cloud Build 的构建日志和失败步骤:先确认锁文件、Dockerfile、API、实际构建身份及仓库权限。构建尚未成功,Cloud Run 运行日志中没有应用记录是正常的。组织政策拒绝也需要管理员处理。
服务成功创建后,由有服务 IAM 策略权限的管理员授予本次调用者访问权:
gcloud run services add-iam-policy-binding "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION" \
--member="$DEPLOYER_MEMBER" --role=roles/run.invoker
部署者身份执行 Cloud Run 本机代理:
gcloud run services proxy "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION"
保持代理运行,在另一终端请求:
curl --fail --silent --show-error http://127.0.0.1:8080/
预期仍是 v1 的 JSON。这次虽然地址是 localhost,请求会经代理到达 Google Cloud,使用当前账号身份并可能计费。先确认之前的本机 Gunicorn 已停止;端口被占用时不要误把旧的本机响应当成云端验证成功。
403 通常需要检查当前账号和该服务的 roles/run.invoker;部署阶段的 actAs 错误则要检查运行身份授权。不要添加 allUsers 来排错。生产环境的服务间调用应使用面向目标服务的 ID token,不应依赖开发者电脑上的代理或复制出来的令牌。
8. 查看日志、更新和回滚
gcloud run services logs read "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION" --limit=20
gcloud run revisions list --service "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION"
日志用于区分启动失败、应用异常和请求问题;不要记录凭据或个人请求内容。若没有查看权限,申请相应只读权限,不必扩大运行服务账号的权限。
服务持有 URL 和流量设置,修订版持有一次部署的配置。先在控制台或列表中确认并记下当前承接流量的 v1 修订版名称,不要假定列表第一行就是目标:
export PREVIOUS_REVISION="YOUR_VERIFIED_V1_REVISION"
把 main.py 中的 v1 改成 v2。停止代理,用第 4 节的 uv run 和本机 curl 再检查,确认响应为 v2,然后停止本机服务器。重新执行第 7 节完整部署命令。
通过控制台确认新修订版已经就绪,且准备把全部流量交给它后,显式切换到最新就绪修订版:
gcloud run services update-traffic "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION" \
--to-latest
不要与其他部署并行进行这个练习;--to-latest 指向最新就绪修订版,也会影响后续部署的流量行为。重启代理并调用,确认 v2。已有流量分配可能影响新部署,不应仅凭“部署成功”判断哪个版本正在接收请求。流量更新参考
要回到已确认的 v1,执行:
gcloud run services update-traffic "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION" \
--to-revisions="${PREVIOUS_REVISION}=100"
再次经代理请求,预期恢复 v1。这是流量回滚,不会撤销数据库迁移、删除新镜像或复原外部系统写入。以后继续部署时,再显式检查需要的流量目标。
9. 需要私有数据库时再增加网络配置
VPC(Virtual Private Cloud)提供虚拟网络、路由和防火墙等能力。Google Cloud 的 VPC 网络是全局资源,子网是区域资源。网络连通性和 IAM 是两道不同的检查:网络可达不等于有权读数据库,有权限也不保证有可用网络路径。
Cloud Run 的入口认证回答“谁能请求服务”;连接私有数据库要解决的是“服务如何向外访问数据库”。Direct VPC egress允许 Cloud Run 向 VPC 发送流量而无需 Serverless VPC Access 连接器。不要为了返回一句 JSON 就先创建连接器。
以下术语不能混用:
- Private Services Access通过分配地址范围和私有连接,让 VPC 访问受支持服务提供方网络中的资源;其连接使用 VPC Network Peering。
- Private Service Connect提供另一套私有服务访问机制,例如在使用方 VPC 中设置服务端点。
- Cloud Run 的 VPC 出站配置处理运行实例到 VPC 的路径,本身不是上述托管服务连接机制,也不提供数据库登录权限。
真正接入 Cloud SQL 时,应按所选实例和网络方式设计连接,再配置应用身份和数据库认证。跨项目 API 调用不普遍要求 VPC 对等连接,VPC 对等连接也不会授予 API 权限。本例保持无数据库、无额外 VPC 资源。
10. 只清理本次练习资源
先停止本机代理,核对项目 ID、区域和服务名,并在控制台确认目标确实属于本次练习。下面两条命令会删除资源;保留确认提示,不使用自动确认选项。
gcloud run services delete "$SERVICE" \
--project "$PROJECT_ID" --region "$REGION"
确认运行账号没有被其他服务使用后,才删除此服务账号:
gcloud iam service-accounts delete "$RUNTIME_SA_EMAIL" \
--project "$PROJECT_ID"
删除服务不会自动清除所有构建镜像、上传源码、日志和其他存储。根据之前记录的位置,在控制台逐项核对并删除仅属于本次练习且不再使用的镜像或源码对象。共享的自动仓库不能整库删除;有保留政策或不确定归属时先请管理员确认。
管理员可以移除本次新增且已不再需要的 IAM 绑定,但不能仅凭角色名称判断它是教程新增的。不要移除原有授权、删除默认构建账号、停用共享 API,也不要删除整个项目来代替资源核对。
最后查看计费明细和剩余资源。账单可能延迟显示,仍有费用时按构建、镜像、存储、日志、网络及其他依赖分别追查;服务请求数归零不足以证明全部费用已经停止。遇到配额或速率错误,则按错误中的项目、区域和配额项检查,不要无限重试或把配额理解为免费额度。
继续选择其他工具时,可回到工具与工作流。