Серверное оборудование для бизнеса — надёжные решения для любых задач Подробнее →
Статья

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.

3 Ответа

  1. Ensemble в Triton — это вообще отдельная магия. Настроил пайплайн preprocessing → embedding → reranker без единой строки Python-кода на стороне сервера, и это работает заметно быстрее, чем склеивать всё через FastAPI.

  1. gRPC клиент для Python немного громоздкий, зато прирост latency реальный — особенно когда гоняешь большие батчи эмбеддингов. На HTTP накладные расходы на JSON-сериализацию очень чувствуются.

  1. Отличный разбор. Добавлю: не забывайте про perf_analyzer из состава tritonclient — он позволяет нагрузить модель и сразу получить статистику по latency и throughput без внешних инструментов.