academia/docs/DEPLOYMENT.md

9.2 KiB

Manual de Despliegue

Requisitos del Sistema

Componente Versión Mínima
.NET SDK 10.0
Node.js 22.x
SQL Server 2022
Docker 24.x
Docker Compose 2.x

Variables de Entorno

Backend (.NET)

Variable Descripción Ejemplo
ConnectionStrings__DefaultConnection Connection string SQL Server Server=db;Database=StudentEnrollment;...
ASPNETCORE_ENVIRONMENT Ambiente Production
ASPNETCORE_URLS URLs de escucha http://+:5000

Frontend (Angular)

Variable Descripción Ejemplo
API_URL URL del backend GraphQL https://api.example.com/graphql

Despliegue con Docker

1. Estructura de Archivos

deploy/
└── docker/
    ├── Dockerfile.api
    ├── Dockerfile.frontend
    ├── docker-compose.yml
    └── nginx.conf

2. Dockerfile Backend

# deploy/docker/Dockerfile.api
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY src/backend/ .
RUN dotnet restore Host/Host.csproj
RUN dotnet publish Host/Host.csproj -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .

# Non-root user
RUN adduser --disabled-password --gecos '' appuser
USER appuser

EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:5000/health || exit 1

ENTRYPOINT ["dotnet", "Host.dll"]

3. Dockerfile Frontend

# deploy/docker/Dockerfile.frontend
FROM node:22-alpine AS build
WORKDIR /app

COPY src/frontend/package*.json ./
RUN npm ci

COPY src/frontend/ .
RUN npm run build -- --configuration production

FROM nginx:alpine
COPY --from=build /app/dist/student-enrollment/browser /usr/share/nginx/html
COPY deploy/docker/nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

4. Nginx Configuration

# deploy/docker/nginx.conf
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;

    # SPA routing
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Proxy GraphQL
    location /graphql {
        proxy_pass http://api:5000/graphql;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }

    # Security headers
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
}

5. Docker Compose

# deploy/docker/docker-compose.yml
services:
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=${DB_PASSWORD}
    ports:
      - "1433:1433"
    volumes:
      - sqlserver-data:/var/opt/mssql
    healthcheck:
      test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P $${SA_PASSWORD} -Q "SELECT 1" -C
      interval: 10s
      timeout: 3s
      retries: 10

  api:
    build:
      context: ../..
      dockerfile: deploy/docker/Dockerfile.api
    environment:
      - ConnectionStrings__DefaultConnection=Server=db;Database=StudentEnrollment;User Id=sa;Password=${DB_PASSWORD};TrustServerCertificate=True
      - ASPNETCORE_ENVIRONMENT=Production
    ports:
      - "5000:5000"
    depends_on:
      db:
        condition: service_healthy

  frontend:
    build:
      context: ../..
      dockerfile: deploy/docker/Dockerfile.frontend
    ports:
      - "80:80"
    depends_on:
      - api

volumes:
  sqlserver-data:

6. Ejecutar

cd deploy/docker

# Crear archivo .env
echo "DB_PASSWORD=Your_Str0ng_P@ssword!" > .env

# Build e iniciar
docker-compose up -d --build

# Ver logs
docker-compose logs -f

# Detener
docker-compose down

Despliegue Manual

Backend

cd src/backend/Host

# Build producción
dotnet publish -c Release -o ./publish

# Configurar connection string
export ConnectionStrings__DefaultConnection="Server=..."

# Ejecutar migraciones
dotnet ef database update --project ../Adapters/Driven/Persistence

# Iniciar
cd publish
dotnet Host.dll

Frontend

cd src/frontend

# Build producción
ng build --configuration production

# Los archivos quedan en dist/student-enrollment/browser/
# Servir con cualquier servidor web (nginx, apache, etc.)

Checklist Pre-Producción

Seguridad

  • Connection strings en variables de entorno (no en código)
  • HTTPS habilitado
  • CORS configurado solo para dominios permitidos
  • Rate limiting activo
  • Security headers configurados
  • Logs sin datos sensibles

Performance

  • Response compression habilitado
  • Output caching configurado
  • Bundle Angular optimizado (< 200KB transferidos)
  • Índices de BD aplicados

Monitoreo

  • Health checks funcionando (/health)
  • Logs centralizados
  • Métricas de aplicación

Base de Datos

  • Backup configurado
  • Migraciones aplicadas
  • Datos seed cargados (5 profesores, 10 materias)

URLs de Verificación

Servicio URL Esperado
API Health http://api:5000/health 200 OK
GraphQL Playground http://api:5000/graphql Banana Cake Pop
Frontend http://frontend:80 App Angular

Rollback

# Docker
docker-compose down
docker-compose up -d --no-build  # Usa imágenes anteriores

# Manual
# Restaurar versión anterior de DLLs/archivos
# Rollback de migraciones si es necesario:
dotnet ef database update <MigrationName>

Despliegue en Kubernetes (k3s)

Estructura de Manifiestos

deploy/k3s/
├── namespace.yaml      # Namespace dedicado
├── secrets.yaml        # Credenciales BD
├── configmap.yaml      # Configuración
├── sqlserver.yaml      # Base de datos
├── api.yaml            # Backend GraphQL
├── frontend.yaml       # Frontend Angular
├── ingress.yaml        # Traefik ingress
├── ingress-tls.yaml    # TLS con cert-manager
├── hpa.yaml            # Autoscaling
├── networkpolicy.yaml  # Seguridad de red
├── kustomization.yaml  # Kustomize config
└── deploy.sh           # Script de despliegue

Requisitos k3s

  • k3s instalado y funcionando
  • kubectl configurado
  • Acceso al cluster
  • (Opcional) cert-manager para TLS

Despliegue Rápido

cd deploy/k3s

# Opción 1: Con script
./deploy.sh all        # Build + Deploy

# Opción 2: Con kustomize
kubectl apply -k .

# Verificar estado
kubectl get all -n student-enrollment

Comandos del Script

./deploy.sh build      # Construir imágenes Docker
./deploy.sh deploy     # Desplegar a k3s
./deploy.sh status     # Ver estado del cluster
./deploy.sh logs api   # Ver logs del API
./deploy.sh forward    # Port-forward para desarrollo
./deploy.sh delete     # Eliminar deployment

Configurar Secrets

# Editar secrets antes de desplegar
kubectl create secret generic student-secrets \
  --namespace=student-enrollment \
  --from-literal=db-password='TuPasswordSeguro123!' \
  --from-literal=db-connection-string='Server=sqlserver;Database=StudentEnrollment;User Id=sa;Password=TuPasswordSeguro123!;TrustServerCertificate=True' \
  --dry-run=client -o yaml > secrets.yaml

Acceso Local (Desarrollo)

# Agregar entrada en /etc/hosts
echo "127.0.0.1 students.local" | sudo tee -a /etc/hosts

# Port forward
./deploy.sh forward

# Acceder en:
# - Frontend: http://localhost:8080
# - API: http://localhost:5000/graphql

Habilitar TLS (Producción)

# 1. Instalar cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml

# 2. Editar ingress-tls.yaml con tu dominio y email

# 3. Aplicar ingress con TLS
kubectl apply -f ingress-tls.yaml -n student-enrollment

Scaling Manual

# Escalar API
kubectl scale deployment student-api -n student-enrollment --replicas=3

# Escalar Frontend
kubectl scale deployment student-frontend -n student-enrollment --replicas=2

Monitoreo

# Estado de pods
kubectl get pods -n student-enrollment -w

# Logs en tiempo real
kubectl logs -n student-enrollment -l app=student-api -f

# Eventos
kubectl get events -n student-enrollment --sort-by='.lastTimestamp'

# Recursos
kubectl top pods -n student-enrollment

Rollback en k3s

# Ver historial de deployments
kubectl rollout history deployment/student-api -n student-enrollment

# Rollback a versión anterior
kubectl rollout undo deployment/student-api -n student-enrollment

# Rollback a revisión específica
kubectl rollout undo deployment/student-api -n student-enrollment --to-revision=2

Troubleshooting

# Pod no inicia
kubectl describe pod <pod-name> -n student-enrollment

# Conectar a pod
kubectl exec -it <pod-name> -n student-enrollment -- /bin/sh

# Verificar conectividad BD
kubectl exec -it <api-pod> -n student-enrollment -- \
  curl -v telnet://sqlserver:1433

# Verificar ingress
kubectl describe ingress student-ingress -n student-enrollment