Skip to content
English
On this page

Ejercicio: Sistema de Pedidos con AWS EKS - CI/CD

plaintext
sistema-pedidos/
├── .aws/
│   ├── buildspec/
│   │   ├── buildspec-dev.yml
│   │   ├── buildspec-stg.yml
│   │   └── buildspec-prod.yml
│   │
│   └── codebuild/
│       └── build-config.json

├── apps/
│   ├── pedidos-api/
│   │   ├── src/
│   │   │   ├── controllers/
│   │   │   ├── models/
│   │   │   └── services/
│   │   ├── Dockerfile
│   │   └── package.json
│   │
│   └── worker-processor/
│       ├── src/
│       ├── Dockerfile
│       └── package.json

├── infrastructure/
│   ├── terraform/
│   │   ├── eks/
│   │   │   ├── main.tf
│   │   │   └── variables.tf
│   │   │
│   │   ├── cicd/
│   │   │   ├── codecommit.tf
│   │   │   ├── codebuild.tf
│   │   │   └── codepipeline.tf
│   │   │
│   │   └── modules/
│   │       ├── networking/
│   │       └── database/
│   │
│   └── kubernetes/
│       ├── base/
│       │   ├── pedidos-api/
│       │   │   ├── deployment.yaml
│       │   │   ├── service.yaml
│       │   │   └── hpa.yaml
│       │   │
│       │   └── worker-processor/
│       │       ├── deployment.yaml
│       │       └── service.yaml
│       │
│       └── overlays/
│           ├── dev/
│           ├── stg/
│           └── prod/

├── scripts/
│   ├── setup-cicd.sh
│   ├── deploy.sh
│   └── test.sh

└── docs/
    ├── architecture.md
    └── deployment.md

Parte 1: Configuración de EKS y Base de Datos

En esta primera parte, configuraremos la infraestructura base con EKS y una base de datos Aurora PostgreSQL en modo clúster para alta disponibilidad.

1. Configuración de EKS

hcl
# infrastructure/terraform/eks/main.tf

provider "aws" {
  region = var.region
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 19.0"

  cluster_name    = "${var.environment}-cluster"
  cluster_version = "1.27"

  vpc_id     = var.vpc_id
  subnet_ids = var.private_subnet_ids

  cluster_endpoint_public_access = true

  eks_managed_node_groups = {
    general = {
      min_size     = var.environment == "prod" ? 3 : 2
      max_size     = var.environment == "prod" ? 6 : 4
      desired_size = var.environment == "prod" ? 3 : 2

      instance_types = ["t3.medium"]
      capacity_type  = "ON_DEMAND"

      labels = {
        Environment = var.environment
        Role       = "general"
      }

      tags = {
        Environment = var.environment
      }
    }
  }

  # Addons
  cluster_addons = {
    coredns = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    vpc-cni = {
      most_recent = true
    }
  }

  # OIDC Provider
  enable_irsa = true

  tags = {
    Environment = var.environment
  }
}

# IAM Role para service account
resource "aws_iam_role" "eks_workload" {
  name = "${var.environment}-eks-workload"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRoleWithWebIdentity"
        Effect = "Allow"
        Principal = {
          Federated = module.eks.oidc_provider_arn
        }
      }
    ]
  })
}

# Política para acceder a Parameter Store
resource "aws_iam_role_policy" "parameter_store" {
  name = "parameter-store-access"
  role = aws_iam_role.eks_workload.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ssm:GetParameter",
          "ssm:GetParameters",
          "ssm:GetParametersByPath"
        ]
        Resource = [
          "arn:aws:ssm:${var.region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}/*"
        ]
      }
    ]
  })
}

2. Base de Datos Aurora PostgreSQL

hcl
# infrastructure/terraform/modules/database/main.tf

resource "aws_rds_cluster" "aurora_cluster" {
  cluster_identifier     = "${var.environment}-aurora-cluster"
  engine                = "aurora-postgresql"
  engine_version        = "13.8"
  database_name         = "pedidos"
  master_username       = "admin"
  master_password       = var.database_password
  
  backup_retention_period = var.environment == "prod" ? 7 : 1
  preferred_backup_window = "03:00-04:00"
  
  vpc_security_group_ids = [aws_security_group.aurora.id]
  db_subnet_group_name   = aws_db_subnet_group.aurora.name
  
  skip_final_snapshot    = var.environment != "prod"

  serverlessv2_scaling_configuration {
    min_capacity = var.environment == "prod" ? 2 : 0.5
    max_capacity = var.environment == "prod" ? 8 : 2
  }

  tags = {
    Environment = var.environment
  }
}

resource "aws_rds_cluster_instance" "aurora_instances" {
  count               = var.environment == "prod" ? 3 : 2
  identifier          = "${var.environment}-aurora-instance-${count.index + 1}"
  cluster_identifier  = aws_rds_cluster.aurora_cluster.id
  instance_class      = "db.serverless"
  engine              = aws_rds_cluster.aurora_cluster.engine
  engine_version      = aws_rds_cluster.aurora_cluster.engine_version

  tags = {
    Environment = var.environment
  }
}

resource "aws_security_group" "aurora" {
  name        = "${var.environment}-aurora-sg"
  description = "Security group for Aurora cluster"
  vpc_id      = var.vpc_id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [var.eks_security_group_id]
  }

  tags = {
    Environment = var.environment
  }
}

# Parameter Store para credenciales
resource "aws_ssm_parameter" "db_credentials" {
  name        = "/${var.environment}/database/credentials"
  description = "Credenciales de la base de datos"
  type        = "SecureString"
  value = jsonencode({
    username = aws_rds_cluster.aurora_cluster.master_username
    password = aws_rds_cluster.aurora_cluster.master_password
    host     = aws_rds_cluster.aurora_cluster.endpoint
    port     = 5432
    database = "pedidos"
  })

  tags = {
    Environment = var.environment
  }
}

3. Variables de Entorno en Parameter Store

bash
#!/bin/bash
# scripts/setup-parameters.sh

set -e

ENVIRONMENT=$1
REGION=$2

if [[ ! "$ENVIRONMENT" =~ ^(dev|stg|prod)$ ]]; then
    echo "El ambiente debe ser dev, stg o prod"
    exit 1
fi

# Configurar variables de entorno comunes
aws ssm put-parameter \
    --name "/${ENVIRONMENT}/app/common" \
    --value '{
        "LOG_LEVEL": "'$([ "$ENVIRONMENT" == "prod" ] && echo "info" || echo "debug")'",
        "NODE_ENV": "'$ENVIRONMENT'",
        "APP_PORT": "3000",
        "METRICS_ENABLED": "true"
    }' \
    --type "String" \
    --overwrite

# Configurar variables específicas de la aplicación
aws ssm put-parameter \
    --name "/${ENVIRONMENT}/app/pedidos-api" \
    --value '{
        "MAX_WORKERS": "'$([ "$ENVIRONMENT" == "prod" ] && echo "5" || echo "2")'",
        "CACHE_TTL": "'$([ "$ENVIRONMENT" == "prod" ] && echo "3600" || echo "300")'",
        "RATE_LIMIT": "'$([ "$ENVIRONMENT" == "prod" ] && echo "1000" || echo "100")'"
    }' \
    --type "String" \
    --overwrite

# Configurar variables de monitoreo
aws ssm put-parameter \
    --name "/${ENVIRONMENT}/monitoring" \
    --value '{
        "ALERT_EMAIL": "alerts@empresa.com",
        "METRICS_INTERVAL": "'$([ "$ENVIRONMENT" == "prod" ] && echo "60" || echo "300")'",
        "HEALTH_CHECK_INTERVAL": "'$([ "$ENVIRONMENT" == "prod" ] && echo "30" || echo "60")'"
    }' \
    --type "String" \
    --overwrite

echo "Parámetros configurados exitosamente para el ambiente $ENVIRONMENT"

Verificación Parte 1:

1. Verificar EKS:

  • [ ] Cluster creado correctamente
  • [ ] Nodos worker funcionando
  • [ ] IAM roles configurados
  • [ ] Add-ons instalados

2. Verificar Aurora:

  • [ ] Cluster de base de datos creado
  • [ ] Instancias replicadas funcionando
  • [ ] Seguridad configurada
  • [ ] Backups configurados

3. Verificar Parameter Store:

  • [ ] Variables almacenadas correctamente
  • [ ] Acceso desde EKS funcionando
  • [ ] Valores específicos por ambiente
  • [ ] Encriptación configurada

Parte 3: Servicio de Alta Disponibilidad y Mensajería

1. Configuración de Amazon MQ

hcl
# infrastructure/terraform/modules/messaging/main.tf

resource "aws_mq_broker" "pedidos_broker" {
  broker_name = "${var.environment}-pedidos-broker"

  engine_type        = "ActiveMQ"
  engine_version     = "5.16.5"
  host_instance_type = var.environment == "prod" ? "mq.m5.large" : "mq.t3.micro"
  
  deployment_mode = var.environment == "prod" ? "ACTIVE_STANDBY_MULTI_AZ" : "SINGLE_INSTANCE"

  security_groups = [aws_security_group.mq_security_group.id]
  subnet_ids      = var.private_subnet_ids

  configuration {
    id       = aws_mq_configuration.pedidos_config.id
    revision = aws_mq_configuration.pedidos_config.latest_revision
  }

  user {
    username = "admin"
    password = var.mq_password
  }

  maintenance_window_start_time {
    day_of_week = "SUNDAY"
    time_of_day = "03:00"
    time_zone   = "UTC"
  }

  encryption_options {
    use_aws_owned_key = false
    kms_key_id        = aws_kms_key.mq_encryption.arn
  }

  logs {
    general = true
    audit   = true
  }

  tags = {
    Environment = var.environment
  }
}

# Configuración del broker
resource "aws_mq_configuration" "pedidos_config" {
  name           = "${var.environment}-pedidos-config"
  engine_type    = "ActiveMQ"
  engine_version = "5.16.5"

  data = <<DATA
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<broker xmlns="http://activemq.apache.org/schema/core">
  <plugins>
    <forcePersistencyModeBrokerPlugin persistenceFlag="true"/>
    <statisticsBrokerPlugin/>
    <timeStampingBrokerPlugin ttlCeiling="86400000" zeroExpirationOverride="86400000"/>
  </plugins>
  <destinationPolicy>
    <policyMap>
      <policyEntries>
        <policyEntry topic=">" >
          <pendingMessageLimitStrategy>
            <constantPendingMessageLimitStrategy limit="1000"/>
          </pendingMessageLimitStrategy>
        </policyEntry>
        <policyEntry queue=">" >
          <deadLetterStrategy>
            <individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true"/>
          </deadLetterStrategy>
        </policyEntry>
      </policyEntries>
    </policyMap>
  </destinationPolicy>
</broker>
DATA

  tags = {
    Environment = var.environment
  }
}

# Security Group
resource "aws_security_group" "mq_security_group" {
  name        = "${var.environment}-mq-security-group"
  description = "Security group para Amazon MQ"
  vpc_id      = var.vpc_id

  ingress {
    from_port       = 61617
    to_port         = 61617
    protocol        = "tcp"
    security_groups = [var.eks_security_group_id]
  }

  ingress {
    from_port       = 8162
    to_port         = 8162
    protocol        = "tcp"
    security_groups = [var.eks_security_group_id]
  }

  tags = {
    Environment = var.environment
  }
}

# KMS Key para encriptación
resource "aws_kms_key" "mq_encryption" {
  description             = "KMS key para Amazon MQ"
  deletion_window_in_days = 7
  enable_key_rotation     = true

  tags = {
    Environment = var.environment
  }
}

# Parameter Store para credenciales de MQ
resource "aws_ssm_parameter" "mq_credentials" {
  name        = "/${var.environment}/messaging/credentials"
  description = "Credenciales para Amazon MQ"
  type        = "SecureString"
  value = jsonencode({
    username = "admin"
    password = var.mq_password
    endpoints = aws_mq_broker.pedidos_broker.instances[*].endpoints
  })

  tags = {
    Environment = var.environment
  }
}

2. Servicio de Procesamiento de Pedidos

typescript
// apps/pedidos-api/src/services/PedidosService.ts

import { Connection, Message } from 'activemq-client';
import { EventEmitter } from 'events';
import { Logger } from '../utils/logger';
import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';

interface PedidoMessage {
  id: string;
  clienteId: string;
  items: Array<{
    productoId: string;
    cantidad: number;
    precio: number;
  }>;
  total: number;
  estado: 'PENDIENTE' | 'PROCESANDO' | 'COMPLETADO' | 'FALLIDO';
  fechaCreacion: string;
}

export class PedidosService extends EventEmitter {
  private connection: Connection | null = null;
  private logger: Logger;
  private ssm: SSMClient;
  private readonly environment: string;

  constructor() {
    super();
    this.logger = new Logger('PedidosService');
    this.ssm = new SSMClient({});
    this.environment = process.env.ENVIRONMENT || 'dev';
  }

  async initialize(): Promise<void> {
    try {
      // Obtener credenciales de MQ desde Parameter Store
      const parameterResponse = await this.ssm.send(
        new GetParameterCommand({
          Name: `/${this.environment}/messaging/credentials`,
          WithDecryption: true
        })
      );

      const credentials = JSON.parse(parameterResponse.Parameter!.Value!);

      // Conectar a ActiveMQ
      this.connection = await this.conectarMQ(credentials);
      
      // Configurar listeners
      await this.configurarColas();
      
      this.logger.info('Servicio de pedidos inicializado correctamente');
    } catch (error) {
      this.logger.error('Error inicializando servicio de pedidos:', error);
      throw error;
    }
  }

  private async conectarMQ(credentials: any): Promise<Connection> {
    return new Promise((resolve, reject) => {
      const connection = new Connection({
        host: credentials.endpoints[0],
        port: 61617,
        username: credentials.username,
        password: credentials.password,
        ssl: true
      });

      connection.on('connect', () => {
        this.logger.info('Conectado a ActiveMQ');
        resolve(connection);
      });

      connection.on('error', (error) => {
        this.logger.error('Error en conexión MQ:', error);
        reject(error);
      });

      connection.on('disconnect', () => {
        this.logger.warn('Desconectado de ActiveMQ');
        this.reconectar();
      });
    });
  }

  private async configurarColas(): Promise<void> {
    if (!this.connection) return;

    // Cola de pedidos nuevos
    const pedidosQueue = this.connection.queue('pedidos.nuevos');
    pedidosQueue.subscribe(async (message: Message) => {
      await this.procesarPedido(message);
    });

    // Cola de actualización de estado
    const estadosQueue = this.connection.queue('pedidos.estados');
    estadosQueue.subscribe(async (message: Message) => {
      await this.actualizarEstadoPedido(message);
    });
  }

  private async procesarPedido(message: Message): Promise<void> {
    try {
      const pedido: PedidoMessage = JSON.parse(message.content.toString());
      this.logger.info(`Procesando pedido: ${pedido.id}`);

      // Validar stock
      await this.validarStock(pedido);

      // Procesar pago
      await this.procesarPago(pedido);

      // Actualizar estado
      await this.actualizarEstado(pedido.id, 'COMPLETADO');

      message.ack();
    } catch (error) {
      this.logger.error(`Error procesando pedido: ${error}`);
      message.nack();
    }
  }

  private async actualizarEstadoPedido(message: Message): Promise<void> {
    try {
      const actualizacion = JSON.parse(message.content.toString());
      await this.actualizarEstado(actualizacion.pedidoId, actualizacion.estado);
      message.ack();
    } catch (error) {
      this.logger.error(`Error actualizando estado: ${error}`);
      message.nack();
    }
  }

  private async reconectar(): Promise<void> {
    this.logger.info('Intentando reconexión...');
    try {
      const parameterResponse = await this.ssm.send(
        new GetParameterCommand({
          Name: `/${this.environment}/messaging/credentials`,
          WithDecryption: true
        })
      );

      const credentials = JSON.parse(parameterResponse.Parameter!.Value!);
      this.connection = await this.conectarMQ(credentials);
      await this.configurarColas();
    } catch (error) {
      this.logger.error('Error en reconexión:', error);
      setTimeout(() => this.reconectar(), 5000);
    }
  }

  async cerrar(): Promise<void> {
    if (this.connection) {
      await this.connection.close();
    }
  }
}

3. Configuración de Kubernetes para Alta Disponibilidad

yaml
# infrastructure/kubernetes/base/pedidos-api/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pedidos-api
  labels:
    app: pedidos-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pedidos-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: pedidos-api
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "3000"
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: pedidos-api
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - pedidos-api
              topologyKey: kubernetes.io/hostname
      containers:
      - name: pedidos-api
        image: ${ECR_REPOSITORY_URL}:${IMAGE_TAG}
        ports:
        - containerPort: 3000
        env:
        - name: ENVIRONMENT
          value: ${ENVIRONMENT}
        envFrom:
        - configMapRef:
            name: pedidos-config
        resources:
          requests:
            cpu: "100m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 20

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: pedidos-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: pedidos-api
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
    scaleUp:
      stabilizationWindowSeconds: 60

---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: pedidos-api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: pedidos-api

Verificación Parte 3:

1. Verificar Amazon MQ:

  • [ ] Broker creado correctamente
  • [ ] Alta disponibilidad configurada
  • [ ] Seguridad implementada
  • [ ] Conexión desde EKS establecida

2. Verificar Servicio de Pedidos:

  • [ ] Procesamiento de mensajes funcionando
  • [ ] Manejo de errores implementado
  • [ ] Reconexión automática activa
  • [ ] Logs configurados

3. Verificar Alta Disponibilidad:

  • [ ] Pods distribuidos correctamente
  • [ ] Auto-scaling funcionando
  • [ ] Tolerancia a fallos activa
  • [ ] Recursos asignados correctamente

Parte 4: Monitoreo y Alertas

1. Configuración de CloudWatch

hcl
# infrastructure/terraform/modules/monitoring/main.tf

# Dashboard principal
resource "aws_cloudwatch_dashboard" "pedidos_dashboard" {
  dashboard_name = "${var.environment}-pedidos-dashboard"

  dashboard_body = jsonencode({
    widgets = [
      {
        type   = "metric"
        x      = 0
        y      = 0
        width  = 12
        height = 6
        properties = {
          metrics = [
            ["AWS/ApplicationELB", "RequestCount", "LoadBalancer", var.alb_name],
            [".", "HTTPCode_Target_4XX_Count", ".", "."],
            [".", "HTTPCode_Target_5XX_Count", ".", "."]
          ]
          period = 300
          stat   = "Sum"
          region = var.region
          title  = "API Requests"
        }
      },
      {
        type   = "metric"
        x      = 12
        y      = 0
        width  = 12
        height = 6
        properties = {
          metrics = [
            ["EKS/ContainerInsights", "pod_cpu_utilization", "ClusterName", var.cluster_name],
            [".", "pod_memory_utilization", ".", "."]
          ]
          period = 300
          stat   = "Average"
          region = var.region
          title  = "Pod Resources"
        }
      },
      {
        type   = "metric"
        x      = 0
        y      = 6
        width  = 12
        height = 6
        properties = {
          metrics = [
            ["AWS/AmazonMQ", "CpuUtilization", "Broker", var.mq_broker_name],
            [".", "MemoryUsage", ".", "."],
            [".", "TotalMessageCount", ".", "."]
          ]
          period = 300
          stat   = "Average"
          region = var.region
          title  = "MQ Metrics"
        }
      }
    ]
  })
}

# Alarmas para el servicio
resource "aws_cloudwatch_metric_alarm" "api_error_rate" {
  alarm_name          = "${var.environment}-api-error-rate"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "HTTPCode_Target_5XX_Count"
  namespace           = "AWS/ApplicationELB"
  period             = "300"
  statistic          = "Sum"
  threshold          = var.environment == "prod" ? "10" : "20"
  alarm_description  = "API error rate is too high"
  alarm_actions      = [aws_sns_topic.alerts.arn]

  dimensions = {
    LoadBalancer = var.alb_name
  }
}

resource "aws_cloudwatch_metric_alarm" "pod_cpu_high" {
  alarm_name          = "${var.environment}-pod-cpu-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "3"
  metric_name         = "pod_cpu_utilization"
  namespace           = "EKS/ContainerInsights"
  period             = "300"
  statistic          = "Average"
  threshold          = "80"
  alarm_description  = "Pod CPU utilization is too high"
  alarm_actions      = [aws_sns_topic.alerts.arn]

  dimensions = {
    ClusterName = var.cluster_name
  }
}

resource "aws_cloudwatch_metric_alarm" "mq_connection_count" {
  alarm_name          = "${var.environment}-mq-connections"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "CurrentConnectionsCount"
  namespace           = "AWS/AmazonMQ"
  period             = "300"
  statistic          = "Average"
  threshold          = "80"
  alarm_description  = "Too many MQ connections"
  alarm_actions      = [aws_sns_topic.alerts.arn]

  dimensions = {
    Broker = var.mq_broker_name
  }
}

# SNS Topic para alertas
resource "aws_sns_topic" "alerts" {
  name = "${var.environment}-pedidos-alerts"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "email"
  endpoint  = var.alert_email
}

2. Configuración de Prometheus y Grafana

yaml
# infrastructure/kubernetes/base/monitoring/prometheus.yaml

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
  namespace: monitoring
spec:
  serviceAccountName: prometheus
  podMonitorSelector: {}
  serviceMonitorSelector: {}
  ruleSelector:
    matchLabels:
      app: pedidos-api
  resources:
    requests:
      memory: 400Mi
    limits:
      memory: 1Gi
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: gp2
        resources:
          requests:
            storage: 40Gi
  alerting:
    alertmanagers:
    - namespace: monitoring
      name: alertmanager
      port: web

---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: pedidos-rules
  namespace: monitoring
  labels:
    app: pedidos-api
spec:
  groups:
  - name: pedidos.rules
    rules:
    - alert: PedidosProcesadosBajo
      expr: rate(pedidos_procesados_total[5m]) < 1
      for: 5m
      labels:
        severity: warning
      annotations:
        description: "La tasa de procesamiento de pedidos está por debajo de lo normal"
        
    - alert: TiempoProcesamiento
      expr: histogram_quantile(0.95, rate(pedidos_tiempo_procesamiento_bucket[5m])) > 30
      for: 5m
      labels:
        severity: warning
      annotations:
        description: "El tiempo de procesamiento de pedidos es alto"

---
apiVersion: integreatly.org/v1alpha1
kind: Grafana
metadata:
  name: grafana
  namespace: monitoring
spec:
  ingress:
    enabled: true
    hostname: grafana.${domain_name}
  config:
    security:
      admin_password: ${grafana_password}
    auth:
      disable_login_form: false
  dashboardLabelSelector:
    - matchExpressions:
        - key: app
          operator: In
          values:
            - pedidos-api

---
apiVersion: integreatly.org/v1alpha1
kind: GrafanaDashboard
metadata:
  name: pedidos-dashboard
  namespace: monitoring
  labels:
    app: pedidos-api
spec:
  json: |
    {
      "annotations": {
        "list": []
      },
      "editable": true,
      "panels": [
        {
          "title": "Tasa de Pedidos",
          "type": "graph",
          "datasource": "Prometheus",
          "targets": [
            {
              "expr": "rate(pedidos_procesados_total[5m])",
              "legendFormat": "Pedidos/min"
            }
          ]
        },
        {
          "title": "Tiempo de Procesamiento",
          "type": "graph",
          "datasource": "Prometheus",
          "targets": [
            {
              "expr": "histogram_quantile(0.95, rate(pedidos_tiempo_procesamiento_bucket[5m]))",
              "legendFormat": "P95"
            }
          ]
        }
      ]
    }

3. Implementación de Métricas Personalizadas

typescript
// apps/pedidos-api/src/services/MetricsService.ts

import { Counter, Gauge, Histogram } from 'prom-client';
import { CloudWatch } from 'aws-sdk';

export class MetricsService {
  private cloudwatch: CloudWatch;
  private namespace: string;

  // Prometheus metrics
  private pedidosProcesados: Counter;
  private pedidosFallidos: Counter;
  private tiempoProcesamiento: Histogram;
  private queueSize: Gauge;

  constructor(environment: string) {
    this.cloudwatch = new CloudWatch();
    this.namespace = `Pedidos/${environment}`;

    // Inicializar métricas Prometheus
    this.pedidosProcesados = new Counter({
      name: 'pedidos_procesados_total',
      help: 'Total de pedidos procesados'
    });

    this.pedidosFallidos = new Counter({
      name: 'pedidos_fallidos_total',
      help: 'Total de pedidos fallidos'
    });

    this.tiempoProcesamiento = new Histogram({
      name: 'pedidos_tiempo_procesamiento',
      help: 'Tiempo de procesamiento de pedidos',
      buckets: [1, 5, 10, 30, 60, 120]
    });

    this.queueSize = new Gauge({
      name: 'pedidos_queue_size',
      help: 'Tamaño actual de la cola de pedidos'
    });
  }

  async registrarPedidoProcesado(tiempoProcesamiento: number): Promise<void> {
    // Prometheus
    this.pedidosProcesados.inc();
    this.tiempoProcesamiento.observe(tiempoProcesamiento);

    // CloudWatch
    await this.cloudwatch.putMetricData({
      Namespace: this.namespace,
      MetricData: [
        {
          MetricName: 'PedidosProcesados',
          Value: 1,
          Unit: 'Count'
        },
        {
          MetricName: 'TiempoProcesamiento',
          Value: tiempoProcesamiento,
          Unit: 'Seconds'
        }
      ]
    }).promise();
  }

  async registrarPedidoFallido(error: string): Promise<void> {
    // Prometheus
    this.pedidosFallidos.inc();

    // CloudWatch
    await this.cloudwatch.putMetricData({
      Namespace: this.namespace,
      MetricData: [
        {
          MetricName: 'PedidosFallidos',
          Value: 1,
          Unit: 'Count',
          Dimensions: [
            {
              Name: 'TipoError',
              Value: error
            }
          ]
        }
      ]
    }).promise();
  }

  async actualizarTamañoCola(tamaño: number): Promise<void> {
    // Prometheus
    this.queueSize.set(tamaño);

    // CloudWatch
    await this.cloudwatch.putMetricData({
      Namespace: this.namespace,
      MetricData: [
        {
          MetricName: 'TamañoCola',
          Value: tamaño,
          Unit: 'Count'
        }
      ]
    }).promise();
  }

  async registrarLatenciaMQ(latencia: number): Promise<void> {
    await this.cloudwatch.putMetricData({
      Namespace: this.namespace,
      MetricData: [
        {
          MetricName: 'LatenciaMQ',
          Value: latencia,
          Unit: 'Milliseconds'
        }
      ]
    }).promise();
  }

  async registrarEstadoConexion(conectado: boolean): Promise<void> {
    await this.cloudwatch.putMetricData({
      Namespace: this.namespace,
      MetricData: [
        {
          MetricName: 'EstadoConexionMQ',
          Value: conectado ? 1 : 0,
          Unit: 'Count'
        }
      ]
    }).promise();
  }
}

Verificación Parte 4:

1. Verificar CloudWatch:

  • [ ] Dashboard creado
  • [ ] Alarmas configuradas
  • [ ] Métricas recolectadas
  • [ ] Notificaciones funcionando

2. Verificar Prometheus/Grafana:

  • [ ] Prometheus desplegado
  • [ ] Grafana accesible
  • [ ] Dashboards configurados
  • [ ] Alertas activas

3. Verificar Métricas:

  • [ ] Métricas personalizadas registradas
  • [ ] Datos históricos disponibles
  • [ ] Latencias monitoreadas
  • [ ] Estados de conexión registrados

Continuaré con la Parte 5, que incluirá la documentación completa del sistema y las pruebas necesarias.

Parte 5: Documentación y Pruebas

1. Pruebas del Sistema

typescript
// tests/integration/pedidos.test.ts

import { PedidosService } from '../../src/services/PedidosService';
import { MetricsService } from '../../src/services/MetricsService';
import { Connection } from 'activemq-client';
import { faker } from '@faker-js/faker';

describe('Sistema de Pedidos - Pruebas de Integración', () => {
  let pedidosService: PedidosService;
  let metricsService: MetricsService;
  let connection: Connection;

  const crearPedidoPrueba = () => ({
    id: faker.string.uuid(),
    clienteId: faker.string.uuid(),
    items: Array.from({ length: faker.number.int({ min: 1, max: 5 }) }, () => ({
      productoId: faker.string.uuid(),
      cantidad: faker.number.int({ min: 1, max: 10 }),
      precio: faker.number.float({ min: 10, max: 1000 })
    })),
    total: 0,
    estado: 'PENDIENTE',
    fechaCreacion: new Date().toISOString()
  });

  beforeAll(async () => {
    pedidosService = new PedidosService();
    metricsService = new MetricsService('test');
    await pedidosService.initialize();
  });

  afterAll(async () => {
    await pedidosService.cerrar();
  });

  describe('Procesamiento de Pedidos', () => {
    test('debe procesar un pedido correctamente', async () => {
      const pedido = crearPedidoPrueba();
      const resultado = await pedidosService.procesarPedido(pedido);

      expect(resultado.estado).toBe('COMPLETADO');
      expect(resultado.fechaProcesamiento).toBeDefined();
    });

    test('debe manejar múltiples pedidos concurrentes', async () => {
      const pedidos = Array.from({ length: 10 }, () => crearPedidoPrueba());
      const resultados = await Promise.all(
        pedidos.map(pedido => pedidosService.procesarPedido(pedido))
      );

      resultados.forEach(resultado => {
        expect(resultado.estado).toBe('COMPLETADO');
      });
    });

    test('debe manejar errores de procesamiento', async () => {
      const pedidoInvalido = {
        ...crearPedidoPrueba(),
        items: []
      };

      await expect(
        pedidosService.procesarPedido(pedidoInvalido)
      ).rejects.toThrow();
    });
  });

  describe('Alta Disponibilidad', () => {
    test('debe reconectar automáticamente', async () => {
      await pedidosService.simularDesconexion();
      await new Promise(resolve => setTimeout(resolve, 5000));

      const pedido = crearPedidoPrueba();
      const resultado = await pedidosService.procesarPedido(pedido);

      expect(resultado.estado).toBe('COMPLETADO');
    });

    test('debe mantener el orden de los mensajes', async () => {
      const pedidos = Array.from({ length: 5 }, () => crearPedidoPrueba());
      const timestamps: number[] = [];

      pedidos.forEach((pedido, index) => {
        pedido.id = `orden-${index + 1}`;
      });

      await Promise.all(
        pedidos.map(async pedido => {
          const resultado = await pedidosService.procesarPedido(pedido);
          timestamps.push(new Date(resultado.fechaProcesamiento).getTime());
        })
      );

      // Verificar orden cronológico
      const ordenado = [...timestamps].sort((a, b) => a - b);
      expect(timestamps).toEqual(ordenado);
    });
  });

  describe('Métricas y Monitoreo', () => {
    test('debe registrar métricas correctamente', async () => {
      const pedido = crearPedidoPrueba();
      const inicio = Date.now();
      
      await pedidosService.procesarPedido(pedido);
      
      const metricas = await metricsService.obtenerMetricas();
      
      expect(metricas.pedidosProcesados).toBeGreaterThan(0);
      expect(metricas.tiempoProcesamiento).toBeLessThan(5000);
    });

    test('debe registrar errores en métricas', async () => {
      const pedidoInvalido = {
        ...crearPedidoPrueba(),
        total: -100
      };

      try {
        await pedidosService.procesarPedido(pedidoInvalido);
      } catch (error) {
        // Esperado
      }

      const metricas = await metricsService.obtenerMetricas();
      expect(metricas.pedidosFallidos).toBeGreaterThan(0);
    });
  });
});

2. Pruebas de Carga

javascript
// tests/load/pedidos-load.js

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';

const errorRate = new Rate('errors');

export const options = {
  stages: [
    { duration: '2m', target: 50 },   // Rampa hasta 50 usuarios
    { duration: '5m', target: 50 },   // Mantener 50 usuarios
    { duration: '2m', target: 100 },  // Aumentar a 100
    { duration: '5m', target: 100 },  // Mantener 100
    { duration: '2m', target: 150 },  // Pico de 150
    { duration: '5m', target: 150 },  // Mantener pico
    { duration: '3m', target: 0 },    // Reducción gradual
  ],
  thresholds: {
    'http_req_duration': ['p(95)<2000'],  // 95% peticiones bajo 2s
    'http_req_failed': ['rate<0.01'],     // Menos de 1% errores
    'errors': ['rate<0.05']               // Tasa de errores general
  },
};

const ENDPOINT = __ENV.API_URL || 'http://localhost:3000';

function generarPedido() {
  return {
    clienteId: `client-${Math.random().toString(36).substr(2, 9)}`,
    items: Array.from({ length: Math.floor(Math.random() * 5) + 1 }, () => ({
      productoId: `prod-${Math.random().toString(36).substr(2, 9)}`,
      cantidad: Math.floor(Math.random() * 5) + 1,
      precio: Math.floor(Math.random() * 1000) + 100
    }))
  };
}

export default function() {
  const pedido = generarPedido();

  // Crear pedido
  const resCreate = http.post(
    `${ENDPOINT}/pedidos`,
    JSON.stringify(pedido),
    {
      headers: { 'Content-Type': 'application/json' }
    }
  );

  check(resCreate, {
    'pedido creado': (r) => r.status === 201,
    'tiene ID': (r) => JSON.parse(r.body).id !== undefined,
  }) || errorRate.add(1);

  if (resCreate.status === 201) {
    const pedidoId = JSON.parse(resCreate.body).id;
    sleep(1);

    // Consultar estado
    const resStatus = http.get(`${ENDPOINT}/pedidos/${pedidoId}`);
    
    check(resStatus, {
      'consulta exitosa': (r) => r.status === 200,
      'estado válido': (r) => {
        const body = JSON.parse(r.body);
        return ['PENDIENTE', 'PROCESANDO', 'COMPLETADO'].includes(body.estado);
      },
    }) || errorRate.add(1);
  }

  sleep(Math.random() * 2 + 1);  // Espera aleatoria entre 1-3 segundos
}

export function handleSummary(data) {
  return {
    'stdout': JSON.stringify(data.metrics, null, 2),
    './load-test-results.json': JSON.stringify(data, null, 2),
  };
}

3. Documentación Técnica

Sistema de Gestión de Pedidos - Documentación Técnica

Descripción General

El Sistema de Gestión de Pedidos es una aplicación distribuida de alta disponibilidad diseñada para procesar pedidos en tiempo real utilizando una arquitectura basada en microservicios desplegada en Amazon EKS.

Arquitectura

Componentes Principales

  1. API de Pedidos

    • Servicio REST para la gestión de pedidos
    • Implementado en Node.js/TypeScript
    • Manejo de autenticación y autorización
    • Validación de datos y reglas de negocio
  2. Sistema de Mensajería

    • Amazon MQ (ActiveMQ)
    • Configuración de alta disponibilidad
    • Manejo de colas y tópicos
    • Dead Letter Queue (DLQ)
  3. Base de Datos

    • Aurora PostgreSQL en modo clúster
    • Replicación multi-AZ
    • Backups automáticos
    • Point-in-time recovery
  4. Servicios de AWS

    • Amazon EKS para orquestación
    • AWS CodePipeline para CI/CD
    • CloudWatch para monitoreo
    • Parameter Store para configuración

Despliegue y Configuración

Requisitos Previos

  1. AWS CLI configurado
  2. kubectl instalado
  3. Helm instalado
  4. Acceso a AWS con permisos adecuados

Pasos de Despliegue

  1. Infraestructura Base
bash
cd infrastructure/terraform
terraform init
terraform apply -var-file=environments/${ENV}.tfvars
  1. Configuración de Kubernetes
bash
kubectl apply -k kubernetes/overlays/${ENV}
  1. Configuración de CI/CD
bash
./scripts/setup-cicd.sh ${ENV} ${REGION}

Alta Disponibilidad

Estrategias Implementadas

  1. Redundancia

    • Múltiples pods distribuidos
    • Multi-AZ deployment
    • Replicación de base de datos
    • Broker de mensajes redundante
  2. Escalado

    • HPA configurado
    • Escalado basado en CPU/memoria
    • Límites de recursos definidos
    • Buffer de capacidad
  3. Resiliencia

    • Circuit breakers
    • Retry policies
    • Graceful degradation
    • Health checks

Monitoreo y Alertas

Métricas Clave

  1. Aplicación

    • Tasa de procesamiento
    • Latencia de respuesta
    • Tasa de errores
    • Tamaño de cola
  2. Infraestructura

    • CPU/Memoria por pod
    • Conexiones de red
    • IOPS de base de datos
    • Estado del cluster

Alertas Configuradas

  1. Críticas

    • Error rate > 1%
    • Latencia p95 > 2s
    • CPU > 80%
    • Fallos de conexión
  2. Advertencias

    • Cola creciendo
    • Latencia aumentando
    • Reintentos frecuentes
    • Capacidad limitada

Seguridad

Medidas Implementadas

  1. Red

    • VPC aislada
    • Security Groups
    • Network Policies
    • TLS en tránsito
  2. Acceso

    • IAM roles
    • RBAC en Kubernetes
    • Secrets encryption
    • Audit logging

Operaciones

Tareas Comunes

  1. Despliegues
bash
# Actualizar aplicación
./scripts/deploy.sh ${ENV} ${VERSION}

# Rollback
./scripts/rollback.sh ${ENV} ${VERSION}
  1. Monitoreo
bash
# Ver logs
kubectl logs -l app=pedidos-api -f

# Métricas
kubectl top pods
  1. Troubleshooting
bash
# Verificar estado
./scripts/health-check.sh ${ENV}

# Debug
kubectl debug pod/pedidos-api-xxx -it --image=busybox

Mantenimiento

Tareas Periódicas

  1. Backups

    • Base de datos: diario
    • Configuración: con cambios
    • Logs: retención 30 días
    • Métricas: retención 90 días
  2. Actualizaciones

    • Kubernetes: trimestral
    • Dependencias: mensual
    • Sistema operativo: automático
    • Certificados: trimestral

Contacto y Soporte

  • Emergencias: +XX XXX-XXXX
  • Slack: #pedidos-support
  • Email: soporte@empresa.com
  • Documentación: /docs/pedidos

Referencias

Verificación Parte 5:

1. Verificar Pruebas:

  • [ ] Pruebas de integración pasando
  • [ ] Pruebas de carga exitosas
  • [ ] Cobertura de código adecuada
  • [ ] Reportes generados

2. Verificar Documentación:

  • [ ] Documentación técnica completa
  • [ ] Guías de operación disponibles
  • [ ] Procedimientos documentados
  • [ ] Diagramas actualizados

3. Verificar Sistema Completo:

  • [ ] Todos los componentes funcionando
  • [ ] Monitoreo activo
  • [ ] Alertas configuradas
  • [ ] Seguridad implementada

Lista de Verificación Final:

  1. Infraestructura

    • [ ] EKS configurado y funcionando
    • [ ] Alta disponibilidad verificada
    • [ ] Redes y seguridad configuradas
    • [ ] Variables de entorno protegidas
  2. CI/CD

    • [ ] Pipeline completo funcionando
    • [ ] Despliegues automatizados
    • [ ] Rollbacks probados
    • [ ] Ambientes separados
  3. Aplicación

    • [ ] APIs respondiendo correctamente
    • [ ] Procesamiento de pedidos funcionando
    • [ ] Mensajería configurada
    • [ ] Manejo de errores implementado
  4. Monitoreo

    • [ ] Dashboards disponibles
    • [ ] Alertas funcionando
    • [ ] Logs centralizados
    • [ ] Métricas

Continuaré con la verificación final del sistema y agregaré los procedimientos de mantenimiento y escalabilidad.

4. Procedimientos de Mantenimiento

Procedimientos de Mantenimiento y Escalabilidad

1. Actualizaciones del Sistema

1.1 Actualización de Kubernetes

bash
# Verificar versiones disponibles
aws eks get-updates \
    --name ${CLUSTER_NAME} \
    --region ${REGION}

# Crear plan de actualización
./scripts/update-plan.sh ${CLUSTER_NAME} ${TARGET_VERSION}

# Ejecutar actualización
./scripts/perform-update.sh ${CLUSTER_NAME} ${TARGET_VERSION}

1.2 Actualización de Aplicaciones

bash
# Actualizar dependencias
npm audit fix
npm update

# Actualizar imágenes base
./scripts/update-base-images.sh

# Aplicar actualizaciones de seguridad
./scripts/security-updates.sh

2. Procedimientos de Backup

2.1 Base de Datos

  • Backups automáticos diarios
  • Retención: 30 días en prod, 7 días en otros ambientes
  • Verificación mensual de restauración

2.2 Configuración

  • Backup antes de cambios mayores
  • Versionamiento en CodeCommit
  • Documentación de cambios

2.3 Logs y Métricas

  • Retención según ambiente:
    • PROD: 90 días
    • STG: 30 días
    • DEV: 7 días

3. Escalabilidad

3.1 Vertical

  • Monitorear uso de recursos
  • Ajustar límites de pods
  • Optimizar consultas
  • Analizar cuellos de botella

3.2 Horizontal

  • Revisar métricas de HPA
  • Ajustar thresholds
  • Balancear carga
  • Optimizar distribución

4. Monitoreo Proactivo

4.1 Diario

  • Revisar dashboards
  • Verificar alarmas
  • Analizar tendencias
  • Identificar anomalías

4.2 Semanal

  • Análisis de rendimiento
  • Revisión de logs
  • Verificación de backups
  • Pruebas de salud

4.3 Mensual

  • Análisis de capacidad
  • Revisión de seguridad
  • Optimización de costos
  • Actualizaciones planificadas

5. Procedimientos de Emergencia

5.1 Failover

  • Activación de DR
  • Cambio de región
  • Restauración de datos
  • Verificación de servicios

5.2 Rollback

  • Reversión de cambios
  • Restauración de configs
  • Verificación de integridad
  • Notificación a usuarios

6. Optimización

6.1 Performance

  • Análisis de métricas
  • Optimización de consultas
  • Ajuste de caché
  • Tuning de JVM

6.2 Costos

  • Revisión de recursos
  • Optimización de instancias
  • Análisis de uso
  • Recomendaciones de ahorro

5. Script de Verificación del Sistema

python
#!/usr/bin/env python3
# scripts/verify-system.py

import boto3
import kubernetes
import requests
import json
import sys
from datetime import datetime
from typing import Dict, List

class SystemVerifier:
    def __init__(self, environment: str, region: str):
        self.environment = environment
        self.region = region
        self.results = {
            "timestamp": datetime.now().isoformat(),
            "environment": environment,
            "status": "PENDING",
            "checks": []
        }
        
        # Inicializar clientes AWS
        self.eks = boto3.client('eks', region_name=region)
        self.cloudwatch = boto3.client('cloudwatch', region_name=region)
        self.mq = boto3.client('mq', region_name=region)
        
        # Configurar kubernetes
        self.k8s_config = kubernetes.config.load_kube_config()
        self.k8s_client = kubernetes.client.CoreV1Api()

    def verify_infrastructure(self) -> bool:
        try:
            # Verificar EKS
            cluster_status = self.eks.describe_cluster(
                name=f"{self.environment}-cluster"
            )
            self.add_check("EKS Cluster", cluster_status['cluster']['status'] == 'ACTIVE')

            # Verificar nodos
            nodes = self.k8s_client.list_node()
            nodes_ready = all(
                any(cond.type == 'Ready' and cond.status == 'True' 
                    for cond in node.status.conditions)
                for node in nodes.items
            )
            self.add_check("Kubernetes Nodes", nodes_ready)

            # Verificar MQ
            broker_status = self.mq.describe_broker(
                BrokerId=f"{self.environment}-broker"
            )
            self.add_check("Amazon MQ", broker_status['BrokerState'] == 'RUNNING')

            return all(check['status'] for check in self.results['checks'])
        except Exception as e:
            self.add_check("Infrastructure", False, str(e))
            return False

    def verify_applications(self) -> bool:
        try:
            # Verificar pods
            pods = self.k8s_client.list_namespaced_pod(
                namespace='default',
                label_selector='app=pedidos-api'
            )
            pods_ready = all(
                pod.status.phase == 'Running'
                for pod in pods.items
            )
            self.add_check("Application Pods", pods_ready)

            # Verificar endpoints
            services = self.k8s_client.list_namespaced_service(
                namespace='default',
                label_selector='app=pedidos-api'
            )
            endpoints_ready = all(
                len(svc.spec.ports) > 0
                for svc in services.items
            )
            self.add_check("Service Endpoints", endpoints_ready)

            return all(check['status'] for check in self.results['checks'])
        except Exception as e:
            self.add_check("Applications", False, str(e))
            return False

    def verify_monitoring(self) -> bool:
        try:
            # Verificar CloudWatch
            alarms = self.cloudwatch.describe_alarms()
            alarms_ok = all(
                alarm['StateValue'] != 'ALARM'
                for alarm in alarms['MetricAlarms']
            )
            self.add_check("CloudWatch Alarms", alarms_ok)

            # Verificar métricas
            metrics = self.cloudwatch.list_metrics(
                Namespace=f"Pedidos/{self.environment}"
            )
            metrics_ok = len(metrics['Metrics']) > 0
            self.add_check("Custom Metrics", metrics_ok)

            return all(check['status'] for check in self.results['checks'])
        except Exception as e:
            self.add_check("Monitoring", False, str(e))
            return False

    def verify_security(self) -> bool:
        try:
            # Verificar Network Policies
            network_policies = kubernetes.client.NetworkingV1Api().list_namespaced_network_policy(
                namespace='default'
            )
            policies_ok = len(network_policies.items) > 0
            self.add_check("Network Policies", policies_ok)

            # Verificar Secrets
            secrets = self.k8s_client.list_namespaced_secret(
                namespace='default',
                label_selector='app=pedidos-api'
            )
            secrets_ok = len(secrets.items) > 0
            self.add_check("Kubernetes Secrets", secrets_ok)

            return all(check['status'] for check in self.results['checks'])
        except Exception as e:
            self.add_check("Security", False, str(e))
            return False

    def add_check(self, name: str, status: bool, message: str = None) -> None:
        self.results['checks'].append({
            "name": name,
            "status": status,
            "message": message if message else "OK" if status else "Failed"
        })

    def run_verification(self) -> Dict:
        infrastructure_ok = self.verify_infrastructure()
        applications_ok = self.verify_applications()
        monitoring_ok = self.verify_monitoring()
        security_ok = self.verify_security()

        self.results['status'] = 'HEALTHY' if all([
            infrastructure_ok,
            applications_ok,
            monitoring_ok,
            security_ok
        ]) else 'UNHEALTHY'

        return self.results

    def save_results(self) -> None:
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f"verification_{self.environment}_{timestamp}.json"
        
        with open(filename, 'w') as f:
            json.dump(self.results, f, indent=2)
        
        print(f"Results saved to {filename}")

def main():
    if len(sys.argv) != 3:
        print("Usage: verify-system.py <environment> <region>")
        sys.exit(1)

    environment = sys.argv[1]
    region = sys.argv[2]

    verifier = SystemVerifier(environment, region)
    results = verifier.run_verification()
    verifier.save_results()

    if results['status'] != 'HEALTHY':
        sys.exit(1)

if __name__ == "__main__":
    main()

Verificación Final del Sistema

1. Estado General:

  • [ ] Todos los servicios activos
  • [ ] Métricas estables
  • [ ] Sin alarmas activas
  • [ ] Logs sin errores

2. Performance:

  • [ ] Latencia dentro de límites
  • [ ] Uso de recursos normal
  • [ ] Throughput adecuado
  • [ ] Escalamiento funcionando

3. Seguridad:

  • [ ] Todas las políticas activas
  • [ ] Certificados válidos
  • [ ] Accesos configurados
  • [ ] Auditoría activada

4. Operación:

  • [ ] Backups ejecutándose
  • [ ] Monitoreo activo
  • [ ] Alertas configuradas
  • [ ] Documentación actualizada

Recomendaciones Finales:

  1. Monitoreo Continuo

    • Revisar dashboards diariamente
    • Analizar tendencias semanalmente
    • Ajustar umbrales según necesidad
    • Mantener documentación actualizada
  2. Mantenimiento Preventivo

    • Actualizar dependencias regularmente
    • Realizar pruebas de carga mensualmente
    • Verificar backups semanalmente
    • Rotar secrets trimestralmente
  3. Mejora Continua

    • Analizar métricas de uso
    • Optimizar recursos
    • Actualizar documentación
    • Capacitar al equipo
  4. Plan de Contingencia

    • Mantener procedimientos actualizados
    • Realizar simulacros
    • Documentar incidentes
    • Actualizar contactos