Use Cases

Claude Code × GCP Cloud Run Guía Completa | Despliegue automático de contenedores serverless

Acelera los despliegues en GCP Cloud Run con Claude Code. Guía completa con ejemplos de código reales: generación de Dockerfile, auto-escalado, pipelines CI/CD e integración con Secret Manager.

“Quiero ejecutar contenedores en GCP, pero hay demasiadas configuraciones — no sé por dónde empezar” — yo mismo me sentía así antes. Pero cuando realmente puse manos a la obra con Cloud Run, me sorprendió genuinamente lo mucho más simple que era la configuración comparado con ECS. Sin configuración de VPC, sin diseño de roles de tarea, sin gestión de clústeres. Solo prepara una imagen de contenedor y casi de inmediato tienes un endpoint HTTPS.

Combínalo con Claude Code, y todo — desde la generación del Dockerfile hasta la configuración del pipeline CI/CD con Cloud Build — se puede hacer en un tiempo sorprendentemente corto. Este artículo explica los pasos que realmente probé, en 6 etapas claras.


Por qué Cloud Run es más simple que ECS

Cloud Run es el servicio de ejecución de contenedores totalmente gestionado de GCP. Usa un modelo “serverless impulsado por solicitudes” donde los contenedores solo se inician cuando llegan solicitudes HTTP.

Comparación con ECS:

Cloud Run:
- Gestión de clústeres: No requerida
- Configuración de red: HTTPS externo publicado por defecto
- Escalado:            Totalmente automático de 0 a N
- Facturación:         Basada en tiempo de procesamiento de solicitudes (casi gratis en reposo)

AWS ECS/Fargate:
- Gestión de clústeres: Requerida (3 capas: Cluster / Service / TaskDef)
- Configuración de red: Diseño de VPC y grupos de seguridad requerido
- Escalado:            Facturación por número mínimo de instancias
- Facturación:         Basada en tiempo de tarea en ejecución

Paso 1: Generar automáticamente un Dockerfile

claude -p "
Genera un Dockerfile listo para producción con los siguientes requisitos:

- App: TypeScript + Express API (puerto 8080)
- Versión de Node.js: 22 LTS
- Build multi-etapa (builder → runner)
- Ejecutar como usuario no-root (usuario node)
- También genera .dockerignore
- Soporte para la variable de entorno PORT como recomienda Cloud Run
- Health check: endpoint /health
"

Dockerfile generado:

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

# Copiar dependencias primero para aprovechar la caché de build
COPY package*.json ./
RUN npm ci --only=production=false

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

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

# Seguridad: ejecutar como usuario no-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Instalar solo dependencias de producción
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copiar artefactos del build
COPY --from=builder /app/dist ./dist

# Cloud Run pasa el puerto via la variable de entorno PORT
ENV PORT=8080
ENV NODE_ENV=production

# Cambiar a usuario no-root
USER appuser

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

Paso 2: Primer despliegue en 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

Paso 3: Configurar auto-escalado

gcloud run services update myapp-api \
  --region asia-northeast1 \
  --min-instances 1 \
  --max-instances 20 \
  --concurrency 80 \
  --cpu-throttling \
  --execution-environment gen2
# service.yaml - Configuración del servicio Cloud Run
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: myapp-api
spec:
  template:
    metadata:
      annotations:
        # Número mínimo de instancias (mitigación de cold start)
        autoscaling.knative.dev/minScale: "1"
        # Número máximo de instancias (límite de costos)
        autoscaling.knative.dev/maxScale: "20"
        # Escalar al 70% de uso de CPU
        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"

Paso 4: Integración con Secret Manager

# Registrar secretos
echo -n "postgresql://user:password@host:5432/db" | \
  gcloud secrets create DATABASE_URL --data-file=-

# Otorgar permisos de lectura a la cuenta de servicio
gcloud projects add-iam-policy-binding my-project-123 \
  --member="serviceAccount:[email protected]" \
  --role="roles/secretmanager.secretAccessor"

# Montar secretos en 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",
};

// Validar secretos requeridos al inicio
const requiredEnvVars = ["DATABASE_URL", "SENDGRID_API_KEY", "JWT_SECRET"];
for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    console.error(`Variable de entorno faltante: ${envVar}`);
    process.exit(1);
  }
}

Paso 5: Pipeline CI/CD con Cloud Build

# cloudbuild.yaml
steps:
  # Paso 1: Instalar dependencias y ejecutar tests
  - name: "node:22-alpine"
    id: "test"
    entrypoint: "sh"
    args:
      - "-c"
      - |
        npm ci
        npm run test
        npm run lint

  # Paso 2: Construir imagen 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"
      - "."

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

  # Paso 4: Desplegar en 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"

  # Paso 5: Notificación Slack (en caso de éxito)
  - name: "curlimages/curl"
    id: "notify-success"
    entrypoint: "curl"
    args:
      - "-X"
      - "POST"
      - "-H"
      - "Content-type: application/json"
      - "--data"
      - '{"text":"✅ Despliegue Cloud Run completado: $COMMIT_SHA"}'
      - "$_SLACK_WEBHOOK_URL"

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

Paso 6: Dominio personalizado y balanceador de carga

# 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 = "Protección contra inyección SQL"
  }
  rule {
    action   = "allow"
    priority = "2147483647"
    match {
      versioned_expr = "SRC_IPS_V1"
      config { src_ip_ranges = ["*"] }
    }
    description = "Permitir por defecto"
  }
}

Los 5 errores más comunes

1. Timeouts por cold starts

Cloud Run escala a cero instancias cuando no hay tráfico. Configura min-instances a 1 o más en producción.

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

2. No manejar SIGTERM

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

3. Datos sensibles en texto plano en variables de entorno

# ❌ Nunca: secretos en texto plano
gcloud run services update myapp-api --set-env-vars DATABASE_PASSWORD=mypassword123

# ✅ Correcto: usar Secret Manager
gcloud run services update myapp-api --set-secrets="DATABASE_PASSWORD=DATABASE_PASSWORD:latest"

4. Configuración de memoria insuficiente

# Solución: establecer explícitamente el tamaño del heap de Node.js
CMD ["node", "--max-old-space-size=384", "dist/index.js"]

5. Procesamiento en segundo plano fuera de solicitudes

# Modo CPU siempre activo (para servicios que necesitan procesamiento en segundo plano)
gcloud run services update myapp-api --no-cpu-throttling --region asia-northeast1

Resumen

TareaContribución de Claude Code
Generación de DockerfileBuild multi-etapa y configuración no-root automatizada
Primer despliegueComandos gcloud completos generados desde requisitos
Configuración de escaladoInstancias mín/máx y umbrales de CPU optimizados
Integración Secret ManagerCreación de secretos, permisos y configuración de montaje generados
Pipeline CI/CDcloudbuild.yaml con tests generado
Dominio personalizadoConfiguración Terraform del balanceador de carga generada automáticamente

Artículos relacionados

Referencias

#claude-code #gcp #cloud-run #docker #typescript #serverless
Gratis

PDF gratuito: Hoja de trucos de Claude Code en 5 minutos

Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.

Cuidamos tus datos personales y nunca enviamos spam.

Lleva tu flujo con Claude Code al siguiente nivel

50 plantillas de prompts probadas en producción, listas para copiar y pegar en Claude Code.

Masa

Sobre el autor

Masa

Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.