+86 189 8218 1436 周一至五 10:00-22:00

使用 GCP Cloud Run 入门与运维实战

TL;DR:用小白能懂的方式讲清使用 GCP Cloud Run 部署容器、自动扩缩容、日志排障与成本控制,并对比 VM、GKE 的选择场景。

它是什么:把容器变成可访问服务

把 使用 GCP Cloud Run 想成「交一个盒子,换一个网址」。它是 GCP 的托管式无服务器容器平台:上传容器镜像后,平台直接给你一个 https://xxx.run.app,立刻对外提供 HTTP / HTTPS 服务,不用自己配 Nginx。

对小白最友好的是「不用管机器」。传统方式得先创建 VM、装系统、打补丁、维护 SSH 登录;Cloud Run 全包了:启动实例、路由请求、按流量扩缩容都由平台做。没人访问就缩到 0(不计费),流量来了自动加。你只管盒子里的程序能不能跑。

先拿官方镜像感受一步到位,复制执行即可(需已装 gcloud 并登录、选好项目):

gcloud run deploy my-first-run 
  --image=gcr.io/cloudrun/hello 
  --region=asia-east1 
  --allow-unauthenticated

为什么这么写:--image 用现成镜像,省去本地打包;--region 选近节点延迟低;--allow-unauthenticated 允许浏览器直接访问。最常见的坑就是漏了最后一项,打开网址看到 403 以为挂了,其实只是没开公开权限。跑完终端会打印 Service URL,点开即见欢迎页。

换自己的镜像前,再记三个坑:

  • 要监听 0.0.0.0,别只听 127.0.0.1,否则健康检查连不上。
  • 端口读环境变量 PORT,别在代码里写死 8080,平台会动态注入。
  • 缩到 0 后首次访问有 1~2 秒冷启动,不是报错,刷新即可。

它最适合接口、网站后端、回调接口、轻量任务和内部工具:来个请求、算一下、回个结果,不必常驻一台服务器。一句话收尾:重点是把容器写好,而不是把服务器管好。

解决什么问题:少运维、快上线、按量付费

没碰过服务器,可以先这样理解:使用 GCP Cloud Run 是把“养一台机器”换成“交一份能跑的容器”。传统 VM 上线,小接口也要选 CPU/内存机型、配防火墙、SSH 装 Python/Node/Nginx、配开机启动和监控、定期打补丁。代码写完了,新手却常卡在 Linux 权限、路径、端口没放行、服务没启动。

Cloud Run 把这些托管掉:给镜像后,平台自动处理容量、负载均衡、HTTPS 证书和版本发布(每次部署生成 Revision);没请求可缩到零,减少闲置 VM 成本。计费通常看请求数和容器运行时间,也就是按量付费。坑是镜像、日志、网络仍可能有少量费用,缩零后首次访问会冷启动,别以为绝对 0 元或永远毫秒响应。

最短路径先用 Cloud Shell 跑官方镜像,不用本机装环境。第一次用先单独复制这一行确认账号和项目;已确认可跳过,再复制第二段。很多“没权限”“找不到服务”其实是项目选错。

gcloud init

第二段里,启用 API 是打开 Cloud Run;部署示例镜像是先跑通路径,不急着写 Dockerfile;打印网址和 Revision 列表,是让你看到“发版留下版本”。区域固定 asia-east1,后面查询要一致。

gcloud services enable run.googleapis.com
gcloud run deploy hello-run --image gcr.io/cloudrun/hello --region asia-east1 --allow-unauthenticated
gcloud run services describe hello-run --region asia-east1 --format='value(status.url)'
gcloud run revisions list --service hello-run --region asia-east1

网址能打开就算上线。之后团队主要把时间放在代码、镜像、环境变量和权限上:配置别写死;密码别放普通环境变量,生产用 Secret Manager;–allow-unauthenticated 只给公开网页,内部接口收紧 IAM。每次 deploy 生成新 Revision,出问题把流量切回旧版本即可,回滚比登录 VM 改文件安全,也更容易,上线路径更短。

  • 常见坑:没开结算、API 未启用、项目选错,都会让 deploy 失败;先查项目和账单。
  • 别乱改区域:一会儿 asia-east1、一会儿别的区域,会以为服务丢了;公开访问也别照抄到内部接口。

怎么上手:从镜像到第一个网址

第一次使用 GCP Cloud Run,建议在控制台右上角打开 Cloud Shell:自带谷歌云命令行工具,先确认已选项目并开通结算。下面做一个最小网页服务:读取 PORT,没给就监听 8080,并绑定 0.0.0.0;静态页打进镜像,生成的镜像在本地也能启动。先确认服务不依赖固定本机磁盘:Cloud Run 实例随时重建,坑:别写死 127.0.0.1,也别把上传文件、SQLite 长期放本机盘,要放 Cloud Storage 或数据库。

mkdir -p cloud-run-hello
cd cloud-run-hello
PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
test -n "$PROJECT_ID" || { echo "请先在控制台选好项目"; exit 1; }
REGION=asia-east1
REPO=run-demo
IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/hello:1"
gcloud services enable run.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com --quiet
gcloud artifacts repositories describe "$REPO" --location="$REGION" >/dev/null 2>&1 || gcloud artifacts repositories create "$REPO" --repository-format=docker --location="$REGION" --description="Cloud Run demo" --quiet
cat > Dockerfile <<'EOF'
FROM python:3.12-slim
WORKDIR /app
RUN echo "你好,Cloud Run" > index.html
CMD ["sh", "-c", "python -m http.server ${PORT:-8080} --bind 0.0.0.0"]
EOF
gcloud builds submit --tag "$IMAGE" --quiet

这几步分别启用 Cloud Run、Artifact Registry、Cloud Build;构建命令把 Dockerfile 变成镜像并推到仓库,Cloud Run 只负责跑镜像。仓库已存在会跳过创建,重复复制也不容易报错。控制台同理:搜索这三个服务点“启用”,建 Docker 仓库再选镜像。

PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
REGION=asia-east1
IMAGE="$REGION-docker.pkg.dev/$PROJECT_ID/run-demo/hello:1"
gcloud run deploy hello-run --image "$IMAGE" --region "$REGION" --allow-unauthenticated --set-env-vars "WELCOME=你好" --min-instances 0 --max-instances 3 --port 8080 --memory 512Mi --quiet
URL=$(gcloud run services describe hello-run --region "$REGION" --format='value(status.url)')
echo "$URL"
curl -fsS "$URL"

区域选离用户近的;--allow-unauthenticated 让第一个网址能直接打开,内部系统别公开,改用 IAM 给账号 Cloud Run Invoker。环境变量用来放配置,别写死进镜像;最小实例 0 省钱但会冷启动,最大实例 3 给账单加护栏。看到“你好,Cloud Run”就成功了。

REGION=asia-east1
gcloud run revisions list --service hello-run --region "$REGION"
gcloud logging read 'resource.type="cloud_run_revision" AND resource.labels.service_name="hello-run"' --limit 20 --format='table(timestamp, severity, textPayload)'

服务页“指标”看请求数、延迟、实例数;“修订版本”看每次配置变更,可回滚。出错按这个顺序查:

  • 启动失败:日志里看命令、缺文件、镜像架构;
  • 端口:是否读 PORT、是否绑 0.0.0.0;
  • 权限:403 多半是没允许未认证或 IAM 没配;
  • 内存:突然退出、OOM 就调高 --memory 或优化程序。

与替代方案对比:什么时候不用 Cloud Run

选工具别只看「哪个最火」,得看你的活儿合不合它的脾气。Cloud Run 最大的好处是不用管服务器,但这份省心是用「交出控制权」换来的。下面拿几个常见替补比一比,帮你提前避坑。

和 Compute Engine(VM)比:VM 像自己租了台电脑,Cloud Run 像住酒店。酒店有人天天打扫(自动扩缩、自动打补丁),但你不能砸墙、也不能配把钥匙进机房。所以要跑常驻进程、习惯 SSH 进去敲两行排障、或者要装特定驱动 / 自定义内核,就别用 Cloud Run——它压根没有一台能登进去的机器。小白最常踩的坑,是代码里写了个 while True 做后台轮询,以为会一直跑;结果请求一结束实例就被「冻住」,任务直接断在半截。想直观感受差别,直接复制这两行跑一下(没开过资源也没关系,只会返回空列表,不会报错):

gcloud logging read "resource.type=cloud_run_revision" --limit=5
gcloud compute instances list

第一行是 Cloud Run 唯一的「排障窗口」:只能翻日志,进不去;第二行列出来的,才是能 gcloud compute ssh 进去随便改的机器。如果你看完第一行第一反应是「我还是想进去看一眼配置文件」,别硬扛,老老实实开 VM。

和 GKE 比:Cloud Run 学习成本几乎为零,不用懂 Pod、Ingress、节点池,运维活儿全交给谷歌;代价是网络被「包」住了。一旦要做复杂 VPC 网络、十几个服务互相编排、灰度流量精细切分,或者吃满 GPU / 追求低延迟高性能,就该上 GKE。别为了图省事把一整套微服务硬塞进 Cloud Run,后期调网络反而更痛。

和 App Engine / Cloud Functions 比:那俩是「传个代码文件就跑」,Cloud Run 则更依赖容器,必须先自己打镜像。听着多一步,但换来了自由:冷门语言、系统级依赖、锁死某个运行时版本,全由你说了算。反过来,若只是写个定时发邮件的小脚本、连 Dockerfile 都不想碰,Cloud Functions 更香。

最后,这几类业务先别急着上线,务必提前评估限制:

  • 长连接:WebSocket 能跑,但别指望几天不中断,实例会被回收重拉;
  • 后台守护进程:默认只在处理请求时才给 CPU,想一直跑得额外改配置加钱,真不如直接 VM;
  • 固定出口地址:默认出口 IP 是动态的,得补配 VPC 连接器 + Cloud NAT,对方白名单卡得严时最容易翻车;
  • 本地持久化磁盘:文件系统是临时的,重启就清空。文件请扔 Cloud Storage,数据请接 Cloud SQL。

记一句人话就够了:「来一个请求、算完就走、状态全放外部」,符合就闭眼用 Cloud Run;凡是想「一直开着、进去改、存本地」,趁早换替补,别跟平台较劲。

最佳实践:小白也能稳稳上线

使用 GCP Cloud Run,把应用想成“随时开箱、随时回收的小盒子”。镜像只放运行时依赖,别塞编译工具、缓存、.env;启动越慢,冷启动越明显。日志用 print、console.log 打到标准输出,Cloud Run 会送进 Cloud Logging。坑:写本地文件,实例一重启就没了。

密钥放 Secret Manager,再用引用注入;数据库密码、API Key 别写进源码或 Dockerfile。坑:删了代码,旧镜像层仍能翻出来,必须轮换。服务单独用最小权限账号,只给读取当前密钥的权限;要连库再加角色,别给 Owner/Editor,泄露会拖累整个项目。发布前把当前项目切到生产项目;开发、预发、生产各拆项目,环境变量和权限变更写进 Git 或工单,配合审计日志回看。除非做公开网站,别开“允许未认证访问”,也别给 allUsers 调用权限;命令会禁止公网直连并打印核对结果。

生产先保守:并发 80、超时 30 秒、内存 512Mi、最小实例 1。并发太高易 OOM,最小实例减冷启动但一直计费;再到 Cloud Monitoring 配 5xx、延迟、启动耗时告警。发布别一把梭:先发不接流量的新版本,再 10%、50%、100% 切流,每步看错误率。每周看账单、请求数、5xx、容器启动时间,设 50%、90% 预算提醒;低流量别把最小实例开太大。

在同一终端复制第一段:它沿用当前项目(没设置才选第一个,别当生产兜底),建专用账号和示例密钥,带生产参数部署,先切 10%,再读 20 条日志。

PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "(unset)" ]; then PROJECT_ID=$(gcloud projects list --format='value(projectId)' | head -n1); gcloud config set project "$PROJECT_ID" --quiet; fi
REGION=asia-east1
SERVICE=hello-run
SA_NAME=run-minimal
SA="$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"
gcloud services enable run.googleapis.com secretmanager.googleapis.com iam.googleapis.com --project="$PROJECT_ID" --quiet
sleep 10
gcloud iam service-accounts describe "$SA" --project="$PROJECT_ID" >/dev/null 2>&1 || gcloud iam service-accounts create "$SA_NAME" --display-name="Cloud Run minimal" --project="$PROJECT_ID" --quiet
gcloud secrets describe app-secret --project="$PROJECT_ID" >/dev/null 2>&1 || printf 'demo-secret-value' | gcloud secrets create app-secret --data-file=- --replication-policy=automatic --project="$PROJECT_ID" --quiet
gcloud secrets add-iam-policy-binding app-secret --member="serviceAccount:$SA" --role="roles/secretmanager.secretAccessor" --project="$PROJECT_ID" >/dev/null
gcloud run deploy "$SERVICE" --image=gcr.io/cloudrun/hello --region="$REGION" --project="$PROJECT_ID" --service-account="$SA" --set-secrets=APP_SECRET=app-secret:latest --set-env-vars=APP_ENV=prod --concurrency=80 --timeout=30s --memory=512Mi --min-instances=1 --no-allow-unauthenticated --quiet
gcloud run services get-iam-policy "$SERVICE" --region="$REGION" --project="$PROJECT_ID" --format='table(bindings.role,bindings.members)'
REV_OLD=$(gcloud run revisions list --service="$SERVICE" --region="$REGION" --project="$PROJECT_ID" --sort-by='~metadata.creationTimestamp' --format='value(metadata.name)' --limit=1)
gcloud run deploy "$SERVICE" --image=gcr.io/cloudrun/hello --region="$REGION" --project="$PROJECT_ID" --service-account="$SA" --set-secrets=APP_SECRET=app-secret:latest --set-env-vars=APP_ENV=prod,RELEASE=canary --concurrency=80 --timeout=30s --memory=512Mi --min-instances=1 --no-allow-unauthenticated --no-traffic --quiet
REV_NEW=$(gcloud run revisions list --service="$SERVICE" --region="$REGION" --project="$PROJECT_ID" --sort-by='~metadata.creationTimestamp' --format='value(metadata.name)' --limit=1)
gcloud run services update-traffic "$SERVICE" --region="$REGION" --project="$PROJECT_ID" --to-revisions="$REV_NEW=10,$REV_OLD=90" --quiet
gcloud logging read 'resource.type="cloud_run_revision"' --limit=20 --project="$PROJECT_ID" --format='value(textPayload)'

停在 10% 观察 10~30 分钟,看 5xx、延迟和日志。没问题再复制第二段切 50%;再观察,最后复制第三段全量。坑:三段别一次全贴,否则小流量就白做了。

PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "(unset)" ]; then PROJECT_ID=$(gcloud projects list --format='value(projectId)' | head -n1); gcloud config set project "$PROJECT_ID" --quiet; fi
REGION=asia-east1
SERVICE=hello-run
REVS=$(gcloud run revisions list --service="$SERVICE" --region="$REGION" --project="$PROJECT_ID" --sort-by='~metadata.creationTimestamp' --format='value(metadata.name)' --limit=2)
REV_NEW=$(printf '%sn' "$REVS" | sed -n '1p')
REV_OLD=$(printf '%sn' "$REVS" | sed -n '2p')
gcloud run services update-traffic "$SERVICE" --region="$REGION" --project="$PROJECT_ID" --to-revisions="$REV_NEW=50,$REV_OLD=50" --quiet
PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "(unset)" ]; then PROJECT_ID=$(gcloud projects list --format='value(projectId)' | head -n1); gcloud config set project "$PROJECT_ID" --quiet; fi
REGION=asia-east1
SERVICE=hello-run
REV_NEW=$(gcloud run revisions list --service="$SERVICE" --region="$REGION" --project="$PROJECT_ID" --sort-by='~metadata.creationTimestamp' --format='value(metadata.name)' --limit=1)
gcloud run services update-traffic "$SERVICE" --region="$REGION" --project="$PROJECT_ID" --to-revisions="$REV_NEW=100" --quiet

常见问题(FAQ)

使用 GCP Cloud Run 一定要会 Docker 吗?

不一定要精通,但要会打包镜像、看日志和理解端口配置。

Cloud Run 适合放数据库吗?

不建议直接放数据库,应搭配 Cloud SQL 等托管数据库。

没有流量时还会收费吗?

通常可缩到零,但存储镜像、请求和保留最小实例会产生费用。

能 SSH 到 Cloud Run 实例里排障吗?

不能像 VM 那样 SSH,主要靠日志、指标和临时调试版本排查。

部署后访问报 403 怎么办?

多半是 IAM 权限或未认证访问未开放,检查服务访问设置。

不想自己折腾 GCP?我们提供云平台代维护、上云迁移与账单代付服务,一对一技术支持。

查看代维服务 →

发表评论