Saltearse al contenido

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: Builder
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Build TypeScript to JavaScript
RUN npm run build
# Stage 2: Production
FROM node:20-alpine
WORKDIR /app
# Copy only necessary files from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
# Expose port
EXPOSE 3001
# Health check
HEALTHCHECK --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 application
CMD ["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

ContainerPortProtocolPurpose
API Gateway3000HTTPREST API Endpoints
Orders MS3001HTTPMicroservicio internal API + health check
Provider MS3002HTTPMicroservicio internal API + health check
Notifications MS3003HTTPMicroservicio internal API + health check
Dashboard80HTTPStatic React app (NGINX)
MongoDB (if self-hosted)27017TCPBase de datos connection
Redis6379TCPMessage broker
Kafka9092TCPMessage broker

Environment Variables

API Gateway:

NODE_ENV=production
PORT=3000
JWT_SECRET=<secret>
JWT_EXPIRES_IN=24h
REDIS_HOST=redis-cluster
REDIS_PORT=6379
KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092
LOG_LEVEL=info
CORS_ORIGIN=https://dashboard.algesta.com

Orders Microservicio:

NODE_ENV=production
PORT=3001
MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-orders
REDIS_HOST=redis-cluster
REDIS_PORT=6379
KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092
AZURE_STORAGE_CONNECTION_STRING=<connection-string>
ASANA_API_KEY=<api-key>
DOCUSIGN_API_KEY=<api-key>
QSYSTEMS_API_URL=https://api.qsystems.com
ASSETS_API_URL=https://api.assets.algesta.com
LOG_LEVEL=info

Provider Microservicio:

NODE_ENV=production
PORT=3002
MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-provider
REDIS_HOST=redis-cluster
REDIS_PORT=6379
KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092
AZURE_STORAGE_CONNECTION_STRING=<connection-string>
AZURE_STORAGE_CONTAINER_NAME=provider-documents
LOG_LEVEL=info

Notifications Microservicio:

NODE_ENV=production
PORT=3003
MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/algesta-notifications
REDIS_HOST=redis-cluster
REDIS_PORT=6379
KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092
SENDGRID_API_KEY=<api-key>
SENDGRID_FROM_EMAIL=notifications@algesta.com
JELOU_API_URL=https://api.jelou.ai
JELOU_API_KEY=<api-key>
LOG_LEVEL=info

Health Check Endpoints

Each Microservicio exposes a /health Endpoint:

health.controller.ts
@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:

Ventana de terminal
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f orders-ms
# Stop all services
docker-compose down

Azure 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

algesta-ms-orders-nestjs/azure-pipelines.yml
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)

ConfigurationValue
Base de datosLocal MongoDB container
Message BrokerLocal Redis container
File StorageLocal filesystem or Azure Blob (emulator)
External APIsMock services or sandbox Endpoints
LoggingDEBUG level, console output
HTTPSNot required (HTTP only)

QA/Staging (Azure Test Environment)

ConfigurationValue
Base de datosMongoDB Atlas (shared tier) or Azure Cosmos DB
Message BrokerAzure Redis Cache (Basic tier)
File StorageAzure Blob Storage (test container)
External APIsSandbox/test Endpoints
LoggingINFO level, Azure Log Analytics
HTTPSYes, with Let’s Encrypt certificate
Resource Limits1 vCPU, 2GB RAM per container

Production (Azure Production Environment)

ConfigurationValue
Base de datosMongoDB Atlas (dedicated cluster) or Azure Cosmos DB
Message BrokerAzure Redis Cache (Standard tier) or Kafka cluster
File StorageAzure Blob Storage (production container)
External APIsProduction Endpoints
LoggingWARN/ERROR level, Azure Log Analytics + Application Insights
HTTPSYes, with purchased SSL certificate
Resource Limits2-4 vCPUs, 4-8GB RAM per container
Replicas2-3 replicas per Microservicio for high availability

Security Considerations

Secrets Management

Option 1: Azure Key Vault

// Use Azure Key Vault for secrets
import { 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)

kubernetes-secret.yml
apiVersion: v1
kind: Secret
metadata:
name: algesta-secrets
type: Opaque
stringData:
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):

deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-ms
spec:
replicas: 3 # 3 replicas for HA
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0

Load 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:

  1. Identify failing Despliegue via health checks
  2. Azure Pipelines: Trigger rollback stage
  3. Deploy previous working image from container registry
  4. Verify health checks pass
  5. Notify team of rollback

Base de datos Rollback:

  1. Identify data corruption or schema issue
  2. Restore from latest backup (MongoDB Atlas)
  3. Replay event stream from message broker (if applicable)
  4. Verify data integrity

Incident Response

  1. Detection: Health checks fail, alerts triggered
  2. Notification: Team notified via email/Slack
  3. Investigation: Check logs in Log Analytics
  4. Mitigation: Rollback or hotfix Despliegue
  5. Post-Mortem: Documento incident and prevention measures

Referencias Cruzadas

  • Dockerfile Locations (see source repositories):

    • algesta-api-gateway-nestjs/Dockerfile
    • algesta-ms-orders-nestjs/Dockerfile
    • algesta-ms-provider-nestjs/Dockerfile
    • algesta-ms-notifications-nestjs/Dockerfile
    • algesta-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: