Servicio completo de predicción de ganancias usando Ray Serve con preprocesamiento local, validación de esquemas y despliegue en Kubernetes.
Esta arquitectura implementa un servicio de ML usando únicamente Ray Serve, donde todo el procesamiento (preprocesamiento e inferencia) se ejecuta dentro del mismo deployment de Ray.
Cliente → Ray Serve (FastAPI) → Preprocesamiento → ONNX Runtime → Respuesta
Componentes:
- Ray Serve: Framework de serving y orquestación
- FastAPI: API REST con validación automática
- Pydantic: Validación de esquemas de entrada
- Scikit-learn: Preprocesamiento (encoders, scalers)
- ONNX Runtime: Motor de inferencia del modelo
- Flexibilidad máxima: Fácil modificar el preprocesamiento en Python
- Desarrollo rápido: Ciclo de desarrollo ágil, solo requiere Python
- Validación robusta: Pydantic valida automáticamente las entradas
- Despliegue simple: Un solo contenedor, menos complejidad operacional
- Debugging fácil: Código Python estándar, fácil de depurar
- Bajo costo de recursos: No requiere GPU ni servidores adicionales
- Performance limitada: Menor throughput que Triton (~100 req/s vs ~1000 req/s)
- Sin aceleración GPU: ONNX Runtime en CPU, no optimizado para GPU
- Escalado simple: Escalado horizontal básico, sin batching dinámico sofisticado
- ✅ Equipos que trabajan principalmente con Python
- ✅ Proyectos donde el preprocesamiento cambia frecuentemente
- ✅ Fase de desarrollo y experimentación
- ✅ Casos donde la flexibilidad es más importante que el máximo rendimiento
- ✅ Infraestructura con recursos limitados
Los modelos no están incluidos en este repositorio. Debes obtenerlos de una de estas formas:
# Crear directorio de modelos
mkdir -p models
# Descargar desde tu almacenamiento (ejemplo con gsutil de GCP)
gsutil cp gs://tu-bucket/models/serving/model.onnx models/
gsutil cp gs://tu-bucket/models/serving/model.onnx.data models/
gsutil cp gs://tu-bucket/models/serving/preprocessing_info.pkl models/
# O desde S3 (AWS)
aws s3 cp s3://tu-bucket/models/serving/model.onnx models/
aws s3 cp s3://tu-bucket/models/serving/model.onnx.data models/
aws s3 cp s3://tu-bucket/models/serving/preprocessing_info.pkl models/# Ejemplo: Entrenar y exportar modelo a ONNX
import pickle
import torch
import onnx
from sklearn.preprocessing import LabelEncoder, StandardScaler
# 1. Entrenar tu modelo (ejemplo con PyTorch/Scikit-learn)
# ... código de entrenamiento ...
# 2. Exportar a ONNX
torch.onnx.export(
model,
dummy_input,
"models/model.onnx",
export_params=True,
opset_version=11,
input_names=['input'],
output_names=['output']
)
# 3. Guardar preprocessing_info.pkl
preprocessing_info = {
'encoders': {
'Region': region_encoder,
'Country': country_encoder,
'Item_Type': item_type_encoder,
'Sales_Channel': sales_channel_encoder,
'Order_Priority': order_priority_encoder
},
'scaler': scaler,
'feature_columns': feature_columns_list,
'categorical_columns': categorical_columns_list
}
with open('models/preprocessing_info.pkl', 'wb') as f:
pickle.dump(preprocessing_info, f)El modelo ONNX debe:
- Input: Tensor de forma
[-1, 15](15 features preprocesadas) - Output: Tensor de forma
[-1, 1](predicción de profit) - Tipo de datos: FP32 (float32)
El archivo preprocessing_info.pkl debe contener:
encoders: Dict con LabelEncoders para columnas categóricasscaler: StandardScaler para normalizaciónfeature_columns: Lista con nombres de las 15 featurescategorical_columns: Lista con nombres de columnas categóricas
Este servicio implementa un deployment avanzado de Ray Serve que:
- Carga un modelo ONNX entrenado para predecir ganancias de ventas
- Realiza preprocesamiento completo de datos (codificación categórica, normalización, feature engineering)
- Valida las entradas usando FastAPI y Pydantic
- Expone endpoints REST para predicciones y utilidades
- Soporta despliegue tanto con Docker como con volúmenes de Kubernetes
- Framework: Ray Serve 2.46.0
- API: FastAPI con validación Pydantic
- Modelo: ONNX con información de preprocesamiento
- Preprocesamiento: Scikit-learn encoders y scalers
- Contenedorización: Docker opcional
- Orquestación: Kubernetes (usando RayService CRD)
test-serve/
├── Dockerfile # Imagen Docker con modelo y código
├── serve.py # Deployment principal con preprocesamiento
├── schema.py # Esquemas Pydantic para validación
├── requirements.txt # Dependencias Python
├── config-serve-docker.yaml # Configuración K8s con imagen Docker
├── config-serve-volumen.yaml # Configuración K8s con volúmenes/ConfigMap
└── models/
├── model.onnx # Modelo ONNX entrenado
├── model.onnx.data # Datos adicionales del modelo ONNX
└── preprocessing_info.pkl # Encoders, scalers y metadata de preprocesamiento
Contiene el deployment ProfitPredictionService que:
- Carga el modelo: ONNX desde
/app/model.onnx - Carga preprocesamiento: Encoders, scalers y metadata desde
preprocessing_info.pkl - Preprocesa datos:
- Convierte fechas a características temporales (año, mes, día, día de semana)
- Calcula días hasta envío
- Codifica variables categóricas (Region, Country, Item_Type, etc.)
- Normaliza características numéricas
- Realiza predicciones: Usa
onnxruntimepara inferencia - Valida entradas: Usa Pydantic para validar el esquema de entrada
- Expone endpoints:
POST /predict: Predicción principal de gananciasGET /fake-email: Utilidad para generar emails falsos (usando Faker)
Define el esquema SalesPredictionRequest con Pydantic:
- Campos requeridos para predicción de ventas
- Validación automática de tipos
- Documentación integrada
Construye una imagen Docker que:
- Usa
rayproject/ray:2.46.0-py311como base - Instala todas las dependencias necesarias
- Copia modelo, código y archivos de preprocesamiento
- Configura el entorno de trabajo
Configuración de Kubernetes que:
- Usa imagen Docker personalizada
ray-serving-model:latest - Define recursos y escalado del cluster
- Configura 2 réplicas del servicio
Configuración alternativa que:
- Usa imagen base de Ray (sin modelo empaquetado)
- Monta código y modelo desde ConfigMap
- Permite actualizar modelo sin reconstruir imagen
- Instala dependencias en runtime usando
runtime_env
docker build -t ray-serving-model:latest .kind load docker-image ray-serving-model:latest --name ray-cluster-
Configurar autenticación con GCP:
# Instalar Google Cloud SDK si no lo tienes # https://cloud.google.com/sdk/docs/install # Autenticarse con tu cuenta de Google gcloud auth login # Configurar proyecto (reemplaza {PROJECT_ID} con tu proyecto) gcloud config set project {PROJECT_ID} # Configurar Docker para usar credenciales de gcloud # Para Artifact Registry gcloud auth configure-docker us-central1-docker.pkg.dev # O si usas otra región, reemplaza us-central1 # gcloud auth configure-docker REGION-docker.pkg.dev # Verificar autenticación gcloud auth list gcloud config get-value project
-
Crear un Artifact Registry (si no existe):
# Crear repositorio en Artifact Registry gcloud artifacts repositories create ray-serving-repo \ --repository-format=docker \ --location=us-central1 \ --description="Repositorio para imágenes de Ray Serve"
-
Taggear la imagen con la URL de GCP:
# Formato: REGION-docker.pkg.dev/PROYECTO-ID/REPOSITORIO/IMAGEN:TAG docker tag ray-serving-model:latest \ us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest # O con versión específica docker tag ray-serving-model:latest \ us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:v1.0.0
-
Hacer push de la imagen:
docker push us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
-
Configurar autenticación:
gcloud auth login gcloud auth configure-docker gcloud config set project {PROJECT_ID} -
Taggear la imagen:
# Formato: gcr.io/PROYECTO-ID/IMAGEN:TAG docker tag ray-serving-model:latest \ gcr.io/{PROJECT_ID}/ray-serving-model:latest -
Hacer push:
docker push gcr.io/{PROJECT_ID}/ray-serving-model:latest -
Actualizar
config-serve-docker.yaml:Cambia la línea de la imagen en el archivo YAML:
# Para Artifact Registry image: us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest imagePullPolicy: Always # Cambiar de Never a Always # Para Container Registry image: gcr.io/{PROJECT_ID}/ray-serving-model:latest imagePullPolicy: Always
-
Configurar acceso desde Kubernetes:
Si tu cluster de Kubernetes está en GCP (GKE), asegúrate de que tenga permisos para acceder al registry:
# Para GKE, el acceso suele estar configurado automáticamente # Si usas un cluster externo, necesitarás crear un Secret: kubectl create secret docker-registry gcp-registry-secret \ --docker-server=us-central1-docker.pkg.dev \ --docker-username=oauth2accesstoken \ --docker-password=$(gcloud auth print-access-token) \ --docker-email=TU-EMAIL
kubectl apply -f config-serve-docker.yaml# Crear ConfigMap con código y modelo
kubectl create configmap model-code \
--from-file=serve.py \
--from-file=schema.py \
--from-file=models/
# Desplegar servicio
kubectl apply -f config-serve-volumen.yaml# Ver pods
kubectl get pods
# Ver servicios
kubectl get services
# Ver logs
kubectl logs -f <pod-name># Port-forward al servicio
kubectl port-forward service/rayservice-volumen-serve-svc 8000:8000
# Probar predicción
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{
"Region": "Sub-Saharan Africa",
"Country": "Mozambique",
"Item_Type": "Office Supplies",
"Sales_Channel": "Online",
"Order_Priority": "M",
"Order_Date": "2013-01-01",
"Ship_Date": "2013-01-05",
"Units_Sold": 1000.0,
"Unit_Price": 9.33,
"Unit_Cost": 6.92,
"Total_Revenue": 9330.0,
"Total_Cost": 6920.0
}'| Endpoint | Método | Descripción |
|---|---|---|
/predict |
POST | Predicción de ganancias (principal) |
/fake-email |
GET | Generador de emails falsos para pruebas |
Realiza una predicción de ganancias basada en datos de venta.
URL: http://localhost:8000/predict
Request Body:
{
"Region": "Sub-Saharan Africa",
"Country": "Mozambique",
"Item_Type": "Office Supplies",
"Sales_Channel": "Online",
"Order_Priority": "M",
"Order_Date": "2013-01-01",
"Ship_Date": "2013-01-05",
"Units_Sold": 1000.0,
"Unit_Price": 9.33,
"Unit_Cost": 6.92,
"Total_Revenue": 9330.0,
"Total_Cost": 6920.0
}Response (Success):
{
"status": "success",
"prediction": {
"total_profit": 2410.0,
"currency": "USD"
},
"input_summary": {
"region": "Sub-Saharan Africa",
"country": "Mozambique",
"item_type": "Office Supplies",
"units_sold": 1000.0,
"total_revenue": 9330.0,
"total_cost": 6920.0,
"actual_profit": 2410.0
},
"model_info": {
"type": "ONNX",
"features_used": 15
}
}Response (Error):
{
"status": "error",
"message": "Error description",
"type": "ExceptionType"
}Ejemplo con curl:
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{
"Region": "Sub-Saharan Africa",
"Country": "Mozambique",
"Item_Type": "Office Supplies",
"Sales_Channel": "Online",
"Order_Priority": "M",
"Order_Date": "2013-01-01",
"Ship_Date": "2013-01-05",
"Units_Sold": 1000,
"Unit_Price": 9.33,
"Unit_Cost": 6.92,
"Total_Revenue": 9330.0,
"Total_Cost": 6920.0
}'Genera un email falso usando la librería Faker (útil para testing).
URL: http://localhost:8000/fake-email
Response:
"john.doe@example.com"Ejemplo con curl:
curl http://localhost:8000/fake-email- Validación de entrada: Pydantic valida el esquema
- Conversión a DataFrame: Los datos se convierten a pandas DataFrame
- Feature engineering:
- Extracción de características temporales de fechas
- Cálculo de días hasta envío
- Codificación categórica: Variables categóricas se codifican usando LabelEncoders
- Normalización: Características numéricas se escalan usando StandardScaler
- Predicción: El modelo ONNX realiza la inferencia
- Respuesta: Se estructura y retorna la respuesta con metadatos
- Head Node: 2 CPU, 2Gi memoria
- Worker Nodes: 1 CPU, 2Gi memoria (escalable de 1 a 5 réplicas)
- Réplicas: 2 réplicas del servicio
- CPU por réplica: 0.1 CPU (configurable)
- Máximo por nodo: 1 réplica por nodo
Cuando se usa config-serve-volumen.yaml, las dependencias se instalan en runtime:
- faker
- torch
- pandas
- numpy
- scikit-learn
- onnx
- onnxruntime
- onnxscript
- pydantic
Este servicio es más completo que onnx-base-serve:
- ✅ Incluye preprocesamiento completo de datos
- ✅ Usa FastAPI para validación de esquemas
- ✅ Tiene información de preprocesamiento persistida
- ✅ Feature engineering avanzado (fechas, cálculos derivados)
- ✅ Manejo robusto de errores y validaciones
- ✅ Endpoints adicionales para utilidades
- El modelo debe estar entrenado y exportado a ONNX
- El archivo
preprocessing_info.pkldebe contener los mismos encoders y scalers usados durante el entrenamiento - Las fechas deben estar en formato ISO (YYYY-MM-DD)
- El servicio maneja automáticamente valores categóricos desconocidos usando valores por defecto
Si recibes el error:
denied: Unauthenticated request. Unauthenticated requests do not have permission
"artifactregistry.repositories.uploadArtifacts"
Sigue estos pasos para resolverlo:
-
Verificar autenticación actual:
gcloud auth list gcloud config get-value project
-
Re-autenticarse si es necesario:
# Cerrar sesión y volver a autenticarse gcloud auth logout gcloud auth login # O usar una cuenta específica gcloud auth login TU-EMAIL@ejemplo.com
-
Configurar Docker con la región correcta:
# IMPORTANTE: Especificar la región exacta del repositorio gcloud auth configure-docker us-central1-docker.pkg.dev # Si tu repositorio está en otra región, usa esa región: # gcloud auth configure-docker europe-west1-docker.pkg.dev # gcloud auth configure-docker asia-east1-docker.pkg.dev
-
Obtener token de acceso y configurar manualmente:
# Obtener token de acceso gcloud auth print-access-token # Configurar Docker manualmente (si el método anterior no funciona) echo $(gcloud auth print-access-token) | docker login -u oauth2accesstoken --password-stdin us-central1-docker.pkg.dev
-
Verificar permisos en el proyecto:
# Verificar que tienes los permisos necesarios gcloud projects get-iam-policy {PROJECT_ID} --flatten="bindings[].members" --filter="bindings.members:user:TU-EMAIL@ejemplo.com" # Si no tienes permisos, necesitarás que un administrador te otorgue: # - Artifact Registry Writer # - O Storage Admin (para Container Registry)
-
Usar Application Default Credentials (alternativa):
# Configurar Application Default Credentials gcloud auth application-default login # Luego intentar el push nuevamente docker push us-central1-docker.pkg.dev/{PROJECT_ID}/ray-serving-repo/ray-serving-model:latest
-
Verificar que el repositorio existe y tienes acceso:
# Listar repositorios gcloud artifacts repositories list --location=us-central1 # Ver detalles del repositorio gcloud artifacts repositories describe ray-serving-repo --location=us-central1
Si aún tienes problemas después de autenticarte:
-
Verificar que el proyecto es correcto:
gcloud config set project {PROJECT_ID} gcloud config get-value project -
Verificar que el repositorio existe en la región correcta:
gcloud artifacts repositories list --location=us-central1 --format="table(name,format,location)" -
Contactar al administrador del proyecto para verificar que tienes los roles necesarios:
roles/artifactregistry.writer(para Artifact Registry)roles/storage.admin(para Container Registry)
- El valor categórico no fue visto durante el entrenamiento
- El servicio usa un valor por defecto, pero puede afectar la precisión
- Verifica que el modelo esté en
/app/model.onnxo./models/model.onnx - Si usas ConfigMap, verifica que esté montado correctamente
- Verifica que
preprocessing_info.pklesté presente - Debe contener:
encoders,scaler,feature_columns,categorical_columns