> For the complete documentation index, see [llms.txt](https://docs.clore.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.clore.ai/guides/guides_v2-es/mlops-y-despliegue/triton-inference-server.md).

# Triton Inference Server

**Servidor de inferencia NVIDIA Triton** es una plataforma de inferencia de código abierto y de nivel de producción que admite prácticamente todos los principales frameworks de ML. Diseñado para servir con alto rendimiento y baja latencia, Triton maneja PyTorch, TensorFlow, ONNX, TensorRT, OpenVINO y más, todo desde un único proceso de servidor. Despliégalo en la nube de GPU de Clore.ai para una infraestructura de inferencia escalable y rentable.

***

## ¿Qué es Triton Inference Server?

Triton es la respuesta de NVIDIA al desafío de servir modelos de ML a escala:

* **Multiframework:** PyTorch, TensorFlow, TensorRT, ONNX, OpenVINO, backends personalizados en Python
* **Ejecución concurrente:** Varios modelos, varias instancias por GPU
* **Batching dinámico:** Agrupa automáticamente las solicitudes para mayor rendimiento
* **gRPC + HTTP:** Protocolos estándar de la industria listos para usar
* **Métricas:** Endpoint de métricas compatible con Prometheus
* **Repositorio de modelos:** Gestión de modelos basada en el sistema de archivos

**Puertos utilizados:**

| Puerto | Protocolo | Propósito              |
| ------ | --------- | ---------------------- |
| 8000   | HTTP      | API REST de inferencia |
| 8001   | gRPC      | API gRPC de inferencia |
| 8002   | HTTP      | Métricas de Prometheus |

***

## Requisitos previos

| Requisito      | Mínimo                        | Recomendado     |
| -------------- | ----------------------------- | --------------- |
| VRAM de GPU    | 8 GB                          | 16–24 GB        |
| GPU            | Cualquier NVIDIA con CUDA 11+ | RTX 4090 / A100 |
| RAM            | 16 GB                         | 32 GB           |
| Almacenamiento | 20 GB                         | 50 GB           |

{% hint style="info" %}
Triton también admite inferencia solo en CPU para cargas de trabajo que no usan CUDA. Usa la `solo-CPU` variante de la imagen de Docker para ahorrar costos en trabajos por lotes que no requieren GPU.
{% endhint %}

***

## Paso 1 — Alquila una GPU en Clore.ai

1. Inicia sesión en [clore.ai](https://clore.ai).
2. Haz clic en **Marketplace** y filtra por VRAM ≥ 16 GB.
3. Selecciona un servidor y haz clic en **Configurar**.
4. Establece la imagen de Docker: **`nvcr.io/nvidia/tritonserver:24.01-py3`**
   * (Reemplaza `24.01` por la versión más reciente — consulta [catálogo NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver))
5. Establece los puertos abiertos: `22` (SSH), `8000` (HTTP), `8001` (gRPC), `8002` (métricas).
6. Haz clic en **Alquilar**.

{% hint style="warning" %}
Las imágenes Docker de Triton son grandes (\~15–20 GB). Permite 3–5 minutos para la descarga inicial en el primer inicio. Los inicios posteriores son rápidos.
{% endhint %}

***

## Paso 2 — Dockerfile personalizado (con SSH)

La imagen oficial de Triton no incluye un servidor SSH. Usa este Dockerfile:

```dockerfile
FROM nvcr.io/nvidia/tritonserver:24.01-py3

RUN apt-get update && apt-get install -y \\
    openssh-server \\
    wget curl \\
    && rm -rf /var/lib/apt/lists/*

# Configurar SSH
RUN mkdir /var/run/sshd && \\
    echo 'root:clore123' | chpasswd && \\
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Instalar la biblioteca cliente de Python
RUN pip install tritonclient[all] numpy Pillow

RUN mkdir -p /models

EXPOSE 22 8000 8001 8002

CMD service ssh start && \\
    tritonserver \\
        --model-repository=/models \\
        --log-verbose=0 \\
        --http-port=8000 \\
        --grpc-port=8001 \\
        --metrics-port=8002
```

***

## Paso 3 — Comprende el repositorio de modelos

Triton carga los modelos desde un **repositorio de modelos** — un directorio con una estructura específica:

```
/models/
├── model_name_1/
│   ├── config.pbtxt          # Configuración del modelo
│   ├── 1/                    # Versión 1
│   │   └── model.pt          # Archivo del modelo
│   └── 2/                    # Versión 2 (opcional)
│       └── model.pt
├── model_name_2/
│   ├── config.pbtxt
│   └── 1/
│       └── model.onnx
```

Cada modelo necesita:

1. Un directorio con el nombre del modelo
2. Un `config.pbtxt` archivo de configuración
3. Al menos un subdirectorio de versión (por ejemplo, `1/`) con el archivo del modelo

***

## Paso 4 — Despliega un modelo PyTorch

### Exportar el modelo a TorchScript

```python
import torch
import torchvision

# Carga un ResNet50 preentrenado
model = torchvision.models.resnet50(pretrained=True)
model.eval()

# Exportar a TorchScript
example_input = torch.randn(1, 3, 224, 224)
traced_model = torch.jit.trace(model, example_input)

# Guardar
traced_model.save("/tmp/resnet50.pt")
print("Modelo exportado correctamente")
```

### Configurar el repositorio de modelos

```bash
# Conéctate por SSH a tu instancia de Clore.ai
ssh root@<clore-host> -p <port>

# Crear la estructura de directorios
mkdir -p /models/resnet50/1

# Subir el modelo
# (desde tu máquina local)
scp -P <port> /tmp/resnet50.pt root@<clore-host>:/models/resnet50/1/model.pt
```

### Crear config.pbtxt

```bash
cat > /models/resnet50/config.pbtxt << 'EOF'
name: "resnet50"
platform: "pytorch_libtorch"
max_batch_size: 32

input [
  {
    name: "input__0"
    data_type: TYPE_FP32
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "output__0"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

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

instance_group [
  {
    count: 2
    kind: KIND_GPU
    gpus: [0]
  }
]
EOF
```

***

## Paso 5 — Despliega un modelo ONNX

### Exportar a ONNX

```python
import torch
import torchvision
import torch.onnx

model = torchvision.models.resnet50(pretrained=True)
model.eval()

dummy_input = torch.randn(1, 3, 224, 224)

torch.onnx.export(
    model,
    dummy_input,
    "/tmp/resnet50.onnx",
    opset_version=13,
    input_names=["images"],
    output_names=["logits"],
    dynamic_axes={
        "images": {0: "batch_size"},
        "logits": {0: "batch_size"}
    }
)
```

### Configuración de ONNX

```bash
mkdir -p /models/resnet50_onnx/1
scp -P <port> /tmp/resnet50.onnx root@<clore-host>:/models/resnet50_onnx/1/model.onnx

cat > /models/resnet50_onnx/config.pbtxt << 'EOF'
name: "resnet50_onnx"
platform: "onnxruntime_onnx"
max_batch_size: 32

input [
  {
    name: "images"
    data_type: TYPE_FP32
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "logits"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

dynamic_batching {
  preferred_batch_size: [8, 16, 32]
  max_queue_delay_microseconds: 100
}
EOF
```

***

## Paso 6 — Despliega un backend personalizado en Python

Para modelos que no encajan en los backends estándar (preprocesamiento personalizado, lógica de ensemble):

```bash
mkdir -p /models/custom_model/1

cat > /models/custom_model/1/model.py << 'EOF'
import triton_python_backend_utils as pb_utils
import numpy as np
import torch

class TritonPythonModel:
    def initialize(self, args):
        self.model = torch.nn.Linear(10, 5).cuda()
        self.model.eval()
    
    def execute(self, requests):
        responses = []
        for request in requests:
            input_tensor = pb_utils.get_input_tensor_by_name(request, "INPUT")
            input_np = input_tensor.as_numpy()
            
            with torch.no_grad():
                inp = torch.from_numpy(input_np).float().cuda()
                out = self.model(inp).cpu().numpy()
            
            output_tensor = pb_utils.Tensor("OUTPUT", out.astype(np.float32))
            responses.append(pb_utils.InferenceResponse(output_tensors=[output_tensor]))
        
        return responses
    
    def finalize(self):
        pass
EOF

cat > /models/custom_model/config.pbtxt << 'EOF'
name: "custom_model"
backend: "python"
max_batch_size: 64

input [
  {
    name: "INPUT"
    data_type: TYPE_FP32
    dims: [10]
  }
]

output [
  {
    name: "OUTPUT"
    data_type: TYPE_FP32
    dims: [5]
  }
]
EOF
```

***

## Paso 7 — Inicia Triton y prueba

### Iniciar el servidor Triton

```bash
# Iniciar (si usas el CMD del Dockerfile, se inicia automáticamente)
tritonserver \\
    --model-repository=/models \\
    --http-port=8000 \\
    --grpc-port=8001 \\
    --metrics-port=8002 \\
    --log-verbose=0 &

# Esperar a que el servidor esté listo
sleep 5
curl -s http://localhost:8000/v2/health/ready
# Esperado: {"live": true}
```

### Comprobar los modelos disponibles

```bash
curl http://<clore-host>:<public-8000>/v2/models
```

### Ejecutar inferencia vía HTTP

```python
import tritonclient.http as httpclient
import numpy as np

client = httpclient.InferenceServerClient(
    url="<clore-host>:<public-port-8000>",
    ssl=False
)

# Comprobar el estado del servidor
print("Servidor listo:", client.is_server_ready())
print("Modelo listo:", client.is_model_ready("resnet50_onnx"))

# Crear entrada
image = np.random.rand(1, 3, 224, 224).astype(np.float32)
input_tensor = httpclient.InferInput("images", image.shape, "FP32")
input_tensor.set_data_from_numpy(image)

# Ejecuta la inferencia
outputs = [httpclient.InferRequestedOutput("logits")]
response = client.infer("resnet50_onnx", [input_tensor], outputs=outputs)

logits = response.as_numpy("logits")
predicted_class = np.argmax(logits[0])
print(f"Clase predicha: {predicted_class}")
```

### Ejecutar inferencia vía gRPC

```python
import tritonclient.grpc as grpcclient
import numpy as np

client = grpcclient.InferenceServerClient(
    url="<clore-host>:<public-port-8001>"
)

image = np.random.rand(1, 3, 224, 224).astype(np.float32)
input_tensor = grpcclient.InferInput("images", image.shape, "FP32")
input_tensor.set_data_from_numpy(image)

outputs = [grpcclient.InferRequestedOutput("logits")]
response = client.infer("resnet50_onnx", [input_tensor], outputs=outputs)

logits = response.as_numpy("logits")
print(f"Forma de salida: {logits.shape}")
```

***

## Monitorización con Prometheus

Triton expone métricas en el puerto 8002:

```bash
curl http://<clore-host>:<public-port-8002>/metrics
```

Métricas clave:

```
# Rendimiento de inferencia
nv_inference_request_success{model="resnet50_onnx", version="1"}
# Tiempo medio de inferencia
nv_inference_compute_infer_duration_us{model="resnet50_onnx", version="1"}
# Utilización de la GPU
nv_gpu_utilization{gpu_uuid="..."}
# Memoria de la GPU
nv_gpu_memory_used_bytes{gpu_uuid="..."}
```

***

## Configuración de batching dinámico

```protobuf
dynamic_batching {
  preferred_batch_size: [4, 8, 16, 32]
  max_queue_delay_microseconds: 5000
  preserve_ordering: true
  
  priority_levels: 3
  default_priority_level: 2
  default_queue_policy {
    timeout_action: REJECT
    default_timeout_microseconds: 10000
    allow_timeout_override: true
    max_queue_size: 100
  }
}
```

***

## Solución de problemas

### Fallo al cargar el modelo

```
Error al cargar el modelo: no se pudo encontrar el archivo del modelo
```

**Solución:** Comprueba la estructura de directorios y los permisos:

```bash
ls -la /models/resnet50/1/
# Debe contener model.pt (PyTorch) o model.onnx (ONNX)
chmod -R 755 /models/
```

### Incompatibilidad con CUDA

**Solución:** Haz coincidir la versión de la imagen Triton con tu controlador CUDA:

```bash
nvidia-smi  # Nota la versión de CUDA
# Usa una etiqueta de tritonserver que coincida, por ejemplo, 23.10 para CUDA 12.2
```

### Puerto no accesible

**Solución:** Verifica que los tres puertos (8000, 8001, 8002) estén redirigidos en Clore.ai. Prueba cada uno:

```bash
curl http://<host>:<port>/v2/health/live
```

### OOM durante la carga del modelo

**Solución:** Reduce el número de instancias o usa instancias de CPU para algunos modelos:

```protobuf
instance_group [
  {
    count: 1       # Reducir desde el valor predeterminado
    kind: KIND_GPU
  }
]
```

***

## Estimación de costos

| GPU       | VRAM  | Precio estimado                           | Rendimiento (ResNet50) |
| --------- | ----- | ----------------------------------------- | ---------------------- |
| RTX 3080  | 10 GB | $0,05–0,19/h                              | \~500 solicitudes/seg  |
| RTX 4090  | 24 GB | $0.14–0.42/h                              | \~1500 solicitudes/seg |
| A100 40GB | 40 GB | [bare metal](https://clore.ai/bare-metal) | \~3000 solicitudes/seg |
| H100      | 80 GB | \~$1.04/h                                 | \~8000 solicitudes/seg |

***

## Recursos útiles

* [GitHub de Triton](https://github.com/triton-inference-server/server)
* [Registro de contenedores NGC](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver)
* [Bibliotecas cliente de Triton](https://github.com/triton-inference-server/client)
* [Referencia de configuración de modelos de Triton](https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/model_configuration.html)
* [Backend de Python de Triton](https://github.com/triton-inference-server/python_backend)
* [Analizador de rendimiento de Triton](https://github.com/triton-inference-server/client/blob/main/src/c%2B%2B/perf_analyzer/README.md)

***

## Recomendaciones de GPU para Clore.ai

| Caso de uso              | GPU recomendada | Costo estimado en Clore.ai                |
| ------------------------ | --------------- | ----------------------------------------- |
| Desarrollo/Pruebas       | RTX 3090 (24GB) | $0.07–0.21/gpu/hr                         |
| Inferencia en producción | RTX 4090 (24GB) | $0.14–0.42/gpu/hr                         |
| Modelos grandes (70B+)   | A100 80GB       | [bare metal](https://clore.ai/bare-metal) |

> 💡 Todos los ejemplos de esta guía pueden desplegarse en [Clore.ai](https://clore.ai/marketplace) servidores GPU. Explora las GPUs disponibles y alquila por hora: sin compromisos, acceso root completo.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-es/mlops-y-despliegue/triton-inference-server.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
