Use Cases

Claude Code × GCP Cloud Run 完全指南 | 无服务器容器自动部署

使用 Claude Code 加速 GCP Cloud Run 部署。完整指南含真实代码示例:Dockerfile 生成、自动扩缩容、CI/CD 流水线和 Secret Manager 集成。

“我想在 GCP 上运行容器,但配置项太多了——不知道从哪里开始”——我以前也有同样的感受。但当我真正动手使用 Cloud Run 时,真的被它比 ECS 简单得多的配置方式惊到了。无需 VPC 设置、无需任务角色设计、无需集群管理。只需准备好容器镜像,几乎立刻就能获得 HTTPS 端点。

结合 Claude Code,从 Dockerfile 生成到 Cloud Build CI/CD 流水线搭建,一切都能在令人惊讶的短时间内完成。本文将以 6 个清晰的步骤,讲解我实际尝试过的操作过程。


为什么 Cloud Run 比 ECS 更简单

Cloud Run 是 GCP 提供的完全托管容器执行服务。采用”请求驱动的无服务器”模型,仅在 HTTP 请求到达时才启动容器。

与 ECS 的对比:

Cloud Run:
- 集群管理:无需
- 网络配置:默认发布外部 HTTPS
- 扩缩容:  从 0 到 N 全自动
- 计费:    基于请求处理时间(空闲时几乎免费)

AWS ECS/Fargate:
- 集群管理:必需(3 层:Cluster / Service / TaskDef)
- 网络配置:需要设计 VPC 和安全组
- 扩缩容:  按最小实例数计费
- 计费:    基于运行任务时间

Step 1:自动生成 Dockerfile

claude -p "
按以下条件生成生产级 Dockerfile。

- 应用:TypeScript + Express API(端口 8080)
- Node.js 版本:22 LTS
- 多阶段构建(builder → runner)
- 以非 root 用户(node 用户)运行
- 同时生成 .dockerignore
- 支持 Cloud Run 推荐的 PORT 环境变量
- 健康检查:/health 端点
"

生成的 Dockerfile:

# ---- builder 阶段 ----
FROM node:22-alpine AS builder
WORKDIR /app

# 先复制依赖项以利用构建缓存
COPY package*.json ./
RUN npm ci --only=production=false

COPY tsconfig.json ./
COPY src ./src
RUN npm run build

# ---- runner 阶段 ----
FROM node:22-alpine AS runner
WORKDIR /app

# 安全:以非 root 用户运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# 仅安装生产依赖
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 复制构建产物
COPY --from=builder /app/dist ./dist

# Cloud Run 通过 PORT 环境变量传递端口
ENV PORT=8080
ENV NODE_ENV=production

# 切换到非 root 用户
USER appuser

EXPOSE 8080
CMD ["node", "dist/index.js"]

Step 2:首次部署到 Cloud Run

gcloud run deploy myapp-api \
  --image asia-northeast1-docker.pkg.dev/my-project-123/myapp/api:v1.0.0 \
  --region asia-northeast1 \
  --platform managed \
  --memory 512Mi \
  --cpu 1 \
  --concurrency 80 \
  --allow-unauthenticated \
  --set-env-vars NODE_ENV=production \
  --port 8080

Step 3:配置自动扩缩容

gcloud run services update myapp-api \
  --region asia-northeast1 \
  --min-instances 1 \
  --max-instances 20 \
  --concurrency 80 \
  --cpu-throttling \
  --execution-environment gen2
# service.yaml - Cloud Run 服务配置文件
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: myapp-api
spec:
  template:
    metadata:
      annotations:
        # 最小实例数(防止冷启动)
        autoscaling.knative.dev/minScale: "1"
        # 最大实例数(成本上限)
        autoscaling.knative.dev/maxScale: "20"
        # CPU 使用率 70% 时扩容
        autoscaling.knative.dev/target-utilization-percentage: "70"
        run.googleapis.com/execution-environment: gen2
    spec:
      containerConcurrency: 80
      containers:
        - image: asia-northeast1-docker.pkg.dev/my-project-123/myapp/api:latest
          resources:
            limits:
              memory: 512Mi
              cpu: "1"

Step 4:Secret Manager 集成

# 注册密钥
echo -n "postgresql://user:password@host:5432/db" | \
  gcloud secrets create DATABASE_URL --data-file=-

# 为服务账号授予读取权限
gcloud projects add-iam-policy-binding my-project-123 \
  --member="serviceAccount:[email protected]" \
  --role="roles/secretmanager.secretAccessor"

# 将密钥挂载到 Cloud Run
gcloud run services update myapp-api \
  --region asia-northeast1 \
  --set-secrets="DATABASE_URL=DATABASE_URL:latest,SENDGRID_API_KEY=SENDGRID_API_KEY:latest,JWT_SECRET=JWT_SECRET:latest"
// src/config.ts
export const config = {
  databaseUrl: process.env.DATABASE_URL!,
  sendgridApiKey: process.env.SENDGRID_API_KEY!,
  jwtSecret: process.env.JWT_SECRET!,
  port: parseInt(process.env.PORT || "8080", 10),
  nodeEnv: process.env.NODE_ENV || "development",
};

// 启动时验证必需的密钥
const requiredEnvVars = ["DATABASE_URL", "SENDGRID_API_KEY", "JWT_SECRET"];
for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    console.error(`缺少必需的环境变量:${envVar}`);
    process.exit(1);
  }
}

Step 5:使用 Cloud Build 构建 CI/CD 流水线

# cloudbuild.yaml
steps:
  # Step 1:安装依赖并运行测试
  - name: "node:22-alpine"
    id: "test"
    entrypoint: "sh"
    args:
      - "-c"
      - |
        npm ci
        npm run test
        npm run lint

  # Step 2:构建 Docker 镜像
  - name: "gcr.io/cloud-builders/docker"
    id: "build"
    args:
      - "build"
      - "-t"
      - "asia-northeast1-docker.pkg.dev/$PROJECT_ID/myapp/api:$COMMIT_SHA"
      - "-t"
      - "asia-northeast1-docker.pkg.dev/$PROJECT_ID/myapp/api:latest"
      - "."

  # Step 3:推送到 Artifact Registry
  - name: "gcr.io/cloud-builders/docker"
    id: "push"
    args:
      - "push"
      - "--all-tags"
      - "asia-northeast1-docker.pkg.dev/$PROJECT_ID/myapp/api"

  # Step 4:部署到 Cloud Run
  - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
    id: "deploy"
    entrypoint: "gcloud"
    args:
      - "run"
      - "deploy"
      - "myapp-api"
      - "--image"
      - "asia-northeast1-docker.pkg.dev/$PROJECT_ID/myapp/api:$COMMIT_SHA"
      - "--region"
      - "asia-northeast1"
      - "--platform"
      - "managed"
      - "--quiet"

  # Step 5:Slack 通知(成功时)
  - name: "curlimages/curl"
    id: "notify-success"
    entrypoint: "curl"
    args:
      - "-X"
      - "POST"
      - "-H"
      - "Content-type: application/json"
      - "--data"
      - '{"text":"✅ Cloud Run 部署完成:$COMMIT_SHA"}'
      - "$_SLACK_WEBHOOK_URL"

options:
  logging: CLOUD_LOGGING_ONLY
  machineType: E2_HIGHCPU_8
timeout: "1200s"

Step 6:自定义域名与负载均衡器

# main.tf - Cloud Run + Load Balancer + SSL
resource "google_compute_region_network_endpoint_group" "cloudrun_neg" {
  name                  = "myapp-neg"
  network_endpoint_type = "SERVERLESS"
  region                = "asia-northeast1"
  cloud_run {
    service = "myapp-api"
  }
}

resource "google_compute_managed_ssl_certificate" "default" {
  name = "myapp-ssl-cert"
  managed {
    domains = ["api.example.com"]
  }
}

resource "google_compute_security_policy" "policy" {
  name = "myapp-security-policy"
  rule {
    action   = "deny(403)"
    priority = "1000"
    match {
      expr {
        expression = "evaluatePreconfiguredExpr('sqli-stable')"
      }
    }
    description = "SQL 注入防护"
  }
  rule {
    action   = "allow"
    priority = "2147483647"
    match {
      versioned_expr = "SRC_IPS_V1"
      config { src_ip_ranges = ["*"] }
    }
    description = "默认允许"
  }
}

五大常见陷阱

1. 冷启动导致超时

生产环境中将 min-instances 设置为 1 以上。

gcloud run services update myapp-api --min-instances 1 --region asia-northeast1

2. 忘记处理 SIGTERM

// src/index.ts - 正确处理 SIGTERM
process.on("SIGTERM", () => {
  server.close(() => process.exit(0));
  setTimeout(() => process.exit(1), 30000);
});

3. 在环境变量中明文写入敏感信息

# ❌ 绝对禁止:明文写入密钥
gcloud run services update myapp-api --set-env-vars DATABASE_PASSWORD=mypassword123

# ✅ 正确做法:通过 Secret Manager 安全传递
gcloud run services update myapp-api --set-secrets="DATABASE_PASSWORD=DATABASE_PASSWORD:latest"

4. 内存配置不足

# 解决方案:显式设置 Node.js 堆大小
CMD ["node", "--max-old-space-size=384", "dist/index.js"]

5. 在请求之外进行后台处理

# 始终分配 CPU 模式(需要后台处理的服务)
gcloud run services update myapp-api --no-cpu-throttling --region asia-northeast1

总结

任务Claude Code 的贡献
Dockerfile 生成多阶段构建和非 root 设置自动化
首次部署从需求生成完整 gcloud 命令
扩缩容配置最小/最大实例和 CPU 阈值优化
Secret Manager 集成密钥创建、权限和挂载配置生成
CI/CD 流水线含测试的 cloudbuild.yaml 生成
自定义域名负载均衡器 Terraform 配置自动生成

相关文章

参考资料

#claude-code #gcp #cloud-run #docker #typescript #serverless
免费

免费 PDF:5 分钟看懂 Claude Code 速查表

只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。

我们会严格保护你的个人信息,绝不发送垃圾邮件。

让你的 Claude Code 工作流更上一层楼

50 个经过实战检验的提示词模板,现在就能复制粘贴到 Claude Code 中使用。

Masa

本文作者

Masa

深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。