Tools: Arquitectura Serverless en GCP con Terraform: Cómo ahorré costos y sobreviví al "State Drift"

Tools: Arquitectura Serverless en GCP con Terraform: Cómo ahorré costos y sobreviví al "State Drift"

Source: Dev.to

La historia de cómo conecté Cloud Run con Cloud SQL usando IP privada sin pagar el conector VPC, y cómo recuperé mi infraestructura cuando Terraform "olvidó" todo. ## El sueño del MVP barato ## Problema 1: La Amnesia de Terraform (State Drift) ## La Solución: ## Problema 2: Conectar Cloud Run a SQL (La trampa de los $17 USD) ## La Solución: Direct VPC Egress. ## Conclusión ## 🎁 Bonus: SDK de Node.js Oficial Cuando empecé a construir Geo-Engine, mi SaaS de procesamiento geoespacial con Go, tenía una meta clara: quería una arquitectura profesional, pero no quería pagar cientos de dólares al mes mientras buscaba mis primeros usuarios. La elección obvia fue Google Cloud Platform (GCP): Sonaba perfecto en papel, pero la realidad me golpeó con dos problemas que casi descarrilan el proyecto: Terraform queriendo borrar mi producción y una factura potencial innecesaria por conectividad de red. Aquí te cuento cómo solucioné ambos. Desarrollo desde mi laptop (Fedora) y a veces desde la nube (Project IDX). Un día, al intentar desplegar desde el entorno nuevo, Terraform me dio el susto de mi vida: ¿Crear? ¡Pero si ya existían! Terraform quería duplicar todo (o borrar y recrear). Lo que aprendí: El archivo terraform.tfvars (donde guardo mi project_id y variables sensibles) estaba en mi .gitignore. Al cambiar de entorno, Terraform usó valores por defecto, apuntó a un "limbo" y decidió que no había nada creado. Esto sincronizó mi estado local con la realidad de la nube y el plan volvió a decir "No changes". Paz mental restaurada. Por seguridad, mi base de datos solo tiene IP Privada. Pero Cloud Run vive "fuera" de mi VPC. La documentación oficial recomienda usar un Serverless VPC Access Connector. El problema: Ese conector cuesta mínimo ~$17 USD/mes porque mantiene instancias VM encendidas 24/7. Para un MVP sin ingresos, eso es un "impuesto" doloroso. Descubrí que Cloud Run ahora soporta una conexión directa a la VPC sin intermediarios costosos. Solo necesitaba configurar correctamente el bloque de red en Terraform. Aquí está el fragmento de código que me ahorró esos $200 dólares al año: Con PRIVATE_RANGES_ONLY, Cloud Run enruta el tráfico hacia mi base de datos (IP 10.x.x.x) a través de la red interna de Google, y el resto del tráfico sale a internet normalmente. Costo extra: $0. Construir Geo-Engine me enseñó que la infraestructura como código es poderosa, pero requiere disciplina. Hoy tengo un stack corriendo Go, Postgres y Cloud Run que es: Si te interesa ver el resultado final, puedes probar la beta de mi herramienta aquí: Geo-Engine. Mientras ajustaba la infraestructura, me di cuenta de que necesitaba una forma fácil de consumir mi propia API. Así que aproveché para empaquetar y publicar el SDK oficial. Usando tsup, logré generar un paquete híbrido que soporta tanto ES Modules (moderno) como CommonJS (legacy) automáticamente. La estructura final quedó así de limpia: Templates let you quickly answer FAQs or store snippets for re-use. Muy buen artículo!! Enhorabuena Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: Terraform will perform the following actions: # google_cloud_run_service.api will be created # google_sql_database_instance.master will be created ... Plan: 5 to add, 0 to change, 0 to destroy. Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: Terraform will perform the following actions: # google_cloud_run_service.api will be created # google_sql_database_instance.master will be created ... Plan: 5 to add, 0 to change, 0 to destroy. COMMAND_BLOCK: Terraform will perform the following actions: # google_cloud_run_service.api will be created # google_sql_database_instance.master will be created ... Plan: 5 to add, 0 to change, 0 to destroy. CODE_BLOCK: terraform import google_cloud_run_v2_service.default projects/MI_PROYECTO/locations/us-central1/services/geo-api Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: terraform import google_cloud_run_v2_service.default projects/MI_PROYECTO/locations/us-central1/services/geo-api CODE_BLOCK: terraform import google_cloud_run_v2_service.default projects/MI_PROYECTO/locations/us-central1/services/geo-api COMMAND_BLOCK: resource "google_cloud_run_v2_service" "default" { name = "geo-api" location = "us-central1" template { # ... configuración del contenedor ... vpc_access { network_interfaces { network = "default" subnetwork = "default" } # ESTA ES LA CLAVE: egress = "PRIVATE_RANGES_ONLY" } } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: resource "google_cloud_run_v2_service" "default" { name = "geo-api" location = "us-central1" template { # ... configuración del contenedor ... vpc_access { network_interfaces { network = "default" subnetwork = "default" } # ESTA ES LA CLAVE: egress = "PRIVATE_RANGES_ONLY" } } } COMMAND_BLOCK: resource "google_cloud_run_v2_service" "default" { name = "geo-api" location = "us-central1" template { # ... configuración del contenedor ... vpc_access { network_interfaces { network = "default" subnetwork = "default" } # ESTA ES LA CLAVE: egress = "PRIVATE_RANGES_ONLY" } } } CODE_BLOCK: geo-engine-node/ ├── dist/ │ ├── index.js (ES Modules - import) │ ├── index.cjs (CommonJS - require) │ └── index.d.ts (TypeScript Definitions) └── package.json Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: geo-engine-node/ ├── dist/ │ ├── index.js (ES Modules - import) │ ├── index.cjs (CommonJS - require) │ └── index.d.ts (TypeScript Definitions) └── package.json CODE_BLOCK: geo-engine-node/ ├── dist/ │ ├── index.js (ES Modules - import) │ ├── index.cjs (CommonJS - require) │ └── index.d.ts (TypeScript Definitions) └── package.json - Cloud Run: Para escalar a cero (pagar solo por uso). - Cloud SQL (Postgres): Para tener una base de datos gestionada y sólida. - Terraform: Para tener todo como código (IaC). - Recrear variables: Asegurarme de que el project_id en el nuevo entorno fuera idéntico al real. - Importar, no recrear: Usé el comando import para decirle a Terraform: "Oye, este recurso ya existe, mételo en tu memoria". - Seguro: Todo en red privada. - Económico: Escala a cero y no paga conectores ociosos. - Reproducible: Gracias a Terraform (y a saber usar import). - Joined Jan 22, 2026