12 KiB
12 KiB
Manual de Despliegue
Requisitos del Sistema
| Componente | Versión Mínima |
|---|---|
| .NET SDK | 10.0 |
| Node.js | 22.x |
| SQL Server | 2017 |
| 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://+:8080 |
JWT_SECRET_KEY |
REQUERIDO - Secret JWT (mín. 32 chars) | your-super-secret-key-minimum-32-chars |
JWT_ISSUER |
Emisor JWT | StudentEnrollmentApi |
JWT_AUDIENCE |
Audiencia JWT | StudentEnrollmentApp |
JWT_EXPIRATION_MINUTES |
Expiración token | 60 |
Frontend (Angular)
| Variable | Descripción | Ejemplo |
|---|---|---|
API_URL |
URL del backend GraphQL | https://api.example.com/graphql |
Desarrollo Local (SQLite)
| Variable | Descripción | Ejemplo |
|---|---|---|
USE_SQLITE |
Usar SQLite en lugar de SQL Server | true |
ConnectionStrings__DefaultConnection |
Path a archivo SQLite | Data Source=./data/dev.db |
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=Asde71.4Asde71.4" > .env
# Build e iniciar
docker-compose up -d --build
# Ver logs
docker-compose logs -f
# Detener
docker-compose down
Desarrollo Local (Sin Docker)
Script que levanta backend + frontend con SQLite (sin necesidad de SQL Server):
# Iniciar todo
./scripts/dev-start.sh start
# Ver estado
./scripts/dev-start.sh status
# Detener
./scripts/dev-start.sh stop
# Reiniciar
./scripts/dev-start.sh restart
Características:
- Backend usa SQLite en
./data/dev.db - No requiere Docker ni SQL Server
- Frontend en puerto 4200, Backend en puerto 5000
- Hot reload habilitado
- PIDs guardados para cleanup automático
CI/CD Pipeline (Gitea Actions)
Ubicación: .gitea/workflows/deploy.yaml
Trigger: Push a rama main
Flujo Automático
- Checkout código en Gitea runner
- Setup SSH hacia K3s master
- Sync código con rsync (excluye node_modules, dist, etc.)
- Build imágenes en paralelo (API + Frontend)
- Import a K3s containerd (
docker save | k3s ctr images import) - Apply manifiestos con Kustomize
- Rolling restart de deployments
- Health checks con curl
- Rollback automático si falla
Secretos Requeridos en Gitea
| Secreto | Descripción |
|---|---|
K3S_SSH_KEY |
Clave SSH privada para conectar a K3s master |
K3S_SUDO_PASS |
Password de sudo en K3s host |
Configuración
# Variables de entorno en el workflow
K3S_HOST: 100.67.198.92 # IP del master (hp62a)
NAMESPACE: academia
DOMAIN: academia.ingeniumcodex.com
Tiempo de despliegue: ~3-5 minutos desde push hasta producción
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)
- JWT_SECRET_KEY configurado (mínimo 32 caracteres)
- HTTPS habilitado
- CORS configurado solo para dominios permitidos
- Rate limiting activo (30 mutations/min, 100 queries/min)
- Security headers configurados
- Logs sin datos sensibles (Serilog filtra tokens/passwords)
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:8080/health |
200 OK |
| GraphQL Playground | http://api:8080/graphql |
Banana Cake Pop |
| Frontend | http://frontend:80 |
App Angular |
URLs de Producción (K3s)
| Servicio | URL |
|---|---|
| Frontend | https://academia.ingeniumcodex.com |
| API GraphQL | https://academia.ingeniumcodex.com/graphql |
| Health Check | https://academia.ingeniumcodex.com/health |
| Login | https://academia.ingeniumcodex.com/login |
| Registro | https://academia.ingeniumcodex.com/register |
| Dashboard | https://academia.ingeniumcodex.com/dashboard |
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: academia
├── secrets.yaml # Credenciales BD
├── configmap.yaml # Configuración
├── sqlserver.yaml # Base de datos
├── api.yaml # Backend GraphQL
├── frontend.yaml # Frontend Angular
├── ingress.yaml # Traefik IngressRoute + TLS
├── networkpolicy.yaml # Seguridad de red (incluido en kustomize)
├── hpa.yaml # Autoscaling (opcional, no incluido)
├── kustomization.yaml # Kustomize config
└── deploy.sh # Script de despliegue
Nota: networkpolicy.yaml está incluido en kustomization.yaml y aplica las siguientes reglas:
- Default deny: Bloquea todo tráfico entrante por defecto
- Frontend: Solo acepta tráfico desde Ingress
- API: Solo acepta tráfico desde Frontend e Ingress
- SQL Server: Solo acepta conexiones desde API
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 academia
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=academia \
--from-literal=db-password='Asde71.4Asde71.4' \
--from-literal=db-connection-string='Server=sqlserver;Database=StudentEnrollment;User Id=sa;Password=Asde71.4Asde71.4;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 academia
Scaling Manual
# Escalar API
kubectl scale deployment student-api -n academia --replicas=3
# Escalar Frontend
kubectl scale deployment student-frontend -n academia --replicas=2
Monitoreo
# Estado de pods
kubectl get pods -n academia -w
# Logs en tiempo real
kubectl logs -n academia -l app=student-api -f
# Eventos
kubectl get events -n academia --sort-by='.lastTimestamp'
# Recursos
kubectl top pods -n academia
Rollback en k3s
# Ver historial de deployments
kubectl rollout history deployment/student-api -n academia
# Rollback a versión anterior
kubectl rollout undo deployment/student-api -n academia
# Rollback a revisión específica
kubectl rollout undo deployment/student-api -n academia --to-revision=2
Troubleshooting
# Pod no inicia
kubectl describe pod <pod-name> -n academia
# Conectar a pod
kubectl exec -it <pod-name> -n academia -- /bin/sh
# Verificar conectividad BD
kubectl exec -it <api-pod> -n academia -- \
curl -v telnet://sqlserver:1433
# Verificar ingress
kubectl describe ingress student-ingress -n academia