academia/docs/DEPLOYMENT.md

420 lines
9.2 KiB
Markdown

# 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
```dockerfile
# 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
```dockerfile
# 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
```nginx
# 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
```yaml
# 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
```bash
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
```bash
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
```bash
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
```bash
# 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
```bash
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
```bash
./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
```bash
# 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)
```bash
# 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)
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```