Logo GCP con Eduardo

GCP con Eduardo

Despliegue Serverless en GCP con Antigravity y Cloud Run: De Script Local a Producción
✦ Guía Técnica & Arquitectura

Tu primera App de Python en Producción con Antigravity y Google Cloud Run

El paradigma de DevOps artesanal y la configuración manual de infraestructura en consola están siendo reemplazados por el desarrollo agéntico. Descubre cómo transformar un script local de Python en un servicio serverless contenerizado y desplegado en Google Cloud Platform (GCP) mediante agentes autónomos en Antigravity, código declarativo con Terraform y el runtime de Cloud Run.

Puntos Clave del Flujo de Trabajo Agéntico

Vibe Coding Operativo: De prompt en lenguaje natural a código productivo e infraestructura como código.
Adaptación HTTP Serverless: Conversión de scripts continuos a endpoints compatibles con Cloud Run.
IaC Declarativa: Gestión automatizada de proyectos, APIs, Artifact Registry y servicios con Terraform.
Resolución de Antipatrones: Diagnóstico de bloqueos de IP en centros de datos y manejo de IAM.
Google Cloud Run Antigravity AI Agents Terraform Docker Artifact Registry Python 3.11 Flask / Gunicorn

Matriz de Decisión Arquitectónica para Despliegues en GCP

Comparativa entre los diferentes modelos de ejecución para cargas de trabajo en Python según el nivel de abstracción y los requerimientos operacionales:

Patrón de Cómputo Latencia de Arranque Escalado a Cero Complejidad Operativa Coste en Inactividad Caso de Uso Recomendado
Google Cloud Run Sub-segundo / Bajo Sí (Nativo) Mínima (Serverless) $0.00 (Pago por petición) Microservicios HTTP, APIs, Procesamiento ligero orientado a eventos.
Cloud Run Jobs Segundos Sí (On-demand) Mínima $0.00 Scripts Batch, ETLs programadas, tareas de mantenimiento finitas.
Google Kubernetes Engine (GKE) Inmediata (Pods calientes) No (Requiere nodos mínimos) Alta (Control plane, mallas) Fijo por infraestructura base Monolitos complejos, arquitecturas multi-tenant de gran escala.
Compute Engine (VMs) Minutos No Media-Alta (Parches, SO) Fijo (24/7) Cargas legacy o dependencias a nivel de kernel/driver de hardware.

⚠️ Antipatrones Críticos: Del Script Local al Entorno Serverless

Al migrar un script secuencial o en bucle infinito (por ejemplo, un monitor de precios de Binance) hacia un contenedor en Google Cloud Run, surgen dos fallos recurrentes:

  • Fallo de Port Binding & Health Checks: Cloud Run inyecta la variable de entorno PORT (por defecto 8080) y requiere que el contenedor acepte peticiones HTTP. Si el script se ejecuta en un bucle cerrado sin levantar un servidor web, el servicio fallará al inicializarse tras agotar el timeout de arranque.
  • Error HTTP 451 (Unavailable For Legal Reasons) y Geobloqueo de IP: Servicios de terceros y exchanges financieros frecuentemente bloquean las peticiones originadas desde rangos de IP de centros de datos públicos (como GCP, AWS o Azure). En producción, estos escenarios demandan proxies residenciales, Cloud NAT con IPs dedicadas o endpoints privados autorizados.

Implementación Técnica: Transformación y Despliegue

A continuación se detalla la arquitectura generada por el agente de Antigravity: la envoltura del script en una API REST, el empaquetado del contenedor y la orquestación mediante Terraform.

1. Código de la Aplicación con Adaptación HTTP (`app.py`)

import os
import requests
from flask import Flask, jsonify

app = Flask(__name__)

def obtener_precio_btc():
    url = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
    headers = {"User-Agent": "Mozilla/5.0"}
    try:
        response = requests.get(url, headers=headers, timeout=5)
        response.raise_for_status()
        data = response.json()
        return float(data.get("price", 0.0))
    except Exception as e:
        return {"error": str(e)}

@app.route("/", methods=["GET"])
@app.route("/health", methods=["GET"])
def health_check():
    return jsonify({"status": "healthy", "service": "btc-monitor"}), 200

@app.route("/price", methods=["GET"])
def get_price():
    precio = obtener_precio_btc()
    if isinstance(precio, dict) and "error" in precio:
        return jsonify({"status": "error", "message": precio["error"]}), 502
    return jsonify({
        "status": "success",
        "symbol": "BTCUSDT",
        "price_usd": precio
    }), 200

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8080))
    app.run(host="0.0.0.0", port=port)

2. Definición del Contenedor Multi-Stage (`Dockerfile`)

FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PORT=8080

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn

COPY . .

# Cloud Run inyecta dinámicamente $PORT
CMD exec gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 app:app

3. Infraestructura como Código (`main.tf`)

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

# Habilitación de APIs requeridas
resource "google_project_service" "services" {
  for_each = toset([
    "run.googleapis.com",
    "artifactregistry.googleapis.com",
    "cloudbuild.googleapis.com"
  ])
  project = var.project_id
  service = each.key
  disable_on_destroy = false
}

# Repositorio en Artifact Registry
resource "google_artifact_registry_repository" "repo" {
  depends_on    = [google_project_service.services]
  location      = var.region
  repository_id = var.repository_id
  description   = "Docker repository para btc-monitor"
  format        = "DOCKER"
}

# Servicio en Cloud Run (v2)
resource "google_cloud_run_v2_service" "default" {
  depends_on = [google_artifact_registry_repository.repo]
  name       = var.service_name
  location   = var.region
  ingress    = "INGRESS_TRAFFIC_ALL"

  template {
    containers {
      image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.repository_id}/btc-monitor:latest"
      resources {
        limits = {
          cpu    = "1"
          memory = "512Mi"
        }
      }
    }
  }
}

# Acceso Público No Autenticado
resource "google_cloud_run_v2_service_iam_member" "public_access" {
  name     = google_cloud_run_v2_service.default.name
  location = google_cloud_run_v2_service.default.location
  role     = "roles/run.invoker"
  member   = "allUsers"
}

output "service_url" {
  value       = google_cloud_run_v2_service.default.uri
  description = "URL pública del servicio Cloud Run"
}

Framework de Despliegue Paso a Paso

  1. Inicialización de la Sesión Agéntica en Antigravity: Abre el repositorio local en Antigravity y solicita al agente el objetivo mediante un prompt declarativo (ej. "Quiero migrar mi script app.py a Google Cloud Run usando Terraform y Docker").
  2. Aprobación del Implementation Plan: Revisa la arquitectura propuesta: refactorización a endpoint HTTP, creación del Dockerfile optimizado, declaración de Terraform y script de compilación.
  3. Vinculación de Facturación y Cuenta de GCP: Si el agente crea un nuevo proyecto, proporciona el billing_account_id cuando lo solicite para permitir el aprovisionamiento de APIs administradas.
  4. Ejecución del Pipeline de Construcción: El agente ejecuta deploy.sh, que compila la imagen con Cloud Build, la publica en Artifact Registry y aplica terraform apply -auto-approve.
  5. Verificación del Endpoint: Obtén la URL de salida y realiza un health check mediante curl -s https://[SERVICE-URL]/health.

Preguntas Frecuentes de Arquitectura

¿Por qué un script de bucle infinito en Python no funciona de forma nativa en Cloud Run?

Cloud Run es un entorno orientado a peticiones. Si el contenedor no abre un socket TCP en el puerto asignado por $PORT para responder a las comprobaciones de estado de Google, la plataforma considerará el contenedor insalubre y lo reiniciará. Las tareas sin servidor de procesamiento continuo deben estructurarse como Cloud Run Jobs o encapsularse en un servidor web.

¿Cómo previene este flujo el error de cuotas al activar APIs en GCP?

Terraform utiliza el recurso google_project_service con dependencias explícitas (depends_on). Esto garantiza que run.googleapis.com y artifactregistry.googleapis.com estén totalmente propagadas antes de que el script intente crear repositorios o servicios.

¿Cómo mitigar el bloqueo HTTP 451 en entornos productivos?

Para evitar bloqueos por geolocalización o listas negras de proveedores cloud en APIs de terceros, se debe conectar el servicio Cloud Run a una red VPC Serverless con Cloud NAT asignando una IP estática pública, o bien orquestar las peticiones mediante un túnel o proxy intermedio.

Sobre el Autor: Eduardo Martínez Agrelo

AI & Data Architect

Especialista en diseño de arquitecturas de datos a gran escala, modernización cloud en Google Cloud Platform y adopción de paradigmas de desarrollo asistido por IA para acelerar el ciclo de vida de software empresarial.