Saltar al contenido
# 🚀 Deploy — klaUS-aicalc-roi ## 🤔 ¿Qué hago? ¿Cómo lo hago? ¿Y para qué lo hago? **¿Qué hago?** Gestiono el despliegue a producción de klaUS-aicalc-roi en dos plataformas complementarias: la API FastAPI en Railway y el frontend Next.js en Vercel. Cada componente se despliega de forma independiente y se comunica a través de una variable de entorno. **¿Cómo lo hago?** Railway despliega la API usando el `Dockerfile` del repositorio — detecta la imagen automáticamente al conectar el repo GitHub. Vercel despliega el frontend usando el `vercel.json` que apunta al directorio `frontend/` — Next.js se detecta de forma nativa. Ambas plataformas se conectan al repositorio GitHub y despliegan automáticamente en cada merge a `main`. **¿Y para qué lo hago?** Sin un entorno de producción accesible, klaUS-aicalc-roi es solo código en un repositorio. El deploy en producción es el punto de inflexión que convierte el MVP en un producto real — con una URL pública, usuarios reales y métricas reales de uso. --- ## 🗺️ Arquitecturas de despliegue ### 🖥️ Local — Podman (desarrollo y validación) ```mermaid flowchart TD A[👤 Usuario\nnavegador] --> B[🌐 HOST:3000] B --> C[🐳 frontend container\nnode:22-slim] C -- NEXT_PUBLIC_API_URL\n${HOST}:8000 --> D[🐳 api container\npython:3.12-slim] D --> E[⚡ FastAPI + Uvicorn\n:8000] E --> F[🧠 TCO Engine] E --> G[📦 Catálogo estático\nbackend/data/] ENV[📄 .env\nNEXT_PUBLIC_API_URL=...] -.configura.-> C subgraph Podman [🐳 podman compose] C D end ``` ### 🌐 Producción — Railway + Vercel ```mermaid flowchart TD A[👤 Usuario] --> B[🌐 Vercel CDN] B --> C[🖥️ Frontend Next.js\nvercel.app domain] C -- NEXT_PUBLIC_API_URL --> D[🚂 Railway\nrailway.app domain] D --> E[🐳 Docker Container\npython:3.12-slim] E --> F[⚡ FastAPI + Uvicorn\n:PORT] F --> G[🧠 TCO Engine] F --> H[📦 Catálogo estático\nbackend/data/] subgraph CI_CD [⚙️ CI/CD — GitHub Actions] I[Push a main] --> J[lint + test + frontend] J -- ✅ CI verde --> K[Vercel auto-deploy] J -- ✅ CI verde --> L[Railway auto-deploy] end I --> CI_CD ``` --- ## 🖥️ Entorno local — Podman ### Prerrequisitos ```bash # Instalar Podman + podman-compose en macOS M1 brew install podman podman-compose # Inicializar la máquina virtual de Podman (solo la primera vez) podman machine init podman machine start ``` ### ⚙️ Configurar la URL de la API (`.env`) `NEXT_PUBLIC_API_URL` se embebe en el bundle del frontend en build time. Si el navegador accede desde una IP distinta a `localhost` (red local, otro dispositivo, VM), hay que configurarla antes de construir. ```bash # Copiar la plantilla y editar cp .env.example .env # Editar .env con tu IP local # NEXT_PUBLIC_API_URL=http://192.168.1.127:8000 ``` | Escenario | Valor de `NEXT_PUBLIC_API_URL` | | --- | --- | | Acceso solo desde el mismo Mac | `http://localhost:8000` (defecto) | | Acceso desde otro dispositivo en la red | `http://192.168.X.X:8000` | | Acceso desde VM en el mismo host | `http://host.containers.internal:8000` | > ⚠️ `.env` está en `.gitignore` — nunca se commitea. Usa `.env.example` como plantilla compartida. --- ### Levantar el stack completo ```bash # Desde la raíz del repo podman compose up --build # La primera vez tarda ~3-5 minutos (descarga imágenes base + npm ci + build) # Las siguientes arranca en ~30 segundos (capas cacheadas) ``` Cuando veas `frontend_1 | Ready on http://localhost:3000`, abre el navegador: | URL | Qué es | | --- | --- | | `http://localhost:3000` | 🖥️ Interfaz web — formulario TCO | | `http://localhost:8000/health` | 💚 Healthcheck de la API | | `http://localhost:8000/docs` | 📄 Swagger UI de la API | ### Parar el stack ```bash podman compose down ``` ### Reconstruir tras cambios de código ```bash # Si cambias backend/ — solo reconstruye la imagen API podman compose up --build api # Si cambias frontend/ — reconstruye la imagen frontend # (necesario porque NEXT_PUBLIC_API_URL se embebe en build time) podman compose up --build frontend ``` ### Ficheros implicados | Fichero | Rol | | --- | --- | | `compose.yml` | Orquesta API (:8000) + frontend (:3000) | | `Dockerfile` | Imagen API — `python:3.12-slim` + FastAPI + uvicorn | | `frontend/Dockerfile` | Imagen frontend — multi-stage: build Next.js + runner `node:22-slim` | | `.dockerignore` | Excluye `.venv`, `node_modules`, tests, artefactos locales y secretos | > ⚠️ **Por qué `NEXT_PUBLIC_API_URL` apunta a la IP del host y no a `http://api:8000`** > Los `NEXT_PUBLIC_*` se embeben en el JavaScript del cliente en tiempo de build — los ejecuta el navegador del usuario, no el servidor. El navegador ve los puertos del host (`localhost` o la IP de red), no la red interna de Podman (`api`). Configura la URL correcta en `.env` antes de `podman compose up --build`. --- ## 🏗️ Componentes de deploy ### 🐳 Docker — API (Railway) | Fichero | Propósito | | --- | --- | | `Dockerfile` | Imagen Python 3.12-slim con FastAPI + uvicorn | | `.dockerignore` | Excluye frontend, tests, .venv, artefactos locales | | `railway.toml` | Builder dockerfile, healthcheck `/health`, política de restart | **Variables de entorno Railway:** | Variable | Valor | Descripción | | --- | --- | --- | | `PORT` | Auto (Railway lo inyecta) | Puerto en el que escucha uvicorn | El endpoint `/health` devuelve `{"status": "ok"}` — Railway lo usa como liveness probe con timeout 30s. --- ### ⚡ Vercel — Frontend (Next.js) | Fichero | Propósito | | --- | --- | | `vercel.json` | `rootDirectory: frontend`, Next.js framework, `npm ci` + `npm run build` | **Variables de entorno Vercel (configurar en dashboard):** | Variable | Valor | Descripción | | --- | --- | --- | | `NEXT_PUBLIC_API_URL` | `https://<nombre>.railway.app` | URL pública de la API en Railway | > ⚠️ `NEXT_PUBLIC_API_URL` se embebe en el bundle del cliente en build time. Configurar antes del primer deploy de Vercel. --- ## 🔧 Setup inicial — paso a paso ### 1️⃣ Deploy de la API en Railway ```bash # Prerrequisito: cuenta Railway + CLI instalada npm install -g @railway/cli railway login # Crear proyecto desde el repo GitHub railway init # → Seleccionar "Empty Project" → nombre: klaUS-api # Conectar el repo railway connect # Primer deploy (Railway detecta el Dockerfile automáticamente) railway up # Obtener la URL pública railway domain # → Copiar este dominio para configurar Vercel ``` O bien desde la web: railway.app → New Project → Deploy from GitHub repo → seleccionar `Ka0s-Klaus/klaUS-aicalc-roi`. --- ### 2️⃣ Deploy del Frontend en Vercel ```bash # Prerrequisito: cuenta Vercel + CLI instalada npm install -g vercel vercel login # Desde la raíz del repo vercel # Configurar variable de entorno con la URL de Railway vercel env add NEXT_PUBLIC_API_URL production # → Introducir: https://<nombre>.railway.app # Redeploy para que tome la variable vercel --prod ``` O bien desde la web: vercel.com → New Project → Import `Ka0s-Klaus/klaUS-aicalc-roi` → Vercel detecta `vercel.json` automáticamente. --- ### 3️⃣ Verificar el deploy ```bash # Healthcheck API curl https://<nombre>.railway.app/health # → {"status": "ok", "version": "0.1.0"} # Catálogo de modelos curl https://<nombre>.railway.app/v1/models | jq '.total' # → 30 # Frontend accesible open https://<nombre>.vercel.app ``` --- ## 🔁 Flujo de deploys automáticos Una vez conectados ambos servicios al repo GitHub: ```mermaid sequenceDiagram participant Dev as 👨‍💻 Developer participant GH as GitHub participant CI as GitHub Actions participant RW as Railway participant VR as Vercel Dev->>GH: git push main (merge PR) GH->>CI: trigger CI workflow CI->>CI: lint + test + frontend (paralelo) CI-->>GH: ✅ CI verde GH->>RW: webhook → build Dockerfile → deploy API GH->>VR: webhook → npm ci + npm build → deploy frontend RW-->>Dev: 🚂 API live en railway.app VR-->>Dev: ⚡ Frontend live en vercel.app ``` > 📝 Railway y Vercel despliegan de forma independiente tras cada push a `main`. CI no bloquea el deploy de las plataformas — ambas leen el mismo trigger de GitHub. Si CI falla, el equipo debe revisar antes de que el deploy llegue a usuarios. --- ## 🔒 Seguridad | Control | Estado | Detalle | | --- | --- | --- | | Secretos en plataforma | ✅ | Env vars en Railway/Vercel dashboard — nunca en el repo | | HTTPS forzado | ✅ | Ambas plataformas sirven solo HTTPS por defecto | | Sin credenciales en Dockerfile | ✅ | Solo deps públicos de PyPI | | `.dockerignore` | ✅ | Excluye `.env`, `.claude/`, `CLAUDE.md`, `uv.lock` | | CORS | ✅ | `CORSMiddleware` activo con `allow_origins=["*"]` en Fase 1 — restringir al dominio Vercel en Fase 2 | | Auth (Fase 2) | ❌ | Sin autenticación en Fase 1 — API pública de solo lectura | --- ## 📊 Costes estimados (free tier) | Plataforma | Plan | Límite free | Coste si se supera | | --- | --- | --- | --- | | Railway | Hobby ($5/mes) | 500h ejecución / 5GB egress | $0.000463/min CPU + $0.000231/min RAM | | Vercel | Free | 100GB bandwidth / 100h build | $20/mes Pro | Para el MVP con tráfico bajo, ambos planes free/hobby son suficientes. --- ## 🔗 Documentos relacionados - [CI/CD](ci-cd.md) — pipeline de GitHub Actions que valida antes de cada deploy - [API REST](api.md) — endpoints que expone la API desplegada en Railway - [Frontend](frontend.md) — aplicación Next.js desplegada en Vercel - [TCO Engine](tco-engine.md) — motor de cálculo que corre dentro del contenedor Railway