Skip to content

Latest commit

 

History

History
798 lines (643 loc) · 22.2 KB

File metadata and controls

798 lines (643 loc) · 22.2 KB

Ray + Triton Microservices - Predicción de Ganancias

Sistema de predicción de ganancias con arquitectura de microservicios que combina Ray Serve como API Gateway con Triton Server como motor de inferencia independiente.

📋 Descripción General

Esta arquitectura implementa una separación completa entre la capa de API/preprocesamiento (Ray Serve) y la capa de inferencia (Triton Server), ejecutándose como servicios independientes que se comunican por red.

Arquitectura

Cliente → Ray Serve (Puerto 8080) → Preprocesamiento → Triton Server (Interno) → ONNX → Respuesta
          [Público]                                      [No expuesto]

Componentes:

  • Ray Serve: API Gateway, preprocesamiento, validación
  • Triton Server: Motor de inferencia ONNX (solo accesible internamente)
  • Docker Network: Comunicación segura entre servicios
  • Triton Client: Librería para comunicación Ray ↔ Triton

✅ Ventajas

  • Máxima seguridad: Triton Server no expuesto públicamente
  • Escalado independiente: Ray y Triton escalan por separado
  • Flexibilidad de Ray: Preprocesamiento fácil de modificar en Python
  • Performance de Triton: Inferencia optimizada con ONNX Runtime
  • Aislamiento: Fallos en un servicio no afectan directamente al otro
  • API Gateway robusto: Control total sobre validación, autenticación, rate limiting
  • Equipos separados: Un equipo maneja API, otro maneja modelos

⚠️ Consideraciones

  • Mayor complejidad: Dos servicios que coordinar y mantener
  • Latencia de red: Overhead de comunicación entre Ray y Triton (~5-10ms)
  • Más recursos: Requiere más memoria y CPU (dos contenedores)
  • Debugging complejo: Errores pueden ocurrir en múltiples lugares
  • Configuración adicional: Docker Compose, networking, variables de entorno

🎯 Ideal Para

  • ✅ Producción empresarial con requisitos de seguridad
  • ✅ Necesidad de escalar API y modelos independientemente
  • ✅ Equipos separados para desarrollo de API y ML
  • ✅ Casos donde el preprocesamiento cambia más frecuentemente que el modelo
  • ✅ Arquitectura de microservicios existente
  • ✅ Requisitos de aislamiento y fault tolerance

📦 Obtención de Modelos

Los modelos no están incluidos en este repositorio. Debes obtenerlos y colocarlos en las ubicaciones correctas.

Estructura Requerida

ray-triton-separados/
├── preprocessing_info.pkl          # ← DESCARGAR (para Ray)
└── model_repository/
    └── profit/
        ├── 1/
        │   ├── model.onnx          # ← DESCARGAR (para Triton)
        │   └── model.onnx.data     # ← DESCARGAR (si aplica)
        └── config.pbtxt            # ← Ya incluido

Opción 1: Descargar Modelos Pre-entrenados

# Descargar preprocessing info para Ray
gsutil cp gs://tu-bucket/models/serving/preprocessing_info.pkl ./

# Descargar modelo ONNX para Triton
gsutil cp gs://tu-bucket/models/serving/model.onnx \
    model_repository/serving/1/
gsutil cp gs://tu-bucket/models/serving/model.onnx.data \
    model_repository/serving/1/

# O desde S3 (AWS)
aws s3 cp s3://tu-bucket/models/serving/preprocessing_info.pkl ./
aws s3 cp s3://tu-bucket/models/serving/model.onnx \
    model_repository/serving/1/
aws s3 cp s3://tu-bucket/models/serving/model.onnx.data \
    model_repository/serving/1/

Opción 2: Entrenar tu Propio Modelo

import torch.onnx
import pickle

# 1. Exportar modelo ONNX para Triton
torch.onnx.export(
    model,
    dummy_input,
    "model_repository/serving/1/model.onnx",
    input_names=['input'],
    output_names=['output'],
    dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}
)

# 2. Guardar preprocessing_info.pkl para Ray
preprocessing_info = {
    'encoders': {...},              # LabelEncoders
    'scaler': scaler,               # StandardScaler
    'feature_columns': [...],       # Lista de 15 features
    'categorical_columns': [...]    # Lista de columnas categóricas
}

with open('preprocessing_info.pkl', 'wb') as f:
    pickle.dump(preprocessing_info, f)

Requisitos del Modelo

Para Ray Serve (preprocessing_info.pkl):

  • Debe contener encoders, scaler, feature_columns, categorical_columns
  • Compatible con Scikit-learn
  • Produce exactamente 15 features normalizadas

Para Triton Server (modelo ONNX):

  • Input: input - Tensor FP32 de forma [-1, 15]
  • Output: output - Tensor FP32 de forma [-1, 1]
  • Versión: Opset 11 o superior

🚀 Arquitectura del Sistema

El sistema utiliza una arquitectura de microservicios con separación clara de responsabilidades:

  1. Ray Serve:

    • API Gateway con FastAPI
    • Preprocesamiento de datos (feature engineering)
    • Validación de entrada
    • Comunicación con Triton
  2. Triton Server:

    • Servidor de inferencia optimizado
    • Ejecución del modelo ONNX
    • Alto rendimiento y baja latencia

Pipeline de Inferencia

graph LR
    A[Cliente] -->|Puerto 8080| B[Ray Serve<br/>FastAPI]
    B --> C[Preprocesamiento<br/>Local en Ray]
    C --> D[15 Features<br/>Procesadas]
    D -->|Triton Client| E[Triton Server<br/>🔒 No expuesto]
    E --> F[Modelo profit<br/>ONNX]
    F --> G[Predicción]
    G --> B
    B --> A
Loading

🔒 Arquitectura de Seguridad

  • Triton Server: Solo accesible internamente a través de la red Docker
  • Ray Serve: Único punto de entrada público (puerto 8080)
  • Preprocesamiento: Ejecutado localmente en Ray para mayor control y flexibilidad
  • Beneficios:
    • Triton Server protegido de acceso directo externo
    • Ray Serve actúa como API Gateway con validación y control de acceso
    • Preprocesamiento centralizado y versionado con preprocessing_info.pkl
    • Separación clara entre lógica de negocio (Ray) y modelo ML (Triton)

Componentes del Sistema

  1. Ray Serve (Preprocesamiento + API):

    • Carga encoders y scalers desde preprocessing_info.pkl
    • Extracción de características temporales
    • Codificación de variables categóricas
    • Normalización de features
    • Envío de 15 features procesadas a Triton
  2. Modelo profit (ONNX en Triton):

    • Recibe array de 15 features preprocesadas
    • Ejecuta inferencia con modelo ONNX optimizado
    • Retorna predicción de profit

📁 Estructura del Proyecto

ray-triton-separados/
├── serve.py                      # Servicio Ray Serve con FastAPI y preprocesamiento
├── preprocessing_info.pkl        # Encoders, scalers y metadata para preprocesamiento
├── requirements.txt              # Dependencias Python para Ray
├── config-serve-docker.yaml      # Configuración para Docker
├── serve_config.yaml             # Configuración de Ray Serve
├── Dockerfile.ray                # Imagen Docker para Ray Serve
├── Dockerfile.triton             # Imagen Docker para Triton Server
├── docker-compose.yaml           # Orquestación de servicios
├── model_repository/             # Repositorio de modelos para Triton
│   └── profit/                  # Modelo ONNX de predicción
│       ├── 1/                   # Versión 1
│       │   ├── model.onnx       # Modelo entrenado
│       │   └── model.onnx.data  # Datos del modelo
│       └── config.pbtxt         # Configuración de Triton
├── test_integration.py           # Tests de integración
└── README.md                    # Esta documentación

🔧 Configuración Rápida

1. Instalar Dependencias

# Crear entorno virtual
python3 -m venv venv
source venv/bin/activate  # En Windows: venv\Scripts\activate

# Instalar dependencias
pip install -r requirements.txt

2. Configurar Variables de Entorno

# URL del servidor Triton (default: localhost:8000)
export TRITON_SERVER_URL="localhost:8000"

# Nombre del modelo en Triton (default: profit)
export TRITON_MODEL_NAME="profit"

# Versión del modelo (default: 1)
export TRITON_MODEL_VERSION="1"

3. Ejecutar Triton Server

Opción A: Usando Docker Compose (Recomendado)

# Construir y ejecutar ambos servicios
docker-compose up --build

# O en modo detached
docker-compose up -d

Opción B: Ejecutar Servicios Separadamente

# 1. Construir y ejecutar Triton Server
docker build -f Dockerfile.triton -t triton-profit-server .
docker run --rm \
  -p 8000:8000 \
  -p 8001:8001 \
  -p 8002:8002 \
  triton-profit-server

# 2. En otra terminal, ejecutar Ray Serve
serve run serve_config.yaml

4. Verificar los Servicios

# Verificar Triton Server
curl http://localhost:8000/v2/health/ready

# Verificar Ray Serve
curl http://localhost:8080/health

# Ver información del modelo
curl http://localhost:8080/model-info

# Ver información del preprocesamiento
curl http://localhost:8080/preprocessing-info

📊 Componentes del Pipeline

Preprocesamiento (Ray Serve)

  • Ubicación: Ejecutado localmente en Ray Serve
  • Configuración: preprocessing_info.pkl
  • Componentes:
    • Encoders: LabelEncoders para variables categóricas
    • Scaler: StandardScaler para normalización
    • Feature columns: Lista de 15 características esperadas
    • Categorical columns: Columnas que requieren encoding
  • Funciones:
    • Extracción de características temporales (año, mes, día, día de semana)
    • Cálculo de días hasta envío
    • Codificación de variables categóricas
    • Normalización de features numéricas
    • Manejo de valores faltantes

Modelo profit (ONNX en Triton)

  • Tipo: Modelo ONNX (LightGBM/XGBoost/NN)
  • Input: input - Array de 15 features procesadas (FP32)
  • Output: output - Predicción de profit (FP32)
  • Dimensiones:
    • Input: [batch_size, 15]
    • Output: [batch_size, 1]
  • Optimizado para: CPU con ONNX Runtime

Flujo de Datos

  1. Cliente → Envía datos raw en JSON
  2. Ray Serve → Valida y preprocesa datos
  3. Preprocesamiento → Genera 15 features normalizadas
  4. Triton Server → Ejecuta inferencia con modelo ONNX
  5. Ray Serve → Formatea y retorna respuesta

📡 Endpoints API

Ray Serve expone los siguientes endpoints en el puerto 8080:

Endpoint Método Descripción
/predict POST Predicción de ganancias (principal)
/health GET Verificar estado del servicio y conexión con Triton
/model-info GET Información detallada del modelo y preprocesamiento
/preprocessing-info GET Detalles de encoders, scalers y features
/fake-email GET Generador de emails falsos para pruebas

POST /predict

Endpoint principal para realizar predicciones de ganancias.

URL: http://localhost:8080/predict

Request Body:

{
  "Region": "Sub-Saharan Africa",
  "Country": "South Africa",
  "Item_Type": "Fruits",
  "Sales_Channel": "Online",
  "Order_Priority": "M",
  "Order_Date": "2024-01-15",
  "Ship_Date": "2024-01-20",
  "Units_Sold": 1000,
  "Unit_Price": 9.33,
  "Unit_Cost": 6.92,
  "Total_Revenue": 9330.00,
  "Total_Cost": 6920.00
}

Response (Success):

{
  "status": "success",
  "prediction": {
    "total_profit": 2410.00,
    "currency": "USD",
    "formatted": "$2,410.00"
  },
  "input_summary": {
    "region": "Sub-Saharan Africa",
    "country": "South Africa",
    "item_type": "Fruits",
    "units_sold": 1000,
    "unit_price": 9.33,
    "total_revenue": 9330.00
  },
  "model_info": {
    "name": "profit",
    "version": "1",
    "backend": "ONNX Runtime (Triton)",
    "preprocessing": "Ray Serve (Local)"
  }
}

Response (Error):

{
  "detail": "Error description"
}

GET /health

Verifica el estado del servicio y la conexión con Triton Server.

URL: http://localhost:8080/health

Response (Healthy):

{
  "status": "healthy",
  "triton_server": {
    "is_live": true,
    "model_ready": true,
    "model_name": "profit",
    "model_version": "1"
  },
  "preprocessing": {
    "ready": true,
    "location": "Ray Serve",
    "features_count": 15,
    "categorical_features": 5
  }
}

Response (Unhealthy):

{
  "status": "unhealthy",
  "error": "Error description"
}

Ejemplo con curl:

curl http://localhost:8080/health

GET /model-info

Obtiene información detallada del modelo ONNX en Triton y del preprocesamiento en Ray.

URL: http://localhost:8080/model-info

Response:

{
  "model": {
    "name": "profit",
    "versions": ["1"],
    "platform": "onnxruntime_onnx",
    "inputs": [
      {
        "name": "input",
        "datatype": "FP32",
        "shape": [-1, 15]
      }
    ],
    "outputs": [
      {
        "name": "output",
        "datatype": "FP32",
        "shape": [-1, 1]
      }
    ]
  },
  "preprocessing": {
    "features": ["Region", "Country", "Item_Type", "..."],
    "categorical_columns": ["Region", "Country", "Item_Type", "Sales_Channel", "Order_Priority"],
    "encoders_available": ["Region", "Country", "Item_Type", "Sales_Channel", "Order_Priority"],
    "total_features": 15,
    "scaler_type": "StandardScaler"
  }
}

Ejemplo con curl:

curl http://localhost:8080/model-info

GET /preprocessing-info

Obtiene información muy detallada del preprocesamiento, incluyendo las clases de los encoders.

URL: http://localhost:8080/preprocessing-info

Response:

{
  "feature_columns": [
    "Region", "Country", "Item_Type", "Sales_Channel",
    "Order_Priority", "Units_Sold", "Unit_Price", "Unit_Cost",
    "Total_Revenue", "Total_Cost", "Order_Year", "Order_Month",
    "Order_Day", "Order_DayOfWeek", "Days_to_Ship"
  ],
  "categorical_columns": ["Region", "Country", "Item_Type", "Sales_Channel", "Order_Priority"],
  "numerical_columns": ["Units_Sold", "Unit_Price", "Unit_Cost", "..."],
  "encoders": {
    "Region": {
      "type": "LabelEncoder",
      "classes": ["Asia", "Australia and Oceania", "Central America and the Caribbean", "..."]
    },
    "Country": {
      "type": "LabelEncoder",
      "classes": ["Afghanistan", "Albania", "Algeria", "..."]
    }
  },
  "scaler": {
    "type": "StandardScaler",
    "mean": [2.3, 45.6, 1.2, "..."],
    "scale": [0.8, 12.3, 0.5, "..."]
  }
}

Ejemplo con curl:

curl http://localhost:8080/preprocessing-info

GET /fake-email

Genera un email falso para pruebas.

URL: http://localhost:8080/fake-email

Response:

{
  "email": "john.doe@example.com"
}

Ejemplo con curl:

curl http://localhost:8080/fake-email

🔍 Formato de Datos de Entrada

API de Ray Serve

El endpoint /predict acepta datos en formato JSON con los siguientes campos:

{
    "Region": "Sub-Saharan Africa",
    "Country": "South Africa",
    "Item_Type": "Fruits",           // También acepta "Item Type"
    "Sales_Channel": "Online",       // También acepta "Sales Channel"
    "Order_Priority": "M",           // También acepta "Order Priority"
    "Order_Date": "2024-01-15",
    "Ship_Date": "2024-01-20",
    "Units_Sold": 1000,
    "Unit_Price": 9.33,
    "Unit_Cost": 6.92,
    "Total_Revenue": 9330.00,
    "Total_Cost": 6920.00
}

Nota: El API acepta nombres de campos tanto con guiones bajos (Item_Type) como con espacios (Item Type).

🧪 Testing y Validación

Test del API de Ray Serve

Con curl

# Hacer una predicción
curl -X POST http://localhost:8080/predict \
  -H "Content-Type: application/json" \
  -d '{
    "Region": "Sub-Saharan Africa",
    "Country": "South Africa",
    "Item_Type": "Fruits",
    "Sales_Channel": "Online",
    "Order_Priority": "M",
    "Order_Date": "2024-01-15",
    "Ship_Date": "2024-01-20",
    "Units_Sold": 1000,
    "Unit_Price": 9.33,
    "Unit_Cost": 6.92,
    "Total_Revenue": 9330.00,
    "Total_Cost": 6920.00
  }'

# Health check
curl http://localhost:8080/health

# Información del modelo
curl http://localhost:8080/model-info

Con Python

import requests
import json

# URL del servicio Ray Serve
url = "http://localhost:8080/predict"

# Datos de ejemplo
data = {
    "Region": "Sub-Saharan Africa",
    "Country": "South Africa",
    "Item_Type": "Fruits",
    "Sales_Channel": "Online",
    "Order_Priority": "M",
    "Order_Date": "2024-01-15",
    "Ship_Date": "2024-01-20",
    "Units_Sold": 1000,
    "Unit_Price": 9.33,
    "Unit_Cost": 6.92,
    "Total_Revenue": 9330.00,
    "Total_Cost": 6920.00
}

# Hacer predicción
response = requests.post(url, json=data)
result = response.json()

print(json.dumps(result, indent=2))

Respuesta Esperada

{
  "status": "success",
  "prediction": {
    "total_profit": 2410.00,
    "currency": "USD",
    "formatted": "$2,410.00"
  },
  "input_summary": {
    "region": "Sub-Saharan Africa",
    "country": "South Africa",
    "item_type": "Fruits",
    "units_sold": 1000,
    "unit_price": 9.33,
    "total_revenue": 9330.00
  },
  "model_info": {
    "name": "profit",
    "version": "1",
    "backend": "ONNX Runtime (Triton)",
    "preprocessing": "Ray Serve (Local)"
  }
}

Test Directo con Triton (Avanzado)

Si necesitas probar directamente el servidor Triton sin pasar por Ray Serve (requiere features preprocesadas):

import numpy as np
import tritonclient.http as httpclient

# Cliente de Triton
client = httpclient.InferenceServerClient("localhost:8000")

# NOTA: Este test requiere features ya preprocesadas (15 valores)
# En producción, el preprocesamiento se hace en Ray Serve
features_procesadas = np.array([[
    0.5, -0.3, 0.8, 1.2, -0.5,  # Primeras 5 features (ejemplo)
    0.1, 0.9, -0.2, 0.4, 0.7,    # Siguientes 5 features
    2024, 1, 15, 1, 5            # Últimas 5 features (temporales)
]], dtype=np.float32)

# Crear input
inputs = [httpclient.InferInput("input", [1, 15], "FP32")]
inputs[0].set_data_from_numpy(features_procesadas)

# Inferencia
outputs = [httpclient.InferRequestedOutput("output")]
results = client.infer("profit", inputs=inputs, outputs=outputs)
prediction = results.as_numpy("output")
print(f"Profit predicho: ${prediction[0][0]:.2f}")

# IMPORTANTE: Para obtener features preprocesadas correctamente,
# usa el endpoint /preprocessing-info de Ray Serve para ver
# los encoders y scalers aplicados

☸️ Despliegue en Kubernetes

1. Aplicar Manifiestos

# Desplegar Triton Server
kubectl apply -f triton-deployment-docker.yaml

# Verificar pods
kubectl get pods -l app=triton-inference-server

# Ver logs
kubectl logs -f deployment/triton-inference-server

2. Port Forward para Pruebas

kubectl port-forward service/triton-inference-server 8000:8000 8001:8001

🔧 Personalización

Modificar el Preprocesamiento

El preprocesamiento ahora se ejecuta en Ray Serve. Para modificarlo:

  1. Actualizar preprocessing_info.pkl:

    import pickle
    
    # Cargar y modificar
    with open('preprocessing_info.pkl', 'rb') as f:
        info = pickle.load(f)
    
    # info debe contener:
    # - 'encoders': dict con LabelEncoders
    # - 'scaler': StandardScaler o similar
    # - 'feature_columns': lista de 15 columnas
    # - 'categorical_columns': columnas categóricas
    
    # Guardar cambios
    with open('preprocessing_info.pkl', 'wb') as f:
        pickle.dump(info, f)
  2. Modificar lógica en serve.py:

    • Método preprocess_input() para cambiar transformaciones
    • Método load_preprocessing_info() para configuración inicial

Actualizar el Modelo ONNX

  1. Entrena tu nuevo modelo
  2. Exporta a ONNX (debe aceptar 15 features)
  3. Reemplaza model_repository/serving/1/model.onnx
  4. Actualiza preprocessing_info.pkl con nuevos encoders/scalers

Configurar Batching

Edita model_repository/serving/config.pbtxt:

dynamic_batching {
  preferred_batch_size: [ 1, 4, 8, 16, 32 ]
  max_queue_delay_microseconds: 100
}

🐛 Troubleshooting

Error: "preprocessing_info.pkl not found"

  • Verifica que el archivo existe en la raíz del proyecto: preprocessing_info.pkl
  • Asegúrate de que contiene las siguientes claves:
    • encoders: diccionario con LabelEncoders
    • scaler: objeto StandardScaler
    • feature_columns: lista de 15 columnas
    • categorical_columns: lista de columnas categóricas

Error: "Model not ready"

  • Revisa logs de Triton: docker logs <container_id>
  • Verifica que el modelo profit está cargado:
    curl http://localhost:8000/v2/models/profit

Error: "Shape mismatch"

  • El modelo ONNX espera exactamente 15 features
  • Verifica el preprocesamiento con el endpoint:
    curl http://localhost:8080/preprocessing-info
  • Inspecciona el modelo ONNX:
    import onnx
    model = onnx.load("model_repository/serving/1/model.onnx")
    print(model.graph.input[0])  # Debe mostrar shape [-1, 15]

Error: "Unknown categorical value"

  • Ray Serve maneja valores desconocidos usando el valor por defecto (0)
  • Para agregar nuevas categorías, actualiza preprocessing_info.pkl

📈 Monitoreo y Métricas

Triton expone métricas en el puerto 8002:

# Ver métricas
curl http://localhost:8002/metrics

# Métricas importantes:
# - nv_inference_request_success
# - nv_inference_request_failure
# - nv_inference_count
# - nv_inference_exec_count
# - nv_inference_queue_duration_us

📚 Recursos Adicionales

📝 Notas Importantes

  • Preprocesamiento en Ray: Toda la lógica de preprocesamiento se ejecuta en Ray Serve, no en Triton
  • Archivo pickle: El archivo preprocessing_info.pkl es crítico - contiene encoders y scalers entrenados
  • 15 Features: El modelo ONNX espera exactamente 15 features preprocesadas y normalizadas
  • Valores faltantes: Se manejan automáticamente con valores por defecto
  • Categorías desconocidas: Se asigna valor 0 (más común) cuando encuentra categorías no vistas durante entrenamiento
  • Pipeline stateless: Cada request es independiente
  • Optimización: Para producción con alto volumen, considera:
    • GPU para el modelo ONNX en Triton
    • Múltiples réplicas de Ray Serve
    • Cache de preprocesamiento para datos frecuentes