NVIDIA Triton Inference Server: продакшн инференс
Запустить модель машинного обучения через Flask — это быстро и просто. Но когда нагрузка растёт, а задержки становятся критичны, Flask начинает задыхаться. NVIDIA Triton Inference Server создан именно для production: он умеет то, что Flask никогда не умел из коробки.
Зачем Triton, а не просто Flask
Простой Flask-сервер с моделью имеет принципиальные ограничения:
- Обрабатывает один запрос за раз (или требует ручной реализации батчинга)
- Нет встроенной поддержки GPU-параллелизма
- Нет стандартизированных метрик
- Нет управления версиями моделей
Triton решает всё это из коробки: dynamic batching, concurrent model execution, поддержка PyTorch, TensorFlow, ONNX, TensorRT, встроенные метрики Prometheus.
Model Repository: структура хранения моделей
Triton использует стандартизированную структуру директорий:
model_repository/
├── text_classifier/
│ ├── config.pbtxt
│ └── 1/
│ └── model.onnx
├── embedding_model/
│ ├── config.pbtxt
│ └── 1/
│ └── model.pt
└── ensemble_pipeline/
├── config.pbtxt
└── 1/
Файл config.pbtxt описывает входы, выходы и параметры модели:
name: "text_classifier"
backend: "onnxruntime"
max_batch_size: 32
input [{ name: "input_ids" data_type: TYPE_INT64 dims: [512] }]
output [{ name: "logits" data_type: TYPE_FP32 dims: [2] }]
dynamic_batching { preferred_batch_size: [8, 16, 32] max_queue_delay_microseconds: 5000 }
Dynamic Batching
Динамический батчинг — ключевая фича Triton. Сервер автоматически группирует входящие запросы в батчи, пока не истечёт max_queue_delay_microseconds или не наберётся preferred_batch_size.
Это позволяет получить throughput GPU-батча при latency одиночного запроса — компромисс, который делает Triton эффективным под реальной нагрузкой.
Ensemble Models
Ensemble позволяет описать пайплайн из нескольких моделей как единый граф без написания кода:
name: "rag_pipeline"
platform: "ensemble"
max_batch_size: 8
ensemble_scheduling {
step [
{ model_name: "embedding_model" model_version: 1
input_map { key: "text" value: "INPUT_TEXT" }
output_map { key: "embedding" value: "EMBEDDING" } },
{ model_name: "classifier" model_version: 1
input_map { key: "embedding" value: "EMBEDDING" }
output_map { key: "label" value: "OUTPUT_LABEL" } }
]
}
Данные передаются между шагами в памяти — без сериализации и HTTP-вызовов.
Docker Deploy
docker run --gpus all --rm -p 8000:8000 -p 8001:8001 -p 8002:8002
-v /path/to/model_repository:/models
nvcr.io/nvidia/tritonserver:24.01-py3
tritonserver --model-repository=/models
--log-verbose=1
--metrics-port=8002
Порты: 8000 — HTTP, 8001 — gRPC, 8002 — метрики Prometheus.
Метрики Prometheus
Triton экспортирует богатый набор метрик:
nv_inference_request_success— количество успешных инференсовnv_inference_queue_duration_us— время ожидания в очередиnv_inference_compute_infer_duration_us— время GPU-вычисленийnv_gpu_utilization— утилизация GPU
# Проверка метрик
curl http://localhost:8002/metrics | grep nv_inference
gRPC vs HTTP
HTTP/REST — удобен для отладки и интеграции с любым клиентом. gRPC — в 2–4 раза быстрее за счёт бинарного протокола Protocol Buffers, обязателен для high-throughput сценариев.
import tritonclient.grpc as grpcclient
client = grpcclient.InferenceServerClient(url='localhost:8001')
inputs = [grpcclient.InferInput('input_ids', [1, 512], 'INT64')]
inputs[0].set_data_from_numpy(input_array)
outputs = [grpcclient.InferRequestedOutput('logits')]
response = client.infer('text_classifier', inputs, outputs=outputs)
Concurrent Model Execution
Triton может запускать несколько копий одной модели параллельно на одном GPU:
instance_group [{ kind: KIND_GPU count: 2 }]
Это особенно полезно для небольших моделей, которые не утилизируют GPU полностью при единичном запуске.
Triton — стандарт индустрии для production ML inference. Если ваши модели обслуживают реальных пользователей, переход с Flask на Triton окупается уже при нагрузке от 10 RPS.
Ensemble в Triton — это вообще отдельная магия. Настроил пайплайн preprocessing → embedding → reranker без единой строки Python-кода на стороне сервера, и это работает заметно быстрее, чем склеивать всё через FastAPI.