Módulo 7: Alternative Platforms (Render, Railway, Fly.io)
8. Proyecto: Deploy to Platform of Choice
Descripción del proyecto
Este es el proyecto integrador del Módulo 7. Vas a tomar todo lo que aprendiste — Render, Railway, Fly.io, comparativa, CI/CD, decision matrix — y aplicarlo a un deployment real. El resultado es tu app AI desplegada en producción en la plataforma que tu decision matrix recomienda, con CI/CD automatizado, documentación comparativa, y la decision matrix v2 completada.
No es un ejercicio teórico. Tu app estará online, accesible por URL pública, desplegándose automáticamente en cada push. Y tendrás un documento de decisión que justifica por qué elegiste esa plataforma y no otra — con datos reales de haberlas probado todas.
Por qué importa: Este proyecto es el puente entre "conozco las plataformas" y "tengo una app AI en producción." Es lo que muestras cuando te preguntan "¿dónde desplegaste tu proyecto?" — no solo la URL, sino el razonamiento documentado detrás de la elección. Y es la base directa del Módulo 8, donde este deployment se convierte en parte de tu sistema AI integrador.
Objetivo del proyecto
Producir tres entregables:
- App AI desplegada: Tu app running en la plataforma elegida, con URL pública, health check, y CI/CD
- Documentación comparativa: Comparación de tu experiencia en las tres plataformas vs local (M2) vs serverless (M3)
- Decision Matrix v2: La matrix del M1 extendida con criterios de plataforma y datos reales
Recap del Módulo
Antes de empezar, asegúrate de dominar estos conceptos:
| Cápsula | Concepto clave | Lo usas para |
|---|---|---|
| 02 | Render: deploy, render.yaml, databases | Experiencia en plataforma 1 |
| 03 | Railway: CLI, add-ons, preview environments | Experiencia en plataforma 2 |
| 04 | Fly.io: flyctl, multi-region, volumes | Experiencia en plataforma 3 |
| 05 | Comparativa: pricing, limits, features | Datos para tu matrix |
| 06 | CI/CD: GitHub Actions + cada plataforma | Automatizar el deploy |
| 07 | Decision Matrix v2: framework, escenarios | Elegir y justificar plataforma |
Caso de Estudio (si no tienes app propia)
DocuSearch AI — continuación
Si no tienes app propia, usa DocuSearch AI de la cápsula 01 y módulos anteriores:
project_data = {
"name": "DocuSearch AI",
"type": "RAG (Retrieval Augmented Generation)",
"stage": "MVP → Growth early",
"team": "2 developers",
"technical": {
"framework": "FastAPI",
"llm": "GPT-4o-mini via OpenAI API",
"database": "PostgreSQL (inference logs)",
"cache": "Redis (response cache)",
"container": "Docker (Dockerfile funcional)",
"ci_cd": "GitHub Actions (pipeline del M7 cápsula 06)",
},
"constraints": {
"budget": "$30-50/mes para infraestructura",
"latency": "<3 seconds para respuesta",
"streaming": "Nice-to-have, no requerido ahora",
"users": "150 actuales, 500 target a 6 meses",
"availability": "Business hours, no 24/7 critical",
},
"deployment_history": {
"M2": "Docker Compose local — funciona, no accesible externamente",
"M3": "Lambda — funciona para endpoints simples, limitado por cold start",
"M7_render": "Desplegado en cápsula 02 — funciona, timeout 30s preocupa",
"M7_railway": "Desplegado en cápsula 03 — funciona, mejor DX",
"M7_flyio": "Desplegado en cápsula 04 — funciona, más setup pero edge",
},
}
Especificaciones del Entregable
Entregable 1: App AI Desplegada
Requisitos mínimos
Tu app desplegada debe tener:
✅ URL pública accesible (HTTPS)
✅ Endpoint /health que retorne status
✅ Al menos un endpoint de inferencia AI (/ask, /search, /chat, etc.)
✅ Variables de entorno configuradas (API keys como secrets)
✅ CI/CD: push a main → tests → deploy automático
✅ Health check configurado en la plataforma
Requisitos opcionales (recomendados)
Bonus si tu app tiene:
⚠️ Base de datos (PostgreSQL) para logging/metadata
⚠️ Cache (Redis) para responses frecuentes
⚠️ Custom domain
⚠️ Staging + production environments
⚠️ Monitoring/alertas básicas
Entregable 2: Documentación Comparativa
Un archivo deployment-comparison.md con:
# Deployment Comparison: DocuSearch AI
## Resumen ejecutivo
[2-3 párrafos: qué plataforma elegiste, por qué, y cómo se compara
con las alternativas que probaste]
## Plataformas probadas
### Local (Docker Compose — M2)
- **Setup time:** [tiempo real]
- **Deploy time:** [tiempo real]
- **Coste:** $0 (tu laptop)
- **Pros:** [experiencia real]
- **Contras:** [experiencia real]
- **Cuándo usarla:** [tu criterio]
### Serverless (Lambda — M3)
- **Setup time:** [tiempo real]
- **Deploy time:** [tiempo real]
- **Coste estimado:** [cálculo]
- **Pros:** [experiencia real]
- **Contras:** [experiencia real]
- **Cuándo usarla:** [tu criterio]
### Render (M7)
- **Setup time:** [tiempo real]
- **Deploy time:** [tiempo real]
- **Coste:** [real o estimado]
- **Pros:** [experiencia real]
- **Contras:** [experiencia real]
### Railway (M7)
- **Setup time:** [tiempo real]
- **Deploy time:** [tiempo real]
- **Coste:** [real o estimado]
- **Pros:** [experiencia real]
- **Contras:** [experiencia real]
### Fly.io (M7)
- **Setup time:** [tiempo real]
- **Deploy time:** [tiempo real]
- **Coste:** [real o estimado]
- **Pros:** [experiencia real]
- **Contras:** [experiencia real]
## Tabla comparativa
| Dimensión | Local | Lambda | Render | Railway | Fly.io |
|-----------|-------|--------|--------|---------|--------|
| Setup | | | | | |
| Deploy | | | | | |
| Coste/mes | | | | | |
| DX | | | | | |
| Limitaciones AI | | | | | |
## Conclusión
[Cuál elegiste y por qué. Cuándo reconsiderarías.]
Entregable 3: Decision Matrix v2
Un archivo decision-matrix-v2.md:
# Decision Matrix v2: [Tu Proyecto]
## Contexto
[Datos del proyecto: stage, equipo, budget, users, technical stack]
## Evolución desde M1
Matrix v1 (M1): Categoría = Managed
Matrix v2 (M7): Plataforma = [tu elección]
## Criterios y Pesos
| # | Criterio | Peso | Justificación |
|---|----------|------|---------------|
| 1 | [criterio] | [peso] | [por qué este peso] |
| ... | ... | ... | ... |
| **Total** | | **100** | |
## Evaluación (1-5)
| Criterio (Peso) | AWS | Render | Railway | Fly.io | Notas |
|-----------------|-----|--------|---------|--------|-------|
| [criterio] (peso) | [1-5] | [1-5] | [1-5] | [1-5] | [datos reales] |
| ... | ... | ... | ... | ... | ... |
## Scores Ponderados
| Criterio | AWS | Render | Railway | Fly.io |
|----------|-----|--------|---------|--------|
| [criterio] | [w×s] | [w×s] | [w×s] | [w×s] |
| ... | ... | ... | ... | ... |
| **TOTAL** | **[X]** | **[X]** | **[X]** | **[X]** |
| **%** | **X%** | **X%** | **X%** | **X%** |
## Recomendación
### Plataforma elegida: [nombre]
**Score:** [total] de [máximo] ([porcentaje]%)
### Justificación
[3-5 párrafos con datos reales de tu experiencia en las cápsulas 02-06]
### Opciones descartadas
| Opción | Score | Razón de descarte |
|--------|-------|--------------------|
| ... | ... | ... |
## Sensitivity Analysis
[Resultado de variar los 3 criterios top — ¿la recomendación es robusta?]
## Condiciones de Re-evaluación
- [ ] [trigger 1 con métrica]
- [ ] [trigger 2 con métrica]
- [ ] [trigger 3 con métrica]
- Review programado: [fecha — 3 meses]
## Migration Path
Si [trigger], migrar a [plataforma]:
1. [paso 1]
2. [paso 2]
3. [paso 3]
Paso a Paso Guiado
Paso 1: Ejecutar tu Decision Matrix v2 (20 min)
Antes de desplegar, decide dónde. Usa el framework de la cápsula 07:
# Ejecuta tu matrix con tus criterios y pesos reales
# El resultado te dice en qué plataforma desplegar
my_criteria = {
# Ajusta según TU caso
"Coste mensual": 20,
"Developer experience": 15,
"Request timeout": 15,
"WebSocket/streaming": 15,
"Databases integradas": 10,
"RAM disponible": 10,
"Cold start": 10,
"CI/CD integration": 5,
}
# Calcula scores con los datos de la cápsula 07
# Resultado: tu plataforma recomendada
Paso 2: Preparar tu app para producción (15 min)
# Verifica que tu app tiene todo lo necesario
# 1. Health check endpoint
@app.get("/health")
def health():
return {
"status": "healthy",
"version": "1.0.0",
"platform": os.environ.get("PLATFORM", "unknown"),
}
# 2. Variables de entorno (no hardcoded)
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
DATABASE_URL = os.environ.get("DATABASE_URL")
REDIS_URL = os.environ.get("REDIS_URL")
# 3. .env.example (para documentación)
# OPENAI_API_KEY=sk-your-key-here
# PLATFORM=railway
# DATABASE_URL=postgresql://...
# REDIS_URL=redis://...
# 4. Dockerfile funcional
# (ya lo tienes de la cápsula 01)
# 5. Tests básicos
# (ya los tienes de la cápsula 06)
# Verificar que todo funciona localmente
docker build -t docusearch-ai .
docker run -p 8000:8000 \
-e OPENAI_API_KEY=sk-test \
-e PLATFORM=local \
docusearch-ai
curl http://localhost:8000/health
Paso 3: Desplegar en tu plataforma elegida (15 min)
Sigue las instrucciones de la cápsula correspondiente (02, 03, o 04):
# === Si elegiste Railway ===
railway init
railway add # PostgreSQL
railway add # Redis
railway variables set OPENAI_API_KEY=sk-prod-xxx
railway variables set PLATFORM=railway
railway up
railway domain
# URL: https://docusearch-ai-production.up.railway.app
# === Si elegiste Render ===
# Dashboard → New → Web Service → Conectar GitHub → Deploy
# Agregar environment variables
# URL: https://docusearch-ai.onrender.com
# === Si elegiste Fly.io ===
flyctl launch --name docusearch-ai
flyctl secrets set OPENAI_API_KEY=sk-prod-xxx
flyctl deploy
# URL: https://docusearch-ai.fly.dev
Paso 4: Verificar el deployment (5 min)
# Definir tu URL
APP_URL="https://tu-app.tu-plataforma.com"
# Health check
curl $APP_URL/health
# {"status":"healthy","version":"1.0.0","platform":"railway"}
# Inferencia
curl -X POST $APP_URL/ask \
-H "Content-Type: application/json" \
-d '{"question": "¿Qué es deployment?", "max_tokens": 200}'
# Si tienes PostgreSQL, verificar logging
curl $APP_URL/health # debería logear en DB
# Si tienes Redis, verificar cache
# Segunda request al mismo question debería ser más rápida
time curl -X POST $APP_URL/ask \
-H "Content-Type: application/json" \
-d '{"question": "Cache test", "max_tokens": 50}'
Paso 5: Configurar CI/CD (15 min)
Usa el workflow de la cápsula 06 adaptado a tu plataforma:
# Crear workflow
mkdir -p .github/workflows
# .github/workflows/deploy.yml
name: Test and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- run: pip install -r app/requirements.txt pytest httpx
- run: pytest tests/ -v
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
# Descomenta la sección de tu plataforma:
# --- Railway ---
- name: Deploy to Railway
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
run: |
npm install -g @railway/cli
railway up --detach
# --- Render ---
# - name: Deploy to Render
# run: |
# curl -X POST \
# "https://api.render.com/v1/services/${{ secrets.RENDER_SERVICE_ID }}/deploys" \
# -H "Authorization: Bearer ${{ secrets.RENDER_API_KEY }}"
# --- Fly.io ---
# - uses: superfly/flyctl-actions/setup-flyctl@master
# - name: Deploy to Fly.io
# env:
# FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
# run: flyctl deploy --remote-only
- name: Health check
run: |
sleep 60
curl -f ${{ secrets.APP_URL }}/health
# Commit y push — el pipeline se ejecuta automáticamente
git add .github/workflows/deploy.yml
git commit -m "Add CI/CD pipeline for production deployment"
git push origin main
Paso 6: Documentar la experiencia (20 min)
Crea los archivos deployment-comparison.md y decision-matrix-v2.md usando los templates de la sección anterior. Llena con datos REALES de tu experiencia:
# Medir datos reales
# 1. Tiempo de deploy (mira los logs de GitHub Actions)
# 2. Latencia (ejecuta el benchmark de cápsula 05)
# 3. Coste (mira el dashboard de billing de tu plataforma)
# 4. Problemas encontrados (revisa tus notas/troubleshooting)
# collect_metrics.py — Recopilar datos automáticamente
import requests
import time
import json
import statistics
APP_URL = "https://tu-app.tu-plataforma.com"
print(f"Recopilando métricas de {APP_URL}...\n")
# Medir latencia de health check
print("=== Health Check Latency ===")
latencies = []
for i in range(10):
start = time.time()
resp = requests.get(f"{APP_URL}/health", timeout=30)
elapsed = (time.time() - start) * 1000
latencies.append(elapsed)
print(f" Request {i+1}: {elapsed:.0f}ms (status: {resp.status_code})")
time.sleep(1)
print(f" Promedio: {statistics.mean(latencies):.0f}ms")
print(f" Mediana: {statistics.median(latencies):.0f}ms")
print(f" P95: {sorted(latencies)[9]:.0f}ms")
# Medir latencia de inferencia
print("\n=== Inference Latency ===")
inference_latencies = []
for i in range(5):
start = time.time()
resp = requests.post(
f"{APP_URL}/ask",
json={"question": "Benchmark test", "max_tokens": 50},
timeout=60,
)
elapsed = (time.time() - start) * 1000
inference_latencies.append(elapsed)
print(f" Request {i+1}: {elapsed:.0f}ms (status: {resp.status_code})")
time.sleep(2)
print(f" Promedio: {statistics.mean(inference_latencies):.0f}ms")
# Compilar reporte
report = {
"platform": "railway",
"url": APP_URL,
"date": "2026-03-08",
"health_check": {
"avg_ms": round(statistics.mean(latencies), 1),
"median_ms": round(statistics.median(latencies), 1),
"p95_ms": round(sorted(latencies)[9], 1),
"min_ms": round(min(latencies), 1),
"max_ms": round(max(latencies), 1),
},
"inference": {
"avg_ms": round(statistics.mean(inference_latencies), 1),
"median_ms": round(statistics.median(inference_latencies), 1),
"samples": len(inference_latencies),
},
"ci_cd": "GitHub Actions → Railway",
"status": "healthy",
}
print(f"\n=== Reporte Completo ===")
print(json.dumps(report, indent=2))
with open("deployment-metrics.json", "w") as f:
json.dump(report, f, indent=2)
print("\nGuardado en deployment-metrics.json")
Paso 7: Escribir documentación comparativa (15 min)
Con los datos recopilados, crea el documento comparativo:
# generate_comparison.py — Generar template de comparación con datos reales
import json
with open("deployment-metrics.json") as f:
metrics = json.load(f)
comparison_template = f"""# Deployment Comparison: DocuSearch AI
## Resumen Ejecutivo
Desplegué DocuSearch AI en **{metrics['platform']}** después de evaluar
Render, Railway y Fly.io en las cápsulas del Módulo 7. La decisión se
basó en mi decision matrix v2, donde {metrics['platform']} obtuvo el
score más alto para mi caso de uso (MVP, equipo de 2, presupuesto
limitado, necesidad de PostgreSQL + Redis integrados).
URL de producción: {metrics['url']}
CI/CD: {metrics['ci_cd']}
## Métricas de Producción
| Métrica | Valor |
|---------|-------|
| Health check avg | {metrics['health_check']['avg_ms']:.0f}ms |
| Health check p95 | {metrics['health_check']['p95_ms']:.0f}ms |
| Inference avg | {metrics['inference']['avg_ms']:.0f}ms |
| Status | {metrics['status']} |
## Comparativa de Plataformas Probadas
| Dimensión | Local (M2) | Lambda (M3) | Render (M7) | Railway (M7) | Fly.io (M7) |
|-----------|-----------|-------------|-------------|--------------|-------------|
| Setup time | [tu dato] | [tu dato] | [tu dato] | [tu dato] | [tu dato] |
| Deploy time | N/A | [tu dato] | [tu dato] | [tu dato] | [tu dato] |
| Coste/mes | $0 | [tu dato] | [tu dato] | [tu dato] | [tu dato] |
| DX (1-5) | [tu dato] | [tu dato] | [tu dato] | [tu dato] | [tu dato] |
| Limitación principal | No accesible | Cold start | Timeout 30s | Pricing variable | Más setup |
## Conclusión
[Tu justificación con datos reales]
"""
with open("deployment-comparison.md", "w") as f:
f.write(comparison_template)
print("Generado: deployment-comparison.md")
print("Completa los campos [tu dato] con tus mediciones reales.")
Paso 8: Completar decision matrix v2 (15 min)
# generate_matrix.py — Generar decision matrix v2 con cálculos
import json
criteria = {
"Coste mensual": 20,
"Developer experience": 15,
"Request timeout": 15,
"WebSocket/streaming": 15,
"Databases integradas": 10,
"RAM disponible": 10,
"Cold start": 10,
"CI/CD integration": 5,
}
evaluations = {
"AWS": {"Coste mensual": 3, "Developer experience": 2, "Request timeout": 5,
"WebSocket/streaming": 3, "Databases integradas": 4, "RAM disponible": 4,
"Cold start": 2, "CI/CD integration": 4},
"Render": {"Coste mensual": 4, "Developer experience": 3, "Request timeout": 2,
"WebSocket/streaming": 3, "Databases integradas": 3, "RAM disponible": 3,
"Cold start": 2, "CI/CD integration": 3},
"Railway": {"Coste mensual": 3, "Developer experience": 5, "Request timeout": 4,
"WebSocket/streaming": 4, "Databases integradas": 5, "RAM disponible": 4,
"Cold start": 3, "CI/CD integration": 4},
"Fly.io": {"Coste mensual": 5, "Developer experience": 3, "Request timeout": 4,
"WebSocket/streaming": 5, "Databases integradas": 3, "RAM disponible": 3,
"Cold start": 4, "CI/CD integration": 4},
}
max_possible = sum(criteria.values()) * 5
print(f"=== Decision Matrix v2 ===")
print(f"Max possible score: {max_possible}\n")
totals = {}
for platform in evaluations:
total = sum(
criteria[c] * evaluations[platform][c]
for c in criteria
)
totals[platform] = total
for platform, score in sorted(totals.items(), key=lambda x: -x[1]):
pct = score / max_possible * 100
print(f" {platform:<12} {score:>4}/{max_possible} ({pct:.0f}%)")
winner = max(totals, key=totals.get)
print(f"\nRecomendación: {winner}")
Paso 9: Verificar completitud (10 min)
Ejecuta el checklist antes de considerar el proyecto terminado.
Checklist de Completitud
Entregable 1: App Desplegada
- URL pública accesible (HTTPS)
-
/healthretorna 200 con status "healthy" - Al menos un endpoint de inferencia AI funciona
- Variables de entorno configuradas como secrets (no hardcoded)
- CI/CD configurado: push a main → tests → deploy
- Health check configurado en la plataforma
- La app está corriendo (no en sleep/stopped)
Entregable 2: Documentación Comparativa
- Resumen ejecutivo (2-3 párrafos)
- Al menos 3 plataformas documentadas con datos reales
- Tabla comparativa con setup time, deploy time, coste, DX
- Comparación con local (M2) y serverless (M3)
- Conclusión con justificación
Entregable 3: Decision Matrix v2
- Contexto del proyecto con datos específicos
- 7-10 criterios con pesos que suman 100
- Cada peso tiene justificación
- Evaluación 1-5 para las 4 plataformas (AWS, Render, Railway, Fly.io)
- Scores ponderados calculados
- Recomendación con justificación
- Opciones descartadas con razón
- Condiciones de re-evaluación con métricas
- Migration path documentado
- Fecha de review programado
Calidad
- Los datos de la matrix vienen de experiencia real (no de la documentación)
- Las evaluaciones (1-5) tienen justificación concreta (no "es bueno")
- El documento de comparación incluye problemas encontrados (no solo lo positivo)
- El migration path es realista (pasos concretos, no "migrar a AWS")
- Podrías defender la elección en una conversación de 5 minutos
Troubleshooting Específico del Proyecto
"Mi matrix dice una plataforma pero ya estoy cómodo con otra"
Eso es válido. La matrix es una herramienta de decisión, no una obligación. Si la diferencia de scores es <10%, la familiaridad del equipo es un factor legítimo. Documéntalo: "Railway score 380 vs Fly.io 360. Elegí Railway porque el equipo ya lo conoce y la diferencia es marginal."
"No tengo dinero para el plan de pago — ¿puedo usar free tier?"
Sí, pero documenta las limitaciones. Si usas Render Free, documenta que el cold start de 30s afectaría producción real. Si usas Railway Trial, documenta que los $5 de crédito se agotan en una semana. El proyecto funciona con free tier — la documentación de limitaciones es parte del aprendizaje.
"Mi app no tiene tests — ¿puedo skipear el CI?"
No idealmente, pero puedes agregar tests mínimos:
# tests/test_health.py — el test más básico posible
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health():
response = client.get("/health")
assert response.status_code == 200
Un test es mejor que ninguno. El CI/CD pipeline con al menos un test es parte del entregable.
"El deploy funciona pero el health check falla intermitentemente"
Causas comunes:
- Free tier sleep: La app duerme y el health check la encuentra dormida. Solución: upgrade o ping más frecuente.
- Memory limit: La app consume más RAM de la disponible y es killed por OOM. Solución: upgrade RAM o optimizar uso.
- Startup lento: La app tarda en iniciar y el health check es demasiado temprano. Solución: aumentar grace_period.
# Debug: verificar manualmente
curl -v https://tu-app.plataforma.com/health
# Mira el HTTP status code y response time
"Quiero incluir base de datos pero mi free tier no la soporta"
Opciones:
- Usar Supabase free tier como PostgreSQL externo (500 MB gratis)
- Usar Upstash free tier como Redis externo (10K commands/día gratis)
- Documentar que usarías PostgreSQL pero el free tier no lo incluye — esto es un dato real para tu matrix
# main.py — manejar DB opcional
DATABASE_URL = os.environ.get("DATABASE_URL")
if DATABASE_URL:
init_db()
log_to_db = True
else:
log_to_db = False
# Log a archivo o stdout como fallback
Validación del Deployment
Script de validación completo
Ejecuta este script para verificar que tu deployment cumple todos los requisitos antes de entregar:
# validate_deployment.py
import requests
import time
import sys
import json
APP_URL = sys.argv[1] if len(sys.argv) > 1 else "https://tu-app.plataforma.com"
RESULTS = {"passed": 0, "failed": 0, "tests": []}
def test(name: str, condition: bool, detail: str = ""):
status = "PASS" if condition else "FAIL"
RESULTS["passed" if condition else "failed"] += 1
RESULTS["tests"].append({"name": name, "status": status, "detail": detail})
icon = "✅" if condition else "❌"
print(f" {icon} {name}")
if detail and not condition:
print(f" → {detail}")
print(f"=== Validando deployment: {APP_URL} ===\n")
# Test 1: Health check
print("[Endpoints]")
try:
r = requests.get(f"{APP_URL}/health", timeout=10)
test("Health check responds", r.status_code == 200, f"Status: {r.status_code}")
data = r.json()
test("Health includes 'status' field", "status" in data)
test("Health includes 'version' field", "version" in data)
test("Health includes 'platform' field", "platform" in data)
test("Platform is not 'local'", data.get("platform") != "local",
f"Platform: {data.get('platform')}")
except requests.exceptions.RequestException as e:
test("Health check responds", False, str(e))
# Test 2: HTTPS
print("\n[Security]")
test("URL uses HTTPS", APP_URL.startswith("https://"))
# Test 3: Inference endpoint
print("\n[AI Inference]")
try:
r = requests.post(
f"{APP_URL}/ask",
json={"question": "Test de validación", "max_tokens": 20},
timeout=60,
)
test("Inference endpoint responds", r.status_code == 200,
f"Status: {r.status_code}")
if r.status_code == 200:
data = r.json()
test("Response includes 'answer' field", "answer" in data)
test("Response includes 'model' field", "model" in data)
except requests.exceptions.RequestException as e:
test("Inference endpoint responds", False, str(e))
# Test 4: Latency
print("\n[Performance]")
start = time.time()
try:
r = requests.get(f"{APP_URL}/health", timeout=10)
latency = (time.time() - start) * 1000
test(f"Health latency < 1000ms", latency < 1000, f"{latency:.0f}ms")
except Exception:
test("Health latency < 1000ms", False, "Request failed")
# Test 5: Consistent responses
print("\n[Reliability]")
success_count = 0
for i in range(5):
try:
r = requests.get(f"{APP_URL}/health", timeout=10)
if r.status_code == 200:
success_count += 1
except Exception:
pass
time.sleep(0.5)
test(f"5/5 health checks pass", success_count == 5,
f"{success_count}/5 passed")
# Summary
print(f"\n{'='*50}")
print(f"Resultado: {RESULTS['passed']} passed, {RESULTS['failed']} failed")
if RESULTS["failed"] == 0:
print("🎉 Todos los tests pasaron — deployment validado")
else:
print("⚠️ Hay tests fallidos — revisa antes de entregar")
sys.exit(1)
# Ejecutar validación
python validate_deployment.py https://tu-app.tu-plataforma.com
# Output esperado:
# === Validando deployment: https://docusearch-ai-production.up.railway.app ===
#
# [Endpoints]
# ✅ Health check responds
# ✅ Health includes 'status' field
# ✅ Health includes 'version' field
# ✅ Health includes 'platform' field
# ✅ Platform is not 'local'
#
# [Security]
# ✅ URL uses HTTPS
#
# [AI Inference]
# ✅ Inference endpoint responds
# ✅ Response includes 'answer' field
# ✅ Response includes 'model' field
#
# [Performance]
# ✅ Health latency < 1000ms
#
# [Reliability]
# ✅ 5/5 health checks pass
#
# ==================================================
# Resultado: 11 passed, 0 failed
# 🎉 Todos los tests pasaron — deployment validado
Verificar CI/CD
# Verificar que el pipeline de GitHub Actions funciona
# 1. Haz un cambio menor (agregar comentario en main.py)
# 2. Push a main
# 3. Ve a GitHub → Actions → verifica que el workflow se ejecuta
# 4. Verifica que el deploy se completa y el health check pasa
# Desde terminal:
git log --oneline -5
# Verificar que los últimos commits tienen ✓ (Actions passed)
# Via GitHub CLI:
gh run list --limit 5
# Verificar que los últimos runs son "completed" con "success"
Resultado Funcional
Al completar este proyecto, tendrás:
deployment-cloud-guide/module-07/
├── app/
│ ├── main.py # Tu app AI
│ ├── Dockerfile # Container listo para producción
│ ├── requirements.txt
│ └── tests/
│ └── test_app.py # Tests básicos
├── .github/
│ └── workflows/
│ └── deploy.yml # CI/CD pipeline
├── [render.yaml | railway.toml | fly.toml] # Config de plataforma
├── deployment-comparison.md # Comparativa de experiencias
├── decision-matrix-v2.md # Matrix v2 completa
└── deployment-metrics.json # Datos medidos (opcional)
URL pública: https://tu-app.tu-plataforma.com
├── GET /health → {"status": "healthy", ...}
├── POST /ask → {"answer": "...", "model": "...", ...}
└── CI/CD: push → test → deploy → health check ✅
Este proyecto es:
- Portfolio-worthy: Una app AI desplegada en producción con CI/CD y documentación de decisiones
- Reutilizable: El framework de decision matrix aplica a cualquier proyecto futuro
- Extensible: Se integra directamente en el Módulo 8 (Proyecto Integrador)
- Defendible: Cualquier ingeniero puede revisar tu decision matrix y entender tu razonamiento
Conexión con la Guía
¿Qué sigue?
Con tu app desplegada y tu decision matrix v2 completada:
Módulo 8: Proyecto Integrador — Deployed AI System
El M8 integra TODO lo que has aprendido:
Módulo 2: Docker Compose → containerización
+
Módulo 3: Lambda → serverless
+
Módulo 4: LocalStack → desarrollo sin coste
+
Módulo 5: AWS → servicios cloud
+
Módulo 6: Migration → patrones de migración
+
Módulo 7: Plataformas → la plataforma que elegiste aquí
↓
Módulo 8: Sistema AI desplegado, validado, documentado
La plataforma que elegiste en este módulo puede ser la plataforma de producción en M8. Si tu decision matrix dice Railway, el M8 usa Railway. La documentación que escribiste aquí (comparativa + matrix) se incorpora al M8.
Decision Matrix v1 (M1) → framework base
↓
Decision Matrix v2 (M7) → extendida con plataformas
↓
Decision Matrix v_final (M8) → aplicada y verificada con sistema real
Tu siguiente paso es el M8: tomar tu app AI, integrar los componentes de toda la guía (Docker + CI/CD + cloud services + plataforma elegida), y producir un sistema desplegado, validado y documentado para producción.
Recursos para el Proyecto
- Architecture Decision Records (ADR) — Formato para documentar decisiones
- Render Documentation — Si elegiste Render
- Railway Documentation — Si elegiste Railway
- Fly.io Documentation — Si elegiste Fly.io
- GitHub Actions — Referencia de CI/CD
- Supabase Free Tier — PostgreSQL gratuito como alternativa