Cloud Run 部署核心要点:端口暴露、健康检查与VPC连接 1. 项目概述为什么 Cloud Run 是 GCP 上最值得认真对待的无服务器容器平台Cloud Run 这个名字听起来像 Google Cloud PlatformGCP里又一个“跑在云上的运行时”但如果你真把它当成另一个 Kubernetes 管理控制台的简化版或者只是“函数即服务FaaS的换皮产品”那接下来的部署过程大概率会卡在第3步——不是报错而是你发现它“太安静了”安静到连日志都懒得吐一行安静到健康检查反复失败却找不到源头。我第一次用 Cloud Run 部署一个 Python FastAPI 应用时就栽在这“安静”上本地 curl 通、Cloud Build 构建成功、服务状态显示“Ready”但所有 HTTP 请求全部返回 503。查了47分钟最后发现只因为 Dockerfile 里少写了一行EXPOSE 8080而 Cloud Run 的默认端口探测逻辑根本不会告诉你“端口没暴露”它只会默默把流量拒之门外。这就是 Cloud Run 的真实底色它极度克制极度依赖约定极度不宽容任何偏离标准容器行为的微小偏差。它不是 Kubernetes 的替代品也不是 Cloud Functions 的升级版它是 GCP 在“容器即交付单元”这个理念上打磨出的一把精准手术刀。核心关键词非常明确Cloud Run、GCP、容器化应用、无服务器、自动扩缩、HTTP 触发、托管式 Knative。它解决的不是“怎么把代码跑起来”这种初级问题而是“如何让一个标准 Docker 镜像在无需管理任何基础设施的前提下获得毫秒级冷启动、按请求付费、自动弹性伸缩、内置 HTTPS 和全球 CDN 加速”的完整闭环。适合谁三类人最该立刻上手一是正在从 Heroku 或 Vercel 迁移中后台 API 的全栈开发者二是需要快速验证 SaaS 微服务架构的初创技术负责人三是运维团队里负责推动“容器标准化落地”的工程师——你们不用再为每个新服务搭一套 K8s Ingress 和 HPACloud Run 就是那个开箱即用的、符合 OCI 标准的答案。它背后的技术支点其实很硬核底层完全基于开源的 Knative Serving 项目但 Google 做了深度托管和工程优化。这意味着你写的 YAML 文件、配置的环境变量、设置的并发数最终都会被翻译成 Knative 的 Revision、Configuration 和 Route 对象再由 Google 的控制平面统一调度到全球边缘节点的容器运行时上。但它对用户完全隐藏了这些复杂性。你不需要懂 Istio 的 VirtualService 怎么写也不用研究 Knative 的 PodAutoscaler 指标采集逻辑。你只需要确保你的容器监听在PORT环境变量指定的端口默认8080能响应 HTTP GET/的健康检查并在 4 秒内完成启动。剩下的包括 TLS 终止、WAF 防护、地域就近路由、甚至 IPv6 支持全由 Google 托管。这种“契约式编程”带来的效率提升是颠覆性的我团队上个月用 Cloud Run 替换了三个老旧的 Compute Engine 实例承载的内部工具运维工单下降了 92%部署频率从每周一次变成每天平均 17 次而成本直接砍掉 63%——因为那些实例常年空转的 CPU 资源现在只在真实请求到来时才被计费。2. 核心设计思路与方案选型逻辑为什么不是 Cloud Functions、不是 App Engine、更不是裸 K8s2.1 为什么首选 Cloud Run 而非 Cloud Functions这个问题几乎每次内部技术评审都会被问到。答案不在功能列表里而在调用模型和生命周期约束上。Cloud Functions 是典型的事件驱动模型一个函数绑定到 Pub/Sub 主题、Cloud Storage 事件或 HTTP 路由执行完就销毁。它的最大限制是10 分钟超时和固定内存上限8GB。而 Cloud Run 的本质是“有状态的 HTTP 服务”它的容器进程可以长期存活只要不触发自动缩容策略。这意味着你可以轻松跑一个 WebSocket 服务、一个长连接的 gRPC 流式接口甚至一个需要预热缓存的机器学习推理服务。我去年帮一家电商客户做实时库存同步后端需要维持与 Redis 的长连接并监听多个 Pub/Sub 主题。用 Cloud Functions 实现就得写复杂的重连逻辑和状态恢复机制每次函数重启都要重新订阅换成 Cloud Run 后容器启动时一次性建立连接后续所有请求共享这个连接池代码量减少 60%延迟稳定性提升 4 倍。更重要的是资源模型差异。Cloud Functions 的内存和 CPU 是强绑定的比如 2GB 内存必然配 1vCPU而 Cloud Run 允许你独立配置CPU 分配比例100%、200%、400%和内存大小128MB 到 8GB。这让你能精准匹配工作负载一个纯 JSON 解析的 API 可以用 128MB 100% CPU而一个需要大量计算的图像处理服务则可设为 4GB 400% CPU。这种细粒度控制在 Cloud Functions 里根本不存在。另外Cloud Run 支持并发请求数concurrency设置这是应对突发流量的关键。你可以让单个容器实例同时处理 80 个请求而不是像 Cloud Functions 那样每个实例只能串行处理一个。实测下来当 QPS 从 50 突增到 300 时Cloud Run 的扩容延迟稳定在 1.2 秒内而同等配置的 Cloud Functions 平均冷启动时间飙升到 3.8 秒。2.2 为什么不是 App Engine Standard EnvironmentApp Engine 是 GCP 的老将但它的“标准环境”本质上是一个高度封装的运行时沙箱。你必须用它指定的语言版本Python 3.9、Node.js 18 等不能自由选择基础镜像不能安装系统级依赖比如ffmpeg或wkhtmltopdf甚至不能修改gunicorn的 worker 数量。而 Cloud Run 的核心哲学是“你提供容器我负责运行”。只要你能构建出一个符合 OCI 标准的镜像里面装什么、怎么装、用什么启动命令完全由你决定。我们有个内部报表服务需要调用 Java 编写的 PDF 渲染引擎还依赖特定版本的 Ghostscript。在 App Engine Standard 下这条路根本走不通但在 Cloud Run 里我们直接用openjdk:17-jre-slim作为基础镜像apt-get install ghostscript再把 Java JAR 包 COPY 进去整个流程不到 10 分钟。这种自由度带来的不仅是技术可行性更是团队协作效率——后端用 Go 写 API前端用 Node.js 写 SSR 渲染层运维用 Python 写监控脚本所有服务都用同一套 Cloud Run 部署流程CI/CD 流水线复用率高达 85%。2.3 为什么坚决不选自建 KubernetesGKEGKE 当然强大但它的复杂度是指数级的。要让一个简单 Web 服务上线你需要创建集群考虑节点池类型、规模、自动扩缩配置、配置 Ingress 控制器Nginx 或 Istio、编写 Deployment/YAML、设置 HorizontalPodAutoscaler、配置 Service 和 Endpoints、申请 Lets Encrypt 证书并绑定到 Ingress……这一套操作资深 SRE 也要花至少 2 小时。而 Cloud Run 的等效操作是gcloud run deploy --image gcr.io/my-project/my-app --platform managed --region us-central1回车等待 90 秒。它把所有这些抽象成了一个命令。但这不是偷懒而是工程权衡。我们做过测算一个 5 人研发团队如果所有后端服务都跑在 GKE 上每月平均要消耗 37 人时在集群维护、证书轮换、网络策略调试、HPA 参数调优上换成 Cloud Run 后这部分时间归零工程师可以专注在业务逻辑迭代上。当然GKE 仍有不可替代的场景需要 GPU 加速的训练任务、必须使用 StatefulSet 管理有状态数据库、或者要求精细的网络策略隔离。但对于占团队服务总数 78% 的无状态 HTTP APICloud Run 是更优解——它把“基础设施即代码”的理念推进到了“基础设施即参数”的阶段。2.4 方案选型决策树一张表帮你快速判断当你面对一个新项目时不必纠结直接对照这张实战总结的决策表评估维度Cloud Run 最佳匹配场景Cloud Functions 更合适场景GKE 必须选择场景启动时间敏感度要求 1 秒冷启动如用户交互 API可接受 1-3 秒冷启动如后台批处理启动时间非首要考量如数据管道执行时长需要 10 分钟持续运行如流式处理单次执行 10 分钟如文件转码无限制如长期运行的守护进程依赖自由度需要自定义系统包、二进制、GPU 驱动仅需语言运行时标准库需要内核模块、特殊硬件访问网络模型标准 HTTP/HTTPS 流量事件驱动Pub/Sub、Storage复杂网络拓扑多子网、VPC 对等团队运维能力希望零基础设施管理接受轻量级事件抽象拥有专职 SRE 团队成本结构偏好按请求毫秒计费空闲零成本按执行时间内存计费按节点小时计费存在固定成本这张表不是教条而是我们踩过坑后提炼的血泪经验。比如第三行“依赖自由度”曾让我们在一个金融风控服务上栽过大跟头客户要求集成某国产加密 SDK必须编译进 C 二进制。我们最初想用 Cloud Functions 的 Custom Runtime结果发现其构建环境不支持交叉编译折腾三天无果切换到 Cloud RunDockerfile 里加两行RUN apt-get update apt-get install -y build-essential15 分钟搞定。所以选型的本质是看你的瓶颈在哪里——是开发速度运维负担还是技术限制Cloud Run 的价值永远体现在它帮你消除了那个最痛的瓶颈。3. 核心细节解析与实操要点从 Dockerfile 到服务配置的 12 个生死关卡3.1 Dockerfile 的黄金三原则暴露端口、监听正确、健康检查就绪Cloud Run 对容器镜像的要求表面宽松实则苛刻。它不校验你的镜像是否符合 Docker 最佳实践但会在运行时用最严苛的方式测试它。我见过太多团队因为忽略以下三点在部署后陷入“服务 Ready 但无法访问”的诡异状态。第一原则必须显式 EXPOSE 端口。很多人以为只要容器进程监听在 8080Cloud Run 就能自动发现。错。Cloud Run 的健康检查探针liveness probe会向容器发送 HTTP GET 请求目标端口就是PORT环境变量的值默认 8080。但如果 Dockerfile 里没有EXPOSE 8080Kubernetes 层面的端口映射规则就不会生效探针请求根本无法到达容器内部。这不是 Bug而是 OCI 标准的强制约定。解决方案极其简单在 Dockerfile 的最后添加EXPOSE ${PORT:-8080}。注意这里用了 Shell 变量展开语法确保即使你后续通过--set-env-varsPORT3000覆盖环境变量EXPOSE 也能同步更新。第二原则进程必须监听0.0.0.0:${PORT}而非127.0.0.1:${PORT}。这是新手最容易犯的错误。本地开发时为了安全我们习惯让 Web 服务器只绑定到 localhost但容器里没有“本地”概念127.0.0.1指向的是容器自己的 loopback 接口外部流量包括 Cloud Run 的探针根本无法穿透。必须改成0.0.0.0。以 Python Flask 为例启动命令不能是flask run --host127.0.0.1 --port8080而必须是flask run --host0.0.0.0 --port$PORT。Node.js 的 Express 同理app.listen(process.env.PORT || 8080, 0.0.0.0)。这个0.0.0.0字符串是 Cloud Run 容器的生命线。第三原则健康检查路径/必须在 4 秒内返回 2xx。Cloud Run 默认用 HTTP GET/作为存活探针。很多框架如 Django默认根路径是 404或者需要加载大量配置才能响应。你必须显式提供一个轻量级的健康检查端点。最佳实践是在应用启动时立即启动一个最小化的 HTTP 服务器只响应/healthz并在主应用初始化完成后将/也指向健康检查。例如Go 语言中http.HandleFunc(/healthz, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(ok)) }) // 主应用启动后 http.HandleFunc(/, func(w http.ResponseWriter, r *http.Request) { // 代理到主应用或直接返回健康状态 http.Redirect(w, r, /healthz, http.StatusTemporaryRedirect) })这样探针在应用启动的任何阶段都能得到响应避免因初始化耗时过长导致实例被反复杀死。提示不要试图禁用健康检查Cloud Run 不提供--disable-health-check选项。这是它的安全基线绕过等于放弃服务稳定性。3.2 环境变量与密钥管理为什么 secrets manager 比 .env 文件更安全在本地开发.env文件方便快捷但在生产环境硬编码密钥到镜像或环境变量里是重大安全风险。Cloud Run 原生集成了 Google Secret Manager这是必须掌握的核心能力。关键在于理解它的注入机制Secret Manager 中的 secret 版本version会被挂载为容器内的文件/secrets/my-db-password而不是环境变量。为什么因为环境变量可能被进程意外泄露如通过/proc/pid/environ而文件权限可以精确控制为0400仅 owner 可读。实操步骤分三步创建 secret 并添加版本echo -n my-super-secret-password | gcloud secrets create my-db-password --replication-policyautomatic gcloud secrets versions add my-db-password --data-file-在部署时绑定 secret 到服务gcloud run deploy my-api \ --imagegcr.io/my-project/my-api \ --set-secrets/secrets/db-passwordmy-db-password:latest \ --platform managed \ --region us-central1这里的--set-secrets参数格式是MOUNT_PATHSECRET_NAME:VERSION。latest表示最新版本你也可以指定1,2等具体版本号便于灰度发布。在应用代码中读取# Python 示例 with open(/secrets/db-password, r) as f: password f.read().strip() # 构建数据库连接字符串 db_url fpostgresql://user:{password}host:5432/db这种方法的优势是双重的一是密钥永远不会出现在 CI/CD 日志或容器镜像层中二是密钥轮换时只需在 Secret Manager 中创建新版本然后更新服务绑定gcloud run services update ... --set-secrets...应用无需重启即可获取新密钥。我们有个客户曾因数据库密码泄露导致数据被爬取迁移至 Secret Manager 后密钥轮换时间从 4 小时缩短到 47 秒且全程零停机。注意Secret Manager 的访问权限必须显式授予 Cloud Run 服务账号。默认情况下服务账号只有run.invoker权限没有secretmanager.versions.access。你需要额外执行gcloud projects add-iam-policy-binding my-project \ --memberserviceAccount:my-apimy-project.iam.gserviceaccount.com \ --roleroles/secretmanager.secretAccessor3.3 自动扩缩与并发控制如何用 3 个参数驯服流量洪峰Cloud Run 的自动扩缩能力是其灵魂但若配置不当可能从“救星”变成“噩梦”。核心参数只有三个但组合起来威力巨大--min-instances,--max-instances,--concurrency。--concurrency单实例并发请求数这是最关键的参数决定了单个容器实例能同时处理多少请求。默认值是 80但并非越大越好。对于 I/O 密集型服务如调用外部 API提高并发能充分利用等待时间80 是合理值但对于 CPU 密集型服务如图像压缩高并发会导致 CPU 争抢响应延迟飙升。我们实测过一个 FFmpeg 转码服务--concurrency1时单请求耗时 1200ms--concurrency4时单请求耗时反而升到 1800ms因为 4 个 FFmpeg 进程在 1vCPU 上疯狂抢占。最终我们设为--concurrency2平衡了吞吐和延迟。--min-instances最小实例数这是对抗冷启动的终极武器。设为1意味着服务永远有一个实例处于 Warm 状态首次请求延迟从 1-2 秒降到 50ms 以内。但代价是固定成本——即使零请求你也为这 1 个实例付费。我们的经验法则是对核心业务 API如登录、支付必须设--min-instances1对低频内部工具如日志查询保持0即可。--max-instances最大实例数这是防止费用失控的安全阀。不设上限当遭遇 DDoS 或流量误配置时实例数可能瞬间飙到数千账单一夜暴增。我们给所有服务都设置了硬性上限计算公式是最大实例数 (峰值 QPS × 平均请求耗时秒数) / 并发数。例如一个 API 峰值 QPS 为 1000平均耗时 0.5 秒并发设为 10则1000 × 0.5 / 10 50所以--max-instances50。这个数字要留 20% 余量最终设为 60。这三个参数的协同效果可以用一个真实案例说明我们部署了一个实时聊天消息推送服务使用 WebSocket。初始配置是默认值min0, maxunlimited, concurrency80结果在凌晨 3 点用户量低谷时实例数归零早上 8 点通勤高峰瞬间涌入 5000 连接Cloud Run 在 3 秒内拉起 200 个实例但每个实例的 WebSocket 连接数超过 100导致内存溢出崩溃形成雪崩。修复方案是--min-instances5保证基础连接池--concurrency100WebSocket 是 I/O 密集高并发友好--max-instances150根据历史峰值计算。调整后服务在 7x24 小时内保持 99.99% 可用性。3.4 网络与安全配置VPC 连接器、防火墙与 IAM 的铁三角Cloud Run 默认运行在 Google 的共享 VPC 中这意味着你的容器可以直接访问公网但无法访问私有 VPC 内的资源如 Cloud SQL、Redis 实例。要打通这条链路必须配置Serverless VPC Access Connector。配置过程看似简单但有三个致命陷阱Connector 必须与 Cloud Run 服务在同一区域。比如你的服务部署在us-central1那么 VPC Connector 也必须创建在us-central1。跨区连接会失败且错误信息极其模糊Failed to connect to VPC排查起来非常痛苦。Connector 的 IP 地址范围不能与目标 VPC 的 CIDR 冲突。VPC Connector 本身需要一个独立的/28子网16 个 IP。如果你的目标 VPC 是10.0.0.0/16那么 Connector 子网就不能设为10.0.0.0/28否则路由冲突。我们推荐使用10.128.0.0/28这样的“隔离网段”。Cloud SQL 的授权必须包含 Connector 的 IP 段。很多人配置完 Connector却发现连不上 Cloud SQL。原因在于 Cloud SQL 的授权列表里只加了0.0.0.0/0不安全或具体的 Compute Engine IP却忘了添加 Connector 子网。正确做法是在 Cloud SQL 实例的“连接”设置中添加一条授权网络CIDR 为 Connector 子网如10.128.0.0/28。一旦 VPC 连接器配置完成你就可以在部署时启用它gcloud run deploy my-api \ --imagegcr.io/my-project/my-api \ --vpc-connectormy-vpc-connector \ --vpc-egressall-traffic \ --platform managed \ --region us-central1其中--vpc-egressall-traffic表示所有出站流量包括访问公网都经过 VPC Connector。如果你只想让流量访问私有 VPC而公网流量直连应设为private-ranges-only。安全的最后一环是 IAM。Cloud Run 服务默认有一个服务账号service-nameproject-id.iam.gserviceaccount.com它拥有run.invoker角色允许被 HTTP 请求调用。但如果你的服务需要访问其他 GCP 服务如 Pub/Sub、BigQuery就必须给这个服务账号授予对应角色。切记不要给服务账号授予editor或owner这种宽泛角色。最小权限原则在这里是铁律。例如一个只读取 Pub/Sub 消息的服务只需roles/pubsub.subscriber一个只写入 BigQuery 的服务只需roles/bigquery.dataEditor。我们曾因误授editor角色导致一个被黑的前端组件获得了删除整个 Cloud Storage 存储桶的权限损失惨重。4. 实操过程与核心环节实现从零开始部署一个生产级 FastAPI 服务4.1 准备工作项目结构、依赖与 GCP 权限梳理我们以一个真实的内部工具——“员工假期余额查询 API”为例完整走一遍部署流程。这个 API 需要连接 Cloud SQLPostgreSQL查询员工数据并通过 Secret Manager 获取数据库密码。项目结构如下vacation-api/ ├── main.py # FastAPI 主应用 ├── requirements.txt # Python 依赖 ├── Dockerfile # 构建镜像 ├── cloudbuild.yaml # Cloud Build 配置可选 └── README.md第一步确认 GCP 项目权限。这不是可选步骤而是前置条件。你需要确保当前 gcloud CLI 登录的账号拥有以下权限roles/run.admin部署 Cloud Run 服务roles/storage.objectAdmin上传构建镜像到 Container Registryroles/secretmanager.secretAccessor访问 Secret Managerroles/cloudsql.client连接 Cloud SQL最简单的方式是在 GCP Console 的 IAM 页面为你的账号添加Project Editor角色仅用于开发环境。生产环境则必须拆分为上述最小权限角色。第二步创建必要的 GCP 资源。打开 Cloud Console依次创建Cloud SQL 实例选择 PostgreSQL版本 14区域us-central1实例 IDvacation-db。记住生成的密码。Secret Manager Secret创建名为vacation-db-password的 secret添加版本值为上一步的数据库密码。VPC Connector在VPC network Serverless VPC Access中创建 connectorvacation-connector区域us-central1子网10.128.0.0/28。Cloud SQL 授权进入vacation-db实例的“连接”设置添加授权网络10.128.0.0/28。这四步做完基础设施层就准备好了。整个过程约 8 分钟比手动配置一台虚拟机快得多。4.2 编写生产就绪的 FastAPI 应用与 Dockerfilemain.py的核心逻辑必须包含健康检查、数据库连接和错误处理from fastapi import FastAPI, HTTPException, Depends from sqlalchemy import create_engine, text from sqlalchemy.exc import SQLAlchemyError import os app FastAPI(titleVacation Balance API) # 从 Secret Manager 读取密码 def get_db_password(): try: with open(/secrets/vacation-db-password, r) as f: return f.read().strip() except FileNotFoundError: raise RuntimeError(Database password not found in secrets) # 创建数据库引擎使用连接池 DB_PASSWORD get_db_password() DB_URL fpostgresql://vacation_user:{DB_PASSWORD}10.128.0.2:5432/vacation_db engine create_engine(DB_URL, pool_size5, max_overflow10) app.get(/healthz) def health_check(): 轻量级健康检查不依赖数据库 return {status: ok, service: vacation-api} app.get(/vacation/{employee_id}) def get_vacation_balance(employee_id: str): 查询员工假期余额 try: with engine.connect() as conn: result conn.execute(text(SELECT balance FROM employees WHERE id :id), {id: employee_id}) row result.fetchone() if row is None: raise HTTPException(status_code404, detailEmployee not found) return {employee_id: employee_id, balance_days: row[0]} except SQLAlchemyError as e: raise HTTPException(status_code500, detailfDatabase error: {str(e)})requirements.txt只保留最小依赖fastapi0.104.1 sqlalchemy2.0.23 psycopg2-binary2.9.7 uvicorn0.24.0Dockerfile是成败关键必须严格遵循前述三原则# 使用官方 Python slim 镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件并安装利用 Docker layer cache COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY main.py . # 关键暴露端口必须 EXPOSE ${PORT:-8080} # 关键设置启动命令监听 0.0.0.0 CMD exec uvicorn main:app --host 0.0.0.0:$PORT --port $PORT --workers 4 --proxy-headers --forwarded-allow-ips*注意--proxy-headers和--forwarded-allow-ips*这两个参数。Cloud Run 会在请求头中添加X-Forwarded-For等字段Uvicorn 默认不信任这些头会导致request.client.host返回127.0.0.1。加上这两个参数Uvicorn 才能正确解析真实客户端 IP。4.3 构建、推送与部署一条命令完成全流程现在进入最激动人心的环节。我们有两种方式构建镜像本地docker builddocker push或使用 Google Cloud Build推荐更安全、可审计。方式一本地构建适合快速验证# 构建镜像tag 为 GCR 地址 docker build -t gcr.io/my-project/vacation-api . # 推送镜像到 Google Container Registry docker push gcr.io/my-project/vacation-api # 部署到 Cloud Run gcloud run deploy vacation-api \ --imagegcr.io/my-project/vacation-api \ --platformmanaged \ --regionus-central1 \ --allow-unauthenticated \ --min-instances1 \ --max-instances10 \ --concurrency80 \ --cpu1 \ --memory512Mi \ --set-secrets/secrets/vacation-db-passwordvacation-db-password:latest \ --vpc-connectorvacation-connector \ --vpc-egressprivate-ranges-only方式二Cloud Build生产环境首选创建cloudbuild.yamlsteps: - name: gcr.io/cloud-builders/docker args: [build, -t, gcr.io/$PROJECT_ID/vacation-api, .] - name: gcr.io/cloud-builders/docker args: [push, gcr.io/$PROJECT_ID/vacation-api] images: - gcr.io/$PROJECT_ID/vacation-api然后触发构建gcloud builds submit --configcloudbuild.yaml构建成功后再执行gcloud run deploy命令去掉--image参数Cloud Build 会自动推送到 GCR。部署命令中的每个参数都有深意--allow-unauthenticated允许公开访问。如果这是内部 API应改为--no-allow-unauthenticated然后通过 IAM 授予特定用户roles/run.invoker。--cpu1和--memory512Mi根据应用实际需求设置。FastAPI SQLAlchemy 的轻量 API1vCPU 512MB 内存足够。--vpc-egressprivate-ranges-only因为我们只需要访问 Cloud SQL私有 IP10.128.0.2不需要走公网所以设为private-ranges-only更安全、更省钱。执行gcloud run deploy后你会看到类似这样的输出Deploying container to Cloud Run service [vacation-api] in project [my-project] region [us-central1] ✓ Deploying new service... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting up SSL... Done. Service URL: https://vacation-api-abcdef-uc.a.run.app这个https://vacation-api-abcdef-uc.a.run.app就是你的服务地址。Cloud Run 已自动为你申请了免费的 Google 管理的 TLS 证书并配置了全球 CDN。4.4 验证与监控用 curl 和 Cloud Logging 快速定位问题部署完成后别急着庆祝立即验证四个关键点健康检查端点curl -I https://vacation-api-abcdef-uc.a.run.app/healthz # 应返回 HTTP/2 200 OK数据库连接curl https://vacation-api-abcdef-uc.a.run.app/vacation/EMP-001 # 应返回 JSON{employee_id:EMP-001,balance_days:15}冷启动性能time curl -o /dev/null -s -w %{http_code}\n https://vacation-api-abcdef-uc.a.run.app/healthz # 第一次执行冷启动应在 1.5 秒内第二次热实例应在 50ms 内日志查看 打开 Cloud Console Logging Logs Explorer输入查询resource.typecloud_run_revision resource.labels.service_namevacation-api你可以看到每条请求的详细日志包括响应时间、状态码、错误堆栈。如果出现 500 错误日志里会清晰显示Database error: ...而不是像传统服务器那样需要 SSH 登录查journalctl。监控方面Cloud Run 内置了丰富的指标。在 Cloud Console Cloud Run 你的服务 “监控”标签页重点关注请求计数Requests count确认流量是否正常接入。实例数Instance count观察扩缩是否符合预期如流量激增时是否及时扩容。CPU 使用率CPU utilization如果长期高于 80%说明需要增加 CPU 或优化代码。内存用量Memory usage如果接近上限512Mi考虑增加内存或检查内存泄漏。我们曾在一个服务中发现内存用量缓慢爬升从 200Mi 一周内涨到 480Mi。通过 Cloud Logging 查看日志发现是 SQLAlchemy 的连接池未正确关闭导致连接对象堆积。修复后内存稳定在 180Mi。5. 常见问题与排查技巧实录那些文档里不会写的“血泪教训”5.1 问题速查表高频故障与一键修复方案问题现象根本原因诊断命令修复方案我的实操心得服务状态 Ready但所有请求返回 503Dockerfile