Arquitectura de Despliegue
Descripción General
Este documento describe la arquitectura de despliegue para la Plataforma Algesta, incluyendo containerización con Docker, pipelines CI/CD con Azure Pipelines, hosting en la nube de Azure y configuraciones de entorno.
La plataforma sigue una estrategia de despliegue de microservicios containerizados con:
- Contenedores Docker para todos los servicios
- Azure Pipelines para CI/CD automatizado
- Infraestructura en la nube de Azure (Container Instances o AKS)
- MongoDB Atlas o MongoDB auto-hospedado para persistencia de datos
- Cluster Redis/Kafka para message broker
- Azure Blob Storage para almacenamiento de archivos
Diagrama de Despliegue
graph TB
subgraph "Entorno de Nube Azure"
subgraph "Capa de Cómputo"
APIGatewayContainer["Contenedor API Gateway<br/>Puerto: 3000<br/>Imagen: algesta-api-gateway:latest"]
OrdersMSContainer["Contenedor MS Órdenes<br/>Puerto: 3001<br/>Imagen: algesta-ms-orders:latest"]
ProviderMSContainer["Contenedor MS Proveedores<br/>Puerto: 3002<br/>Imagen: algesta-ms-provider:latest"]
NotificationsMSContainer["Contenedor MS Notificaciones<br/>Puerto: 3003<br/>Imagen: algesta-ms-notifications:latest"]
DashboardContainer["Contenedor Dashboard<br/>Puerto: 80<br/>Imagen: algesta-dashboard-react:latest"]
end
subgraph "Capa de Datos"
OrdersDB[("MongoDB Órdenes<br/>BD: algesta-orders<br/>Atlas o Auto-hospedado")]
ProviderDB[("MongoDB Proveedores<br/>BD: algesta-provider<br/>Atlas o Auto-hospedado")]
NotificationsDB[("MongoDB Notificaciones<br/>BD: algesta-notifications<br/>Atlas o Auto-hospedado")]
end
subgraph "Capa de Mensajería"
RedisKafka[("Cluster Redis/Kafka<br/>Transmisión de Eventos<br/>3 nodos")]
end
subgraph "Capa de Almacenamiento"
BlobStorage[("Azure Blob Storage<br/>Contenedor: provider-Documentos<br/>Contenedor: order-files")]
end
subgraph "Balanceador de Carga"
LoadBalancer["Azure Load Balancer<br/>Terminación HTTPS/TLS<br/>Certificado SSL"]
end
end
subgraph "Servicios Externos"
SendGrid["SendGrid API<br/>Entrega de Email"]
Jelou["Plataforma Jelou<br/>Bot de WhatsApp"]
Asana["Asana API"]
DocuSign["DocuSign API"]
Qsystems["Qsystems API"]
Assets["Assets System API"]
end
subgraph "Pipeline CI/CD"
AzurePipelines["Azure Pipelines<br/>Construcción, Pruebas, Despliegue"]
ContainerRegistry["Azure Container Registry<br/>Imágenes Docker"]
end
Users["Usuarios<br/>(Navegador/WhatsApp)"] -->|HTTPS| LoadBalancer
LoadBalancer --> DashboardContainer
LoadBalancer --> APIGatewayContainer
DashboardContainer -->|HTTP| APIGatewayContainer
APIGatewayContainer -->|MessagePattern| OrdersMSContainer
APIGatewayContainer -->|MessagePattern| ProviderMSContainer
APIGatewayContainer -->|MessagePattern| NotificationsMSContainer
OrdersMSContainer -->|Mongoose| OrdersDB
ProviderMSContainer -->|Mongoose| ProviderDB
NotificationsMSContainer -->|Mongoose| NotificationsDB
OrdersMSContainer -->|Pub/Sub| RedisKafka
ProviderMSContainer -->|Pub/Sub| RedisKafka
NotificationsMSContainer -->|Pub/Sub| RedisKafka
OrdersMSContainer -->|Azure SDK| BlobStorage
ProviderMSContainer -->|Azure SDK| BlobStorage
NotificationsMSContainer -->|HTTPS| SendGrid
NotificationsMSContainer -->|HTTPS| Jelou
OrdersMSContainer -->|HTTPS| Asana
OrdersMSContainer -->|HTTPS| DocuSign
OrdersMSContainer -->|HTTPS| Qsystems
OrdersMSContainer -->|HTTPS| Assets
AzurePipelines -->|Sube Imágenes| ContainerRegistry
ContainerRegistry -->|Descarga Imágenes| APIGatewayContainer
ContainerRegistry -->|Descarga Imágenes| OrdersMSContainer
ContainerRegistry -->|Descarga Imágenes| ProviderMSContainer
ContainerRegistry -->|Descarga Imágenes| NotificationsMSContainer
ContainerRegistry -->|Descarga Imágenes| DashboardContainer
style APIGatewayContainer fill:#1168bd,stroke:#0b4884,color:#ffffff
style OrdersMSContainer fill:#438dd5,stroke:#2e6295,color:#ffffff
style ProviderMSContainer fill:#438dd5,stroke:#2e6295,color:#ffffff
style NotificationsMSContainer fill:#438dd5,stroke:#2e6295,color:#ffffff
style DashboardContainer fill:#438dd5,stroke:#2e6295,color:#ffffff
style OrdersDB fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style ProviderDB fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style NotificationsDB fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style RedisKafka fill:#48dbfb,stroke:#2e9cba,color:#000000
style BlobStorage fill:#feca57,stroke:#cb9f46,color:#000000
style LoadBalancer fill:#6bcf7f,stroke:#56a466,color:#ffffff
Figura: Diagrama de Arquitectura de Despliegue - Despliegue en nube de Azure mostrando microservicios containerizados, capa de datos, mensajería y pipeline CI/CD
Container Configuration Details
Dockerfile Structure (Multi-Stage Build)
All Microservicios use a multi-stage Dockerfile for optimized image size:
# Example: algesta-ms-orders-nestjs/Dockerfile# Stage 1: BuilderFROM node:20-alpine AS builder
WORKDIR /app
# Copy package filesCOPY package*.json ./RUN npm ci --only=production
# Copy source codeCOPY . .
# Build TypeScript to JavaScriptRUN npm run build
# Stage 2: ProductionFROM node:20-alpine
WORKDIR /app
# Copy only necessary files from builderCOPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCOPY package*.json ./
# Expose portEXPOSE 3001
# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ CMD node -e "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# Run applicationCMD ["node", "dist/main"]Benefits of Multi-Stage Build:
- Smaller final image (no build tools, TypeScript source)
- Faster Despliegue
- Better security (fewer dependencies in production)
Exposed Ports
| Container | Port | Protocol | Purpose |
|---|---|---|---|
| API Gateway | 3000 | HTTP | REST API Endpoints |
| Orders MS | 3001 | HTTP | Microservicio internal API + health check |
| Provider MS | 3002 | HTTP | Microservicio internal API + health check |
| Notifications MS | 3003 | HTTP | Microservicio internal API + health check |
| Dashboard | 80 | HTTP | Static React app (NGINX) |
| MongoDB (if self-hosted) | 27017 | TCP | Base de datos connection |
| Redis | 6379 | TCP | Message broker |
| Kafka | 9092 | TCP | Message broker |
Environment Variables
API Gateway:
NODE_ENV=productionPORT=3000JWT_SECRET=<secret>JWT_EXPIRES_IN=24hREDIS_HOST=redis-clusterREDIS_PORT=6379KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092LOG_LEVEL=infoCORS_ORIGIN=https://dashboard.algesta.comOrders Microservicio:
NODE_ENV=productionPORT=3001MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-ordersREDIS_HOST=redis-clusterREDIS_PORT=6379KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092AZURE_STORAGE_CONNECTION_STRING=<connection-string>ASANA_API_KEY=<api-key>DOCUSIGN_API_KEY=<api-key>QSYSTEMS_API_URL=https://api.qsystems.comASSETS_API_URL=https://api.assets.algesta.comLOG_LEVEL=infoProvider Microservicio:
NODE_ENV=productionPORT=3002MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-providerREDIS_HOST=redis-clusterREDIS_PORT=6379KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092AZURE_STORAGE_CONNECTION_STRING=<connection-string>AZURE_STORAGE_CONTAINER_NAME=provider-documentsLOG_LEVEL=infoNotifications Microservicio:
NODE_ENV=productionPORT=3003MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-notificationsREDIS_HOST=redis-clusterREDIS_PORT=6379KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092SENDGRID_API_KEY=<api-key>SENDGRID_FROM_EMAIL=notifications@algesta.comJELOU_API_URL=https://api.jelou.aiJELOU_API_KEY=<api-key>LOG_LEVEL=infoHealth Check Endpoints
Each Microservicio exposes a /health Endpoint:
@Controller('health')export class HealthController { constructor( private readonly mongoHealth: MongoHealthIndicator, private readonly kafkaHealth: KafkaHealthIndicator, ) {}
@Get() async check(): Promise<HealthCheckResult> { return { status: 'ok', info: { database: await this.mongoHealth.isHealthy('mongodb'), messageBroker: await this.kafkaHealth.isHealthy('kafka'), }, timestamp: new Date().toISOString(), }; }}Docker Compose for Local Development
docker-compose.yml (for local development):
version: '3.8'
services: # MongoDB mongodb: image: mongo:7 ports: - "27017:27017" environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: password volumes: - mongodb_data:/data/db
# Redis redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data
# API Gateway api-gateway: build: context: ./algesta-api-gateway-nestjs dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - MONGODB_URI=mongodb://admin:password@mongodb:27017/algesta-gateway?authSource=admin - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - mongodb - redis volumes: - ./algesta-api-gateway-nestjs:/app - /app/node_modules
# Orders Microservice orders-ms: build: context: ./algesta-ms-orders-nestjs dockerfile: Dockerfile ports: - "3001:3001" environment: - NODE_ENV=development - MONGODB_URI=mongodb://admin:password@mongodb:27017/algesta-orders?authSource=admin - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - mongodb - redis volumes: - ./algesta-ms-orders-nestjs:/app - /app/node_modules
# Provider Microservice provider-ms: build: context: ./algesta-ms-provider-nestjs dockerfile: Dockerfile ports: - "3002:3002" environment: - NODE_ENV=development - MONGODB_URI=mongodb://admin:password@mongodb:27017/algesta-provider?authSource=admin - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - mongodb - redis volumes: - ./algesta-ms-provider-nestjs:/app - /app/node_modules
# Notifications Microservice notifications-ms: build: context: ./algesta-ms-notifications-nestjs dockerfile: Dockerfile ports: - "3003:3003" environment: - NODE_ENV=development - MONGODB_URI=mongodb://admin:password@mongodb:27017/algesta-notifications?authSource=admin - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: - mongodb - redis volumes: - ./algesta-ms-notifications-nestjs:/app - /app/node_modules
# React Dashboard dashboard: build: context: ./algesta-dashboard-react dockerfile: Dockerfile ports: - "5173:80" depends_on: - api-gateway volumes: - ./algesta-dashboard-react:/app - /app/node_modules
volumes: mongodb_data: redis_data:Usage:
# Start all servicesdocker-compose up -d
# View logsdocker-compose logs -f orders-ms
# Stop all servicesdocker-compose downAzure Pipelines CI/CD Flow
flowchart TD
Start([Code Push to main]) --> Trigger[Azure Pipelines Triggered]
Trigger --> Checkout[Checkout Source Code]
Checkout --> InstallDeps[Install Dependencies<br/>npm ci]
InstallDeps --> Lint[Run Linter<br/>npm run lint]
Lint --> Test[Run Unit Pruebas<br/>npm run test]
Test --> BuildTS[Build TypeScript<br/>npm run build]
BuildTS --> BuildDocker[Build Docker Image<br/>docker build -t image:tag .]
BuildDocker --> PushRegistry[Push to Azure Container Registry<br/>docker push]
PushRegistry --> DeployStaging{Deploy to Staging?}
DeployStaging -->|Yes| PullImageStaging[Pull Image on Staging]
PullImageStaging --> RunContainerStaging[Run Container on Staging]
RunContainerStaging --> HealthCheckStaging[Health Check]
HealthCheckStaging --> HealthOKStaging{Health OK?}
HealthOKStaging -->|Yes| IntegrationPruebas[Run Integration Pruebas]
HealthOKStaging -->|No| RollbackStaging[Rollback Staging]
RollbackStaging --> NotifyFailure[Notify Team of Failure]
IntegrationPruebas --> PruebasPass{Pruebas Pass?}
PruebasPass -->|Yes| DeployProd{Deploy to Production?}
PruebasPass -->|No| RollbackStaging
DeployProd -->|Yes| PullImageProd[Pull Image on Production]
PullImageProd --> RunContainerProd[Run Container on Production]
RunContainerProd --> HealthCheckProd[Health Check]
HealthCheckProd --> HealthOKProd{Health OK?}
HealthOKProd -->|Yes| Completo([Despliegue Completo])
HealthOKProd -->|No| RollbackProd[Rollback Production]
RollbackProd --> NotifyFailure
DeployStaging -->|No| Completo
DeployProd -->|No| Completo
style Start fill:#6bcf7f,stroke:#56a466,color:#ffffff
style Completo fill:#6bcf7f,stroke:#56a466,color:#ffffff
style RollbackStaging fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style RollbackProd fill:#ff6b6b,stroke:#cc5555,color:#ffffff
style NotifyFailure fill:#ff6b6b,stroke:#cc5555,color:#ffffff
Figura: Azure Pipelines CI/CD Flow - Automated build, test, and Despliegue pipeline with staging validation and production Despliegue
azure-pipelines.yml Example
trigger: branches: include: - main paths: include: - algesta-ms-orders-nestjs/*
pool: vmImage: 'ubuntu-latest'
variables: imageName: 'algesta-ms-orders' imageTag: '$(Build.BuildId)' containerRegistry: 'algestacr.azurecr.io'
stages: - stage: Build displayName: 'Build and Test' jobs: - job: BuildJob displayName: 'Build Job' steps: - task: NodeTool@0 inputs: versionSpec: '20.x' displayName: 'Install Node.js'
- script: | cd algesta-ms-orders-nestjs npm ci displayName: 'Install Dependencies'
- script: | cd algesta-ms-orders-nestjs npm run lint displayName: 'Run Linter'
- script: | cd algesta-ms-orders-nestjs npm run test displayName: 'Run Unit Tests'
- script: | cd algesta-ms-orders-nestjs npm run build displayName: 'Build TypeScript'
- task: Docker@2 inputs: command: 'build' repository: '$(containerRegistry)/$(imageName)' dockerfile: 'algesta-ms-orders-nestjs/Dockerfile' tags: | $(imageTag) latest displayName: 'Build Docker Image'
- task: Docker@2 inputs: command: 'push' repository: '$(containerRegistry)/$(imageName)' tags: | $(imageTag) latest displayName: 'Push Docker Image'
- stage: DeployStaging displayName: 'Deploy to Staging' dependsOn: Build condition: succeeded() jobs: - deployment: DeployStaging displayName: 'Deploy to Staging Environment' environment: 'staging' strategy: runOnce: deploy: steps: - task: AzureCLI@2 inputs: azureSubscription: 'Azure-Subscription' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az container restart --name orders-ms-staging --resource-group algesta-staging displayName: 'Restart Staging Container'
- script: | sleep 30 curl -f http://orders-ms-staging.azurecontainer.io:3001/health || exit 1 displayName: 'Health Check'
- stage: DeployProduction displayName: 'Deploy to Production' dependsOn: DeployStaging condition: succeeded() jobs: - deployment: DeployProduction displayName: 'Deploy to Production Environment' environment: 'production' strategy: runOnce: deploy: steps: - task: AzureCLI@2 inputs: azureSubscription: 'Azure-Subscription' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az container restart --name orders-ms-prod --resource-group algesta-prod displayName: 'Restart Production Container'
- script: | sleep 30 curl -f https://api.algesta.com/orders/health || exit 1 displayName: 'Health Check'Environment Configuration
Development (Local Docker Compose)
| Configuration | Value |
|---|---|
| Base de datos | Local MongoDB container |
| Message Broker | Local Redis container |
| File Storage | Local filesystem or Azure Blob (emulator) |
| External APIs | Mock services or sandbox Endpoints |
| Logging | DEBUG level, console output |
| HTTPS | Not required (HTTP only) |
QA/Staging (Azure Test Environment)
| Configuration | Value |
|---|---|
| Base de datos | MongoDB Atlas (shared tier) or Azure Cosmos DB |
| Message Broker | Azure Redis Cache (Basic tier) |
| File Storage | Azure Blob Storage (test container) |
| External APIs | Sandbox/test Endpoints |
| Logging | INFO level, Azure Log Analytics |
| HTTPS | Yes, with Let’s Encrypt certificate |
| Resource Limits | 1 vCPU, 2GB RAM per container |
Production (Azure Production Environment)
| Configuration | Value |
|---|---|
| Base de datos | MongoDB Atlas (dedicated cluster) or Azure Cosmos DB |
| Message Broker | Azure Redis Cache (Standard tier) or Kafka cluster |
| File Storage | Azure Blob Storage (production container) |
| External APIs | Production Endpoints |
| Logging | WARN/ERROR level, Azure Log Analytics + Application Insights |
| HTTPS | Yes, with purchased SSL certificate |
| Resource Limits | 2-4 vCPUs, 4-8GB RAM per container |
| Replicas | 2-3 replicas per Microservicio for high availability |
Security Considerations
Secrets Management
Option 1: Azure Key Vault
// Use Azure Key Vault for secretsimport { DefaultAzureCredential } from '@azure/identity';import { SecretClient } from '@azure/keyvault-secrets';
const credential = new DefaultAzureCredential();const client = new SecretClient('https://algesta-kv.vault.azure.net/', credential);
const mongoUri = await client.getSecret('mongodb-connection-string');Option 2: Environment Variables (Kubernetes Secrets)
apiVersion: v1kind: Secretmetadata: name: algesta-secretstype: OpaquestringData: MONGODB_URI: mongodb+srv://... SENDGRID_API_KEY: SG... JWT_SECRET: ...Network Security
- Virtual Network (VNet): All containers in same VNet
- Network Security Groups (NSGs): Restrict inbound/outbound traffic
- Private Endpoints: Base de datos and storage accessible only from VNet
- HTTPS/TLS: SSL termination at load balancer
- JWT Validation: All API requests validated at gateway
Container Security
- Non-root user: Containers run as non-root user
- Read-only filesystem: Where possible
- Security scanning: Automated vulnerability scanning of images
- Minimal base image: Alpine Linux for smaller attack surface
Monitoring and Observability
Logging Strategy
Winston Logger Configuration:
import { WinstonModule } from 'nest-winston';import * as winston from 'winston';
const logger = WinstonModule.createLogger({ transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.timestamp(), winston.format.json(), ), }), new winston.transports.File({ filename: 'logs/error.log', level: 'error', }), new winston.transports.File({ filename: 'logs/combined.log', }), ],});Log Aggregation:
- Azure Log Analytics for centralized logging
- Application Insights for performance monitoring
- Correlation IDs for request tracing across services
Health Checks
All services expose /health Endpoint checked by:
- Azure Load Balancer (liveness probe)
- Kubernetes (if using AKS)
- Azure Monitor
Métricas Collection
Potential Métricas:
- Request rate, latency, error rate (per Endpoint)
- Base de datos query performance
- Message broker queue depth
- Container CPU/memory usage
- External API call latency
Scalability and High Availability
Horizontal Scaling
Azure Container Instances:
- Manual scaling: Increase replica count
- Auto-scaling: Based on CPU/memory Métricas
Azure Kubernetes Service (AKS):
apiVersion: apps/v1kind: Deploymentmetadata: name: orders-msspec: replicas: 3 # 3 replicas for HA strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0Load Balancing
- Azure Load Balancer distributes traffic across replicas
- Round-robin or least connections algorithm
- Health check-based routing (unhealthy instances removed)
Base de datos Replication
MongoDB Atlas:
- Replica set with 3 nodes (primary + 2 secondaries)
- Automatic failover in case of primary failure
- Read preference: secondary for read-heavy queries
Message Broker Clustering
Redis:
- Redis Sentinel for high availability
- 3 nodes: 1 master + 2 replicas
Kafka:
- 3 broker cluster
- Replication factor: 3 for critical topics
Disaster Recovery
Backup Strategies
MongoDB:
- Automated daily backups (MongoDB Atlas)
- Point-in-time recovery (last 7 days)
- Manual snapshots before major Despliegues
Azure Blob Storage:
- Geo-redundant storage (GRS)
- Soft delete enabled (30-day retention)
Rollback Procedures
Application Rollback:
- Identify failing Despliegue via health checks
- Azure Pipelines: Trigger rollback stage
- Deploy previous working image from container registry
- Verify health checks pass
- Notify team of rollback
Base de datos Rollback:
- Identify data corruption or schema issue
- Restore from latest backup (MongoDB Atlas)
- Replay event stream from message broker (if applicable)
- Verify data integrity
Incident Response
- Detection: Health checks fail, alerts triggered
- Notification: Team notified via email/Slack
- Investigation: Check logs in Log Analytics
- Mitigation: Rollback or hotfix Despliegue
- Post-Mortem: Documento incident and prevention measures
Referencias Cruzadas
-
Dockerfile Locations (see source repositories):
algesta-api-gateway-nestjs/Dockerfilealgesta-ms-orders-nestjs/Dockerfilealgesta-ms-provider-nestjs/Dockerfilealgesta-ms-notifications-nestjs/Dockerfilealgesta-dashboard-react/Dockerfile
-
Docker Compose Files (see source repositories):
algesta-ms-orders-nestjs/docker-compose.yml
-
Azure Pipelines Files (see source repositories):
algesta-ms-orders-nestjs/azure-pipelines.yml
-
Arquitectura Documentoation: