Files
apps-registry/workloads/docs-portal/docs/decisiones/0012-practica-multicloud-gcp-azure.md
T
devopsandClaude Sonnet 5 059597346d
Build and Push Docs Portal / Escaneo de Secretos (Gitleaks) (push) Successful in 36s
Build and Push Docs Portal / Construir y publicar Docs Portal (push) Successful in 9m20s
docs(adr-0012): documentar pivot de Fase 1 a Connectors nativos de claude.ai
El MCP self-hosted (taylorwilsdon/google_workspace_mcp) quedo abandonado:
limite real en FastMCP's OAuthProxy, no error de configuracion -
redirect_uri se arma desde base_url (no desde GOOGLE_OAUTH_REDIRECT_URI),
y base_url tambien alimenta issuer_url, que mcp/server/auth/routes.py
exige HTTPS-o-localhost sin bypass. Documenta la causa raiz confirmada
leyendo el codigo fuente instalado, la decision del usuario de usar
Connectors nativos en su lugar, y la verificacion en vivo (3 calendarios
reales, razonamiento de fechas correcto).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QMP7ULbxbsB5g1vELNAT3D
2026-09-21 04:16:19 +00:00

18 KiB

ADR 0012 — Práctica multi-cloud: GCP y Azure DevOps en paralelo al lab ZimaOS

Estado: aceptada — Fase 1 (Google Workspace) completa y verificada en vivo, con un giro real: el MCP self-hosted planeado (taylorwilsdon/google_workspace_mcp) quedó abandonado por un límite real de esa librería (ver §Fase 1), reemplazado por los Connectors nativos de claude.ai. Fases 2-4 quedan documentadas como roadmap, explícitamente no implementadas hasta que se pidan en una sesión futura. Fecha: 2026-09-20 (Fase 1 cerrada 2026-09-21)

Contexto

El lab GitOps de CruzCloud (ZimaOS + k3d + Gitea + Argo CD) ya prueba en vivo, con rigor de ADR, patrones que después se pueden proponer en trabajo real con clientes — pero corre íntegro sobre hardware propio, sin tocar ningún proveedor de nube público. Dos motivaciones concretas empujan a abrir una segunda línea de práctica en paralelo:

  1. Practicar herramientas que el cliente actual restringe. Ciertas plataformas/herramientas de nube no están disponibles en el entorno de trabajo del cliente actual — la única forma real de ganar experiencia con ellas es un entorno propio.
  2. Preparar propuestas de arquitectura con evidencia, no solo teoría. Poder decir "ya lo corrí, así se ve, esto cuesta X" en una propuesta a un cliente vale más que una descripción de manual.
  3. Portafolio. Los mismos ADRs, diagramas y decisiones documentadas que ya sostiene este lab sirven como muestra de trabajo verificable.

Alcance confirmado explícitamente por el usuario, no asumido:

  • Entorno paralelo, no migración. ZimaOS sigue siendo el lab principal — nada de lo de acá reemplaza nada de lo ya construido.
  • Cuentas personales únicamente. Nunca credenciales del cliente actual ni de ningún cliente futuro — separación total.
  • Costo cero o mínimo, con alertas de presupuesto como red de seguridad desde el primer recurso pago que se cree (ver Análisis de costo más abajo).
  • Mismo rigor documental que el resto del lab: un ADR por decisión arquitectónica real, diagramas Mermaid, cifras verificadas en vivo o contra fuente primaria (no contra un blog de terceros sin corroborar — ver el caso del supuesto MCP "oficial" de Google Workspace en la Fase 1).

Diagrama del lab actual (ZimaOS)

Versión simplificada, con solo los componentes relevantes para la comparación de este ADR — el diagrama completo (con Traefik, serverlb, cada Host de Ingress, etc.) vive en Visión general.

flowchart LR
    User(["Usuario"]) -->|HTTPS| CF["Cloudflare Tunnel<br/>(sin puertos abiertos)"]
    CF --> NPM["Nginx Proxy Manager<br/>enrutamiento por dominio"]
    NPM --> K3D["k3d<br/>(cluster K8s local, Docker)"]

    subgraph K3D_INT["Dentro de k3d"]
        ARGO["Argo CD"] -->|sync| APPS["Apps reales<br/>(ecommerce, docs-portal)"]
        GIT["Gitea<br/>(auto-alojado)"] -.->|watch repos| ARGO
    end
    K3D --> K3D_INT

Costo real: $0 — hardware propio (NAS ZimaOS), sin proveedor de nube de por medio.

Diagrama Fase 1 — entorno paralelo en GCP (VM + k3s)

Esto NO es GKE administrado. Es una VM e2-micro corriendo k3s self-managed, elegida específicamente para mantener el costo real en $0 — GKE (administrado, ver Fase 4) no tiene una vía 100% gratuita permanente (ver el análisis de costo más abajo).

flowchart LR
    User(["Usuario / Claude"]) -->|SSH / kubectl| VM["Compute Engine e2-micro<br/>Always Free — us-west1/us-central1/us-east1<br/>NO es GKE administrado"]

    subgraph VM_INT["Dentro de la VM"]
        K3S["k3s<br/>(single-node, self-managed)"]
        ARGO2["Argo CD<br/>(instancia propia, no la de ZimaOS)"] -->|sync| APPS2["Manifiestos del nuevo repo<br/>(mismo patrón base+overlay<br/>de platform-templates)"]
    end
    VM --> VM_INT

    GIT_EXISTENTE["Gitea existente<br/>gitea.cruzcloud.net<br/>(reusado, repo nuevo, NO uno nuevo)"] -.->|watch| ARGO2

Por qué reusar el Gitea existente en vez de levantar uno nuevo en GCP: el objetivo es practicar el patrón GitOps completo (Kustomize base+overlay + Argo CD sincronizando) con el mínimo de piezas nuevas — levantar un segundo Gitea solo sumaría superficie a mantener sin enseñar nada nuevo sobre GCP en sí. El cómputo (la VM, k3s) es lo que cambia de proveedor; el origen de verdad (Git) se queda donde ya funciona.

Costo real: $0 — e2-micro es Always Free de forma permanente (no es el trial de $300/90 días), siempre que la VM viva en us-west1, us-central1 o us-east1 y no supere 1 instancia no-preemptible + 30 GB-mes de disco persistente estándar + 1 GB/mes de salida hacia Norteamérica — límites reales del programa, verificados contra la documentación oficial de Google Cloud.1

Fase futura (no implementada) — GKE real con Spot + auto-apagado

Marcada explícitamente como fase futura, no implementada — ver Fase 4 para cuándo se decidiría ejecutarla.

flowchart LR
    TF["Terraform<br/>(provisiona todo)"] --> GKE["GKE Standard<br/>1 cluster zonal"]
    GKE --> NP["Node pool<br/>Spot VMs (e2-small)"]

    SCHED["Cloud Scheduler<br/>(cron: apagar/prender)"] -->|invoca| FN["Cloud Run/Functions<br/>job de auto-apagado"]
    FN -->|resize node pool<br/>a 0 / de vuelta| NP

    style GKE fill:#333,stroke:#f90,color:#fff
    style NP fill:#333,stroke:#f90,color:#fff
    style SCHED fill:#333,stroke:#f90,color:#fff
    style FN fill:#333,stroke:#f90,color:#fff

Mecanismo de auto-apagado: comparación antes de recomendar

Dos mecanismos reales existen para esto; se investigaron ambos antes de elegir uno, sin asumir cuál es "obviamente" mejor:

Escalar node pool a 0 Destruir/recrear cluster completo (terraform apply/destroy programado)
Qué apaga Solo el cómputo (los nodos) Todo, incluido el control plane
Ahorra el fee de control plane ($0.10/hora) No — pero ya está cubierto por el crédito mensual de $74.40 para un único cluster, así que no hay nada real que ahorrar ahí Sí, pero es un ahorro sobre algo que ya es $0
Tiempo de "volver a prender" ~1-3 min (crear nodos sobre un cluster que ya existe) ~5-10 min (crear el cluster entero de cero)
Piezas para automatizarlo Cloud Scheduler → Cloud Function/Run → 1 llamada a la API de GKE (resize node pool) Cloud Scheduler → runner con Terraform + credenciales + state remoto seguro → apply/destroy completo
Riesgo de drift/config perdida Bajo — el cluster nunca se borra Mayor — cualquier cosa hecha fuera de Terraform se pierde en cada destroy
Patrón documentado en la industria Sí, es el patrón estándar (Cloud Scheduler + Pub/Sub + Cloud Function), con casos reales de ~30% de ahorro de cómputo en clusters no-críticos2 Existe, pero es la opción menos común para este caso puntual

Recomendación (sin implementar todavía): escalar el node pool a 0/de vuelta vía Cloud Scheduler + Cloud Function, no destruir/recrear el cluster completo. Razón concreta: el crédito de $74.40/mes ya cubre el fee de control plane de un único cluster exista o no exista un nodo adentro — destruir el cluster entero no ahorra nada sobre ese componente porque ya es $0, y a cambio suma la complejidad real de manejar Terraform state de forma segura dentro de un cron job automatizado, más un tiempo de arranque 3-5x más largo. La única razón real para preferir destruir/recrear sería eliminar toda huella en GCP entre usos (por ejemplo, para no depender de que el crédito mensual siga existiendo) — no es el caso acá.

Tabla comparativa

Lab actual (ZimaOS) GCP Fase 1 (VM + k3s) GCP Fase futura (GKE + Spot)
Cómputo NAS propio, Docker/k3d Compute Engine e2-micro GKE Standard, node pool Spot e2-small
¿Kubernetes administrado? No (k3d local) No (k3s self-managed) Sí (GKE Standard)
Control plane N/A (local) N/A (k3s corre en la misma VM) $0.10/hora — cubierto por crédito de $74.40/mes para 1 cluster
Cómputo de nodos $0 (hardware propio) $0 (Always Free permanente) ~$7-10/mes (Spot e2-small, sin cubrir por ningún crédito)
GitOps Argo CD + Gitea, ambos autoalojados Argo CD propio en la VM, mismo Gitea reusado Igual que Fase 1, sobre GKE
Costo mensual real $0 $0 ~$7-10/mes (no $0)
Estado En producción, uso diario A ejecutar esta sesión (Parte 2/3) Documentado, no implementado

Análisis de costo explícito

No existe un GKE administrado 100% gratis de forma permanente. El crédito de $74.40/mes por cuenta de facturación cubre exactamente el fee de control plane ($0.10/hora) de un cluster zonal Standard o Autopilot — nunca el cómputo real. GKE Autopilot factura desde el primer pod que se programa (vCPU/memoria/almacenamiento por pod), sin ningún equivalente al Always Free de Compute Engine; GKE Standard sí puede correr sobre un único nodo Spot/preemptible barato (~$7/mes para un e2-small, verificado contra tarifas actuales), pero ese cómputo nunca es gratis.3

La única ruta de costo cero real y permanente es Compute Engine e2-micro (Always Free) + k3s self-managed — exactamente la Fase 1 de este ADR. No es el trial de $300/90 días (que expira y no es "gratis", es prepago con reloj corriendo); es un beneficio permanente del programa de nivel gratuito de Google Cloud, activo mientras la VM cumpla los límites de región/tamaño/tráfico ya descritos arriba.

Activar cualquier billing account de GCP requiere tarjeta igual, incluso para quedarse exclusivamente en recursos Always Free — Google verifica el medio de pago aunque el cargo real sea $0. Detalle no obvio, verificado contra documentación oficial: las alertas de presupuesto no se pueden configurar mientras la cuenta esté operando sobre crédito gratuito de prueba — hace falta pasar la cuenta de facturación a estado "paga" (el uso real sigue siendo $0 si no se sale del Always Free) para que la opción de crear un presupuesto/alerta quede disponible.4 Recomendación: hacer ese paso — pasar a cuenta de facturación paga — como parte del setup inicial, y configurar de inmediato una alerta de presupuesto en $1-5 USD como red de seguridad, antes de crear ningún recurso más allá de lo cubierto por Always Free.

Secuencia de fases

Todas documentadas acá; solo la Fase 1 se ejecuta en esta sesión. Las Fases 2-4 no se implementan sin pedido explícito en una sesión futura — quedan como roadmap, no como trabajo en curso.

Fase 1 (ejecutada) — Google Workspace, con un giro real en la implementación

Objetivo cumplido — acceso real a Google Calendar desde Claude, verificado en vivo — pero por un camino distinto al planeado originalmente. Detalle de la investigación previa (qué MCP usar, oficial vs comunitario) — resumen: no existe un MCP oficial de Google Workspace publicado por Google (se verificó explícitamente buscando en los GitHub google/googleworkspace oficiales — no aparece ninguno; un solo blog de terceros afirma que "ya existe uno oficial" sin que ninguna otra fuente lo corrobore, así que se descarta esa afirmación por no verificable). Se optó por taylorwilsdon/google_workspace_mcp (comunitario, el más completo y activamente mantenido: 120+ herramientas, OAuth 2.1, +2500 estrellas) como primer intento, self-hosted dentro de tmux-host.

Ese self-hosted quedó abandonado — límite real encontrado en la librería, no un error de configuración. El servidor usa FastMCP's GoogleProvider/OAuthProxy, que construye el redirect_uri enviado a Google directamente desde su propio base_url — no desde la variable GOOGLE_OAUTH_REDIRECT_URI que el proyecto expone (esa variable solo valida entradas, no arma la salida real; hallazgo confirmado leyendo el código fuente instalado, no la documentación pública). Y base_url es la misma variable de la que depende por default issuer_url, que el SDK de MCP exige sea HTTPS salvo que sea literalmente localhost/127.0.0.1 (chequeo hardcodeado en mcp/server/auth/routes.py:validate_issuer_url, sin variable de entorno que lo salte). Resultado: no hay combinación de variables de entorno que satisfaga a la vez "el navegador del usuario debe poder llegar al callback" (necesita un dominio real, no localhost) y "el issuer debe ser HTTPS o localhost" — workspace-mcp no expone issuer_url por separado de base_url en su capa de configuración (confirmado en core/server.py: solo pasa base_url a GoogleProvider(...)).

Decisión del usuario ante ese límite: en vez de armar un proxy HTTPS con certificado autofirmado (infraestructura extra, fricción de certificado no confiable en cada uso) o parchear el paquete instalado a mano (frágil, se pierde en cualquier reinstalación), se usaron los Connectors nativos de claude.ai (Google Calendar, vía el directorio de conectores de la cuenta, fuera de Claude Code por completo) — mismo resultado práctico (Claude con acceso real al calendario personal), infraestructura cero de nuestro lado, sin tocar código de terceros. Contrapartida real: es una integración de cuenta de claude.ai, no un MCP configurado dentro del proyecto platform-multicloud — no aparece como herramienta en sesiones de Claude Code (lab/cloud-practice), solo en el chat normal de claude.ai.

Verificado en vivo: pregunta real en claude.ai normal ("¿qué tengo en el calendario esta semana?") devolvió una respuesta correcta revisando 3 calendarios reales de la cuenta (principal, Familia, festivos de Colombia) vía la herramienta Google Calendar: Lists calendar events, con razonamiento correcto sobre qué cuenta como "esta semana" dado el día/hora real de la consulta.

Fase 2 (documentar, no ejecutar) — Terraform aislado

Instalar el marketplace oficial de HashiCorp Agent Skills, y practicar aprovisionando algo mínimo y desechable en GCP. Nota para cuando se ejecute: HashiCorp también publica un MCP Server de Terraform propio y oficial (hashicorp/terraform-mcp-server, integra con el Terraform Registry y HCP Terraform) — es una pieza distinta a los Agent Skills (el MCP es el "tubo" hacia herramientas reales; los Skills son paquetes de conocimiento/instrucciones) y vale la pena evaluar si se usan juntos o solo uno de los dos en ese momento.

Fase 3 (documentar, no ejecutar) — Azure DevOps en paralelo

MCP oficial microsoft/azure-devops-mcp (confirmado como el oficial y activamente mantenido por Microsoft, hay varios forks/alternativas comunitarias que no son la vía recomendada). Replicar el mismo pipeline DevSecOps que ya corre en Gitea Actions para este lab (Gitleaks → Trivy → Semgrep → Syft → Cosign) como Azure Pipelines YAML, en un proyecto personal — mismo objetivo de práctica que la Fase 1: tener el patrón ya probado para proponerlo en trabajo con clientes que usen Azure DevOps.

Fase 4 (documentar, no ejecutar) — GKE real con Spot + auto-apagado

Solo si se decide conscientemente asumir el costo bajo pero no-cero (~$7-10/mes, ver el análisis de costo arriba) — vía Terraform, node pool Spot, y el mecanismo de auto-apagado recomendado en la sección de arriba (escalar a 0, no destruir/recrear el cluster).

Opciones consideradas

Opción A favor En contra
Entorno paralelo en GCP+Azure (elegido) Practica exactamente lo que el cliente actual restringe; portafolio con evidencia real; ZimaOS sigue intacto Dos entornos más que mantener; curva de aprendizaje de dos proveedores nuevos a la vez
Migrar el lab completo a un solo cloud Un solo entorno que mantener Pierde justo lo que se quiere conservar (costo $0, control total de hardware); no es lo que se pidió
Solo AWS (el "default" de la industria) Ecosistema más grande El cliente actual ya usa/restringe otras plataformas específicas — practicar AWS no cierra esa brecha puntual
GKE administrado desde el día 1 (saltear la VM self-managed) Más parecido a "producción real" desde el inicio Rompe el requisito de costo cero explícito del usuario — ningún tier de GKE es gratis de forma permanente

Decisión

Se abre una línea de práctica multi-cloud paralela al lab ZimaOS, en cuentas personales, arrancando por Compute Engine e2-micro (Always Free) + k3s self-managed en GCP como Fase 1 de cómputo, y Google Workspace vía MCP comunitario (taylorwilsdon/google_workspace_mcp) como primer MCP a configurar en esta sesión. Terraform, Azure DevOps y GKE administrado quedan documentados como Fases 2-4, sin implementar.

Consecuencias

  • Se gana un segundo entorno real, documentado con el mismo rigor de ADR que ya sostiene el lab, listo para mostrar en propuestas a clientes — sin tocar nunca credenciales de cliente.
  • Se sacrifica/queda como deuda técnica: la Fase 1 de GCP usa k3s self-managed, no un Kubernetes administrado real — cualquier lección específica de operar GKE (autoscaling nativo, node pools, IAM de GCP) queda pendiente hasta la Fase 4, y esa fase sí tiene costo real (~$7-10/mes) que hoy no se está pagando.
  • Para revertir: dar de baja la VM e2-micro y (si se llegó a crear) el node pool de la Fase 4 — ningún componente de este ADR toca o depende del lab ZimaOS existente, así que no hay nada que revertir de ese lado.