NVIDIA Triton: мультифреймворк‑сервинг на GPU

Задача страницы. Развернуть и эксплуатировать NVIDIA Triton на https://cloudcompute.ru для сервинга моделей PyTorch/ONNX/TensorRT/Python‑backend с динамическим батчингом, пайплайнами (Ensemble), приоритизацией трафика и прозрачной наблюдаемостью, не жертвуя SLO и экономикой.

TL;DR

Сценарии

  • LLM‑инференс: префилл/декод как отдельные модели/инстансы, микробатчинг, стриминг токенов.
  • CV/видеоаналитика: батчирование кадров×камер, NVDEC→предобработка→инференс→постпроцесс.
  • Классический ML‑скoring: ONNX/TensorRT для низкой латентности, Python‑backend для лёгкого препроцесса.
  • Ensemble‑пайплайны: препроцесс→основная модель→постпроцесс как единый граф.
  • A/B и канареечный релиз: две версии модели в одном репозитории, роутинг на уровне шлюза.
  • Edge‑экспорт/общее хранилище моделей: единый репозиторий, промо через CI/CD.

Архитектуры/пайплайны A) Онлайн LLM (низкая латентность)

Клиент → API Gateway (приоритеты, rate limit)
 → Triton: prefill_model (Δ_small, 1–2 инстанса/GPU)
 → Triton: decode_model (высокая конкуррентность)
 → SSE/WebSocket (стриминг токенов)
Метрики: TTFT, p95 latency, tokens/s, q_len_prefill/decode, gpu_util/hbm

B) CV пайплайн через Ensemble

RTSP → NVDEC → Preprocess(Python backend)
 → Detect(ONNX/TensorRT)
 → Postprocess(Python backend) → Event Bus/SSE

C) Офлайн‑батчи (высокий throughput, Interruptible)

Job Queue → N×Triton Executors (Docker)
 ├─ Δ_large batching
 ├─ локальный NVMe кэш
 └─ выгрузка результатов в "тёплое/холодное"

См. также: https://cloudcompute.ru/solutions/throughput-vs-latency/, https://cloudcompute.ru/solutions/performance-tuning/, https://cloudcompute.ru/solutions/multi-gpu/.

Профили GPU / VRAM / ориентиры

**Профиль** **Видеопамять** **LLM/текст** **CV/видео** **Мульти‑модель** **Рекомендации**
**24 ГБ (Compact)** 24 ГБ малые/средние модели, короткий контекст 1080p@30–60, 1–2 потока 1–2 модели Δ≤20–40 мс, 1 инстанс/модель, NVENC ≤2
**48 ГБ (Balanced)** 48 ГБ средние модели, несколько клиентов 1080p@60, 2–3 потока 2–4 модели Δ≤40–80 мс, 2 инстанса/модель, batch 32–128
**80 ГБ (HQ)** 80 ГБ крупные модели/длинный контекст 4K@30–45, 3–4 потока 4–6 моделей Δ≤80–120 мс, 2–3 инстанса, FP8/INT8 кэш

Держите запас VRAM ≥ 20%. Для сложных пайплайнов разделяйте модели по инстансам/процессам.

Репозиторий моделей: структура

models/
 bert/
 1/model.onnx
 config.pbtxt
 detector/
 1/model.plan # TensorRT
 config.pbtxt
 preprocess/
 1/model.py # Python backend
 config.pbtxt
 postprocess/
 1/model.py
 config.pbtxt
 cv_ensemble/
 config.pbtxt # без папки версии — Ensemble как маршрут
``` ## **Конфиги и скелеты**

**1) Docker Compose (Triton + модели + шлюз FastAPI)**

version: "3.9" services: triton: image: nvcr.io/nvidia/tritonserver:xx.yy-py3 runtime: nvidia gpus: all command: > tritonserver --model-repository=/models --exit-on-error=false --response-cache=true --http-port=8000 --grpc-port=8001 --metrics-port=8002 environment:

  • NVIDIA_VISIBLE_DEVICES=all volumes:
  • /data/nvme/models:/models:ro
  • /data/nvme/cache:/cache ports: ["8000:8000","8001:8001","8002:8002"] networks: [serving] gateway: image: ghcr.io/your-org/fastapi-gateway:latest environment:
  • TRITON_HTTP=http://triton:8000 ports: ["8080:8080"] networks: [serving] networks: serving: {}

**2) ONNX‑модель: config.pbtxt (динамический батчинг)**

name: "bert" backend: "onnxruntime" max_batch_size: 128 input [ { name: "input_ids" data_type: TYPE_INT32 dims: [ -1 ] }, { name: "attention_mask" data_type: TYPE_INT32 dims: [ -1 ] } ] output [ { name: "logits" data_type: TYPE_FP16 dims: [ -1 ] } # FP16 для экономии ] dynamic_batching { preferred_batch_size: [ 4, 8, 16, 32, 64 ] max_queue_delay_microseconds: 60000 # Δ = 60 мс (онлайн) } instance_group [ { kind: KIND_GPU, count: 1 } ] response_cache { enable: true }


**3) TensorRT‑модель: config.pbtxt (высокий throughput)**

name: "detector" platform: "tensorrt_plan" max_batch_size: 256 input [ { name: "images" data_type: TYPE_FP16 dims: [ 3, -1, -1 ] } ] output [ { name: "boxes" data_type: TYPE_FP16 dims: [ -1, 4 ] }, { name: "scores" data_type: TYPE_FP16 dims: [ -1 ] }, { name: "labels" data_type: TYPE_INT32 dims: [ -1 ] } ] dynamic_batching { preferred_batch_size: [ 8, 16, 32, 64, 128 ] max_queue_delay_microseconds: 120000 # Δ = 120 мс (офлайн) } instance_group [ { kind: KIND_GPU, count: 2 } # два инстанса на GPU ]


**4) Python‑backend (препроцесс) preprocess/config.pbtxt**

name: "preprocess" backend: "python" max_batch_size: 128 input [{ name: "image_bytes", data_type: TYPE_UINT8, dims: [ -1 ] }] output [{ name: "images", data_type: TYPE_FP16, dims: [ 3, 640, 640 ] }]

models/preprocess/1/model.py

import numpy as np import triton_python_backend_utils as pb class TritonPythonModel: def initialize(self, args): pass def execute(self, requests): responses = [] for req in requests: inp = pb.get_input_tensor_by_name(req, "image_bytes") bs = inp.as_numpy() # (batch, bytes) imgs = [] for b in bs:

... декод JPEG/resize/normalize → (3,640,640) FP16

imgs.append(np.zeros((3,640,640), dtype=np.float16)) out = pb.Tensor("images", np.stack(imgs, axis=0)) responses.append(pb.InferenceResponse(output_tensors=[out])) return responses


**5) Ensemble (CV конвейер) cv\_ensemble/config.pbtxt**

name: "cv_ensemble" platform: "ensemble" max_batch_size: 128 input [{ name: "image_bytes", data_type: TYPE_UINT8, dims: [ -1 ] }] output [ { name: "boxes", data_type: TYPE_FP16, dims: [ -1, 4 ] }, { name: "scores", data_type: TYPE_FP16, dims: [ -1 ] }, { name: "labels", data_type: TYPE_INT32, dims: [ -1 ] } ] ensemble_scheduling { step [ { model_name: "preprocess" input_map { key: "image_bytes" value: "image_bytes" } output_map { key: "images" value: "images" } }, { model_name: "detector" input_map { key: "images" value: "images" } output_map { key: "boxes" value: "boxes" } output_map { key: "scores" value: "scores" } output_map { key: "labels" value: "labels" } } ] }


**6) Клиент FastAPI → Triton (HTTP) со стримингом SSE**

from fastapi import FastAPI from fastapi.responses import StreamingResponse import tritonclient.http as http import numpy as np, io TRITON_URL = "http://localhost:8000" cli = http.InferenceServerClient(url=TRITON_URL) app = FastAPI() @app.post("/detect") def detect(image: bytes): inputs = [http.InferInput("image_bytes", [1, len(image)], "UINT8")] inputs[0].set_data_from_numpy(np.frombuffer(image, dtype=np.uint8).reshape(1, -1)) outputs = [http.InferRequestedOutput("boxes"), http.InferRequestedOutput("scores"), http.InferRequestedOutput("labels")] res = cli.infer("cv_ensemble", inputs=inputs, outputs=outputs) return { "boxes": res.as_numpy("boxes").tolist(), "scores": res.as_numpy("scores").tolist(), "labels": res.as_numpy("labels").tolist(), } @app.get("/generate") def generate(prompt: str): def stream():

пример: ранняя отдача токенов; сервер внутри батчит/декодит

for tok in llm_tokens_from_triton(prompt, delta_ms=20): yield f"data: {tok}nn" return StreamingResponse(stream(), media_type="text/event-stream")


## **Наблюдаемость, метрики, алерты**

См. <https://cloudcompute.ru/solutions/monitoring-logging/> и <https://cloudcompute.ru/solutions/llm-inference/observability/>.

**Ключевые**

- Очереди/батчинг: queue\_delay\_ms (p50/p95), batch\_size\_eff, inflight\_requests.
- Латентность: ttft\_ms (для LLM), end\_to\_end\_ms (p50/p95), inter\_token\_ms.
- Throughput: qps, fps, tokens\_s, prefill\_tps/decode\_tps.
- GPU: gpu\_util\_pct, gpu\_mem\_used\_gb (HBM), sm\_efficiency\_pct, pcie\_tx\_mb\_s.
- Кэш: response\_cache\_hit\_ratio, model\_repo\_cache\_hit\_ratio.
- I/O: i/o\_backlog\_sec, nvme\_read\_mb\_s, nvme\_write\_mb\_s.

**Алерты (примеры)**

- ttft\_ms(p95) &gt; SLO → уменьшить Δ, увеличить инстансы модели/нод.
- queue\_delay\_ms ↑ и gpu\_util\_pct &lt; 30% → узкое место вне GPU (I/O/CPU).
- gpu\_mem\_used\_gb/vram &gt; 0.9 → уменьшить batch/контекст или включить BF16/FP8/INT8 (см. <https://cloudcompute.ru/solutions/fp8-bf16/>).
- response\_cache\_hit\_ratio &lt; целевого при повторяющихся запросах → включить/расширить кэш/ttl.

## **Экономика и формулы**

Обозначения: c\_gpu — цена GPU/час; U — целевая загрузка GPU; Δ — окно batching; QPS — запросов/сек; TPS — токенов/сек; t\_h — часы.

**Цена за запрос**

EffCostPerHour = c\_gpu / U

ReqPerHour = QPS \* 3600

Cost\_per\_query = EffCostPerHour / ReqPerHour

**LLM токены**

TokensPerHour = TPS \* 3600

Cost\_per\_1000\_tok = EffCostPerHour / (TokensPerHour / 1000)

**Влияние Δ и батча**

Latency ≈ queue\_delay(Δ) + service\_time(batch)

Throughput ↑ с ростом batch, но TTFT ↑ → ищите минимум Cost\_per\_query при TTFT(p95) ≤ SLO

Планирование — <https://cloudcompute.ru/solutions/cost-planner/>, компромиссы — <https://cloudcompute.ru/solutions/throughput-vs-latency/>.

**Безопасность и политики**

- **Секреты/ключи**: только в секрет‑хранилищах/переменных окружения контейнеров; не логируйте payload. См. <https://cloudcompute.ru/solutions/security/>.
- **PII/ретеншн**: для пользовательских данных — маскирование, ротация/архивация, раздельные среды. См. <https://cloudcompute.ru/solutions/storage-data/>.
- **Изоляция трафика**: классы приоритета (realtime/bulk) и лимиты; anti‑noisy‑neighbor.
- **Сетевой периметр**: ограничьте доступ к портам HTTP/gRPC/metrics, аудит вызовов.
- **Прерываемость**: офлайн‑джобы идемпотентны, чекпоинты ≤ 120 сек. См. <https://cloudcompute.ru/solutions/interruptible-patterns/>.

**Траблшутинг**

<table><tbody><tr><td>**Симптом**

</td><td>**Причина**

</td><td>**Решение**

</td></tr><tr><td>TTFT вырос, p95 деградирует

</td><td>Δ слишком велик, смешение long/short запросов

</td><td>Снизить Δ для префилла, сегментировать очереди, выделить инстансы под long‑контекст.

</td></tr><tr><td>Очередь растёт при низкой GPU util

</td><td>Узкое место I/O/CPU, препроцесс в Python

</td><td>Вынести препроцесс в TensorRT/ONNX, включить pinned/zero‑copy, увеличить vCPU/NVMe.

</td></tr><tr><td>OOM на больших батчах

</td><td>KV/активации в FP16/BF16

</td><td>Снизить batch, включить INT8/FP8 (где доступно), ограничить контекст. См. /solutions/fp8-bf16/.

</td></tr><tr><td>«Пила» латентности

</td><td>Агрессивный автоскейлинг

</td><td>Ввести гистерезис, зафиксировать min/max инстансы, стабилизировать Δ.

</td></tr><tr><td>Не конвертируется модель

</td><td>Несовместимые операторы/динамические размеры

</td><td>Экспорт в другой формат (TorchScript/ONNX), статические размеры, разделение на препроц/инференс.

</td></tr><tr><td>Python‑backend тормозит

</td><td>GIL/интерпретируемые циклы

</td><td>Векторизовать, Numpy/CuPy, вынести в TensorRT/ONNX, распараллелить инстансы.

</td></tr><tr><td>503 на шлюзе

</td><td>Admission защитил SLO

</td><td>Увеличить capacity realtime‑класса, шардировать по нодам, включить кэш ответа.

</td></tr><tr><td>Падение точности при FP8/INT8

</td><td>Квантование критичных узлов

</td><td>Исключить LN/Softmax/Logits из квантизации, калибровать amax. См. <https://cloudcompute.ru/solutions/fp8-bf16/>.

</td></tr></tbody></table>

**Как запустить в cloudcompute.ru**

1. Выберите шаблон (Docker/SSH/Jupyter) на <https://cloudcompute.ru/solutions/templates/>.
2. Подготовьте **репозиторий моделей** на NVMe: models/… как в примере, версионирование/манифест.
3. Выберите **профиль GPU**: 24 ГБ (Compact) / 48 ГБ (Balanced) / 80 ГБ (HQ).
4. Настройте **dynamic batching** (preferred\_batch\_size, max\_queue\_delay) по SLO, разделите онлайновые и офлайновые модели/инстансы. См. <https://cloudcompute.ru/solutions/throughput-vs-latency/>.
5. Включите **mixed precision/кэш** (BF16/FP8/INT8, response cache) — см. <https://cloudcompute.ru/solutions/fp8-bf16/>.
6. Подключите **наблюдаемость** и алерты — <https://cloudcompute.ru/solutions/monitoring-logging/> и <https://cloudcompute.ru/solutions/llm-inference/observability/>.
7. Для масштабирования — **мульти‑GPU/мульти‑нод** и роутер на шлюзе — <https://cloudcompute.ru/solutions/multi-gpu/>.
8. Зафиксируйте конфиги в CI/CD и промоутируйте версии — <https://cloudcompute.ru/solutions/containers-ci-cd/>.
9. Проверяйте экономику через <https://cloudcompute.ru/solutions/cost-planner/>.

**Чек‑лист перед продом**

- SLO: TTFT(p95), latency(p95), QPS/FPS/TPS определены и проверены.
- Δ и preferred\_batch\_size настроены, очереди стабильны, нет head‑of‑line blocking.
- GPU util 60–85%, запас VRAM ≥ 20%; I/O backlog отсутствует.
- Модели разнесены по инстансам/приоритетам; офлайн — в отдельный пул.
- Кэш ответов/моделей включён и даёт целевой hit‑ratio.
- Метрики/алерты/трейсы доступны; логи чисты от PII.
- Mixed precision (BF16/FP8/INT8) включена там, где качество не проседает.
- Репозиторий моделей версионирован; промо через CI/CD.
- Экономика подтверждена: Cost\_per\_query/Cost\_per\_1M\_tokens в бюджете.

**Навигация**

- Хаб «Решения»: <https://cloudcompute.ru/solutions/>
- Шаблоны запусков: <https://cloudcompute.ru/solutions/templates/>
- Планирование стоимости: <https://cloudcompute.ru/solutions/cost-planner/>
- Тюнинг производительности: <https://cloudcompute.ru/solutions/performance-tuning/>
- FP8/BF16: <https://cloudcompute.ru/solutions/fp8-bf16/>
- Throughput vs Latency: <https://cloudcompute.ru/solutions/throughput-vs-latency/>
- Multi‑GPU/Multi‑node: <https://cloudcompute.ru/solutions/multi-gpu/>
- Хранилища и данные: <https://cloudcompute.ru/solutions/storage-data/>
- Наблюдаемость/логи: <https://cloudcompute.ru/solutions/monitoring-logging/>
- Observability для сервисов: <https://cloudcompute.ru/solutions/llm-inference/observability/>
- Gradio/FastAPI (стриминг UI): <https://cloudcompute.ru/solutions/gradio-fastapi/>
- Контейнеры и CI/CD: <https://cloudcompute.ru/solutions/containers-ci-cd/>

Готовы запустить?

Запустить GPU-сервер