> 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-ru/mlops-i-razvertyvanie/bentoml.md).

# BentoML

**BentoML** является современной open-source платформой для **создания, доставки и масштабирования AI-приложений**. Она устраняет разрыв между экспериментами в ML и продакшн-развертыванием, позволяя за минуты упаковать любую модель из любого фреймворка в готовый к продакшну API-сервис. Запускайте BentoML в GPU-облаке Clore.ai для экономичного хостинга AI-приложений.

***

## Что такое BentoML?

BentoML упрощает превращение обученной модели в масштабируемый API-сервис:

* **Независим от фреймворка:** PyTorch, TensorFlow, JAX, scikit-learn, HuggingFace, XGBoost, LightGBM и другие
* **Bento:** Самодостаточный, воспроизводимый артефакт (модель + код + зависимости)
* **Runner:** Масштабируемый модуль инференса модели с автоматическим батчингом
* **Service:** HTTP/gRPC-сервис в стиле FastAPI
* **BentoCloud:** Опциональная управляемая платформа развертывания
* **Сначала Docker:** Любой Bento можно контейнеризировать одной командой

**Ключевые возможности:**

* Адаптивный микробатчинг для оптимизации пропускной способности
* Встроенная валидация входа/выхода с Pydantic
* OpenAPI-спецификация генерируется автоматически
* Метрики Prometheus встроены
* Поддержка потоковой передачи ответа (LLM)

***

## Предварительные требования

| Требование | Минимум      | Рекомендуется   |
| ---------- | ------------ | --------------- |
| VRAM GPU   | 8 ГБ         | 16–24 ГБ        |
| GPU        | Любой NVIDIA | RTX 4090 / A100 |
| ОЗУ        | 8 ГБ         | 16 ГБ           |
| Хранилище  | 20 ГБ        | 40 ГБ           |
| Python     | 3.9+         | 3.11+           |

***

## Шаг 1 — Арендуйте GPU на Clore.ai

1. Войдите в [маркетплейсе clore.ai](https://clore.ai).
2. Нажмите **Маркетплейс** и выберите GPU-инстанс с ≥ 16 ГБ VRAM.
3. Установите Docker-образ: мы будем использовать пользовательскую сборку (см. Шаг 2).
4. Откройте порты: `22` (SSH) и `3000` (сервис BentoML).
5. Нажмите **Аренда**.

***

## Шаг 2 — Dockerfile

У BentoML нет официального GPU Docker-образа, поэтому мы собираем свой:

```dockerfile
FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime

ENV DEBIAN_FRONTEND=noninteractive

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

# Настройка SSH
RUN mkdir /var/run/sshd && \\
    echo 'root:clore123' | chpasswd && \\
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Установите BentoML и распространённые ML-библиотеки
RUN pip install --upgrade pip && \\
    pip install \
        bentoml \\
        transformers \
        accelerate \
        diffusers \\
        Pillow \\
        numpy \\
        scipy \
        tritonclient[all]

WORKDIR /workspace

EXPOSE 22 3000

CMD service ssh start && tail -f /dev/null
```

### Сборка и отправка

Соберите образ и отправьте его в свой аккаунт Docker Hub (замените `YOUR_DOCKERHUB_USERNAME` на ваше фактическое имя пользователя):

```bash
docker build -t YOUR_DOCKERHUB_USERNAME/bentoml-gpu:latest .
docker push YOUR_DOCKERHUB_USERNAME/bentoml-gpu:latest
```

{% hint style="info" %}
BentoML не предоставляет официальный GPU Docker-образ на Docker Hub. Образы `bentoml/bento-server` на Docker Hub предназначены для обслуживания заранее упакованных Bentos и не включают поддержку CUDA. Соберите образ из файла Dockerfile выше для развертываний с GPU на Clore.ai.
{% endhint %}

***

## Шаг 3 — Подключение по SSH

```bash
ssh root@<clore-host> -p <assigned-ssh-port>
```

Проверьте BentoML:

```bash
bentoml --version
# Ожидается: bentoml, version 1.x.x
```

***

## Шаг 4 — Ваш первый сервис BentoML

### Простой текстовый классификатор

Создайте файл сервиса:

```bash
mkdir -p /workspace/my-service
cat > /workspace/my-service/service.py << 'EOF'
import bentoml
from bentoml.io import JSON, Text
import numpy as np

# Определите Runner (модуль модели)
class TextClassifierRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu", "cpu")
    SUPPORTS_CPU_MULTI_THREADING = True
    
    def __init__(self):
        import torch
        from transformers import pipeline
        
        self.classifier = pipeline(
            "text-classification",
            model="distilbert-base-uncased-finetuned-sst-2-english",
            device=0 if torch.cuda.is_available() else -1,
        )
    
    @bentoml.Runnable.method(batchable=True, batch_dim=0)
    def classify(self, texts: list[str]) -> list[dict]:
        results = self.classifier(texts)
        return results

# Создайте Runner
classifier_runner = bentoml.Runner(
    TextClassifierRunnable,
    name="text_classifier",
    max_batch_size=32,
    max_latency_ms=100,
)

# Определите Service
svc = bentoml.Service(
    name="text_classifier_service",
    runners=[classifier_runner],
)

@svc.api(input=Text(), output=JSON())
async def classify(text: str) -> dict:
    """Классифицируйте тональность входного текста."""
    results = await classifier_runner.classify.async_run([text])
    return results[0]
EOF
```

### Запустите сервис

```bash
cd /workspace/my-service

bentoml serve service:svc \\
    --host 0.0.0.0 \
    --port 3000 \\
    --reload
```

{% hint style="info" %}
Параметр `--reload` флаг включает горячую перезагрузку во время разработки. Уберите его в продакшене для стабильности.
{% endhint %}

***

## Шаг 5 — Доступ к сервису

Откройте автоматически сгенерированный Swagger UI:

```
http://<clore-host>:<public-port-3000>
```

Или протестируйте через `curl`:

```bash
curl -X POST http://<clore-host>:<public-port-3000>/classify \\
    -H "Content-Type: text/plain" \\
    -d "Это облачный сервис GPU потрясающий!"
```

Ожидаемый ответ:

```json
{"label": "POSITIVE", "score": 0.9986}
```

***

## Шаг 6 — Сервис классификации изображений

### Сервис модели компьютерного зрения

```python
# /workspace/vision-service/service.py
import bentoml
from bentoml.io import Image, JSON
from PIL import Image as PILImage
import numpy as np

class ImageClassifierRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu",)
    SUPPORTS_CPU_MULTI_THREADING = False
    
    def __init__(self):
        import torch
        import torchvision.transforms as transforms
        from torchvision.models import resnet50, ResNet50_Weights
        
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        weights = ResNet50_Weights.DEFAULT
        self.model = resnet50(weights=weights).to(self.device)
        self.model.eval()
        self.preprocess = weights.transforms()
        self.categories = weights.meta["categories"]
    
    @bentoml.Runnable.method(batchable=True, batch_dim=0)
    def predict(self, images: list) -> list[dict]:
        import torch
        
        batch = torch.stack([self.preprocess(img) for img in images]).to(self.device)
        
        with torch.no_grad():
            predictions = self.model(batch).softmax(dim=1)
        
        results = []
        for pred in predictions:
            top5 = pred.topk(5)
            results.append({
                "predictions": [
                    {"label": self.categories[idx], "score": round(score.item(), 4)}
                    for score, idx in zip(top5.values, top5.indices)
                ]
            })
        return results


image_runner = bentoml.Runner(
    ImageClassifierRunnable,
    name="image_classifier",
    max_batch_size=16,
)

svc = bentoml.Service(
    name="image_classifier_service",
    runners=[image_runner],
)

@svc.api(input=Image(), output=JSON())
async def classify(image: PILImage.Image) -> dict:
    """Классифицируйте изображение с помощью ResNet50."""
    results = await image_runner.predict.async_run([image])
    return results[0]
```

```bash
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

Проверьте с помощью изображения:

```bash
curl -X POST http://<clore-host>:<public-port-3000>/classify \\
    -H "Content-Type: image/jpeg" \\
    --data-binary @/path/to/image.jpg
```

***

## Шаг 7 — Сервис потоковой передачи LLM

Для языковых моделей с потоковыми ответами:

```python
# /workspace/llm-service/service.py
import bentoml
from bentoml.io import JSON, Text
from typing import AsyncGenerator

class LLMRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu",)
    SUPPORTS_CPU_MULTI_THREADING = False
    
    def __init__(self):
        from transformers import AutoModelForCausalLM, AutoTokenizer
        import torch
        
        model_name = "microsoft/phi-2"
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForCausalLM.from_pretrained(
            model_name,
            torch_dtype=torch.float16,
            device_map="auto"
        )
    
    @bentoml.Runnable.method(batchable=False)
    def generate(self, prompt: str, max_tokens: int = 200) -> str:
        import torch
        
        inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda")
        
        with torch.no_grad():
            outputs = self.model.generate(
                **inputs,
                max_new_tokens=max_tokens,
                do_sample=True,
                temperature=0.7,
                pad_token_id=self.tokenizer.eos_token_id,
            )
        
        return self.tokenizer.decode(outputs[0], skip_special_tokens=True)


llm_runner = bentoml.Runner(LLMRunnable, name="llm")

svc = bentoml.Service("llm_service", runners=[llm_runner])

@svc.api(input=JSON(), output=Text())
async def generate(body: dict) -> str:
    prompt = body.get("prompt", "")
    max_tokens = body.get("max_tokens", 200)
    return await llm_runner.generate.async_run(prompt, max_tokens)
```

***

## Шаг 8 — Сохраните и соберите Bento

Один **Bento** — это упакованный, воспроизводимый артефакт:

```python
# /workspace/build_bento.py
import bentoml

# Сохраните модель в хранилище моделей BentoML
import torch
from torchvision.models import resnet50, ResNet50_Weights

model = resnet50(weights=ResNet50_Weights.DEFAULT)
model.eval()

saved_model = bentoml.pytorch.save_model(
    name="resnet50",
    model=model,
    labels={"framework": "pytorch", "task": "image-classification"},
    metadata={"accuracy": 0.80, "dataset": "ImageNet"}
)
print(f"Модель сохранена: {saved_model.tag}")
```

```bash
python /workspace/build_bento.py

# Перечислите сохраненные модели
bentoml models list

# Соберите Bento (требуется bentofile.yaml)
bentoml build
```

### bentofile.yaml

```yaml
service: "service:svc"
labels:
  owner: "ml-team"
  stage: "production"
include:
  - "*.py"
python:
  packages:
    - torch
    - torchvision
    - transformers
    - Pillow
    - numpy
docker:
  python_version: "3.11"
  cuda_version: "12.1"
  system_packages:
    - libgl1
```

```bash
bentoml build

# Перечислите собранные Bentos
bentoml list

# Контейнеризуйте
bentoml containerize image_classifier_service:latest \\
    --image-tag YOUR_DOCKERHUB_USERNAME/my-bento:latest
```

***

## Мониторинг и метрики

BentoML предоставляет метрики Prometheus по адресу `/metrics`:

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

Основные метрики:

```
# Частота запросов
bentoml_service_request_total{endpoint="classify", http_status_code="200"}
# Задержка
bentoml_service_request_duration_seconds{endpoint="classify"}
# Пропускная способность Runner  
bentoml_runner_request_total{runner_name="image_classifier"}
```

***

## Конфигурация адаптивного батчинга

```python
# Тонкая настройка поведения батчинга
image_runner = bentoml.Runner(
    ImageClassifierRunnable,
    name="image_classifier",
    max_batch_size=64,          # Максимум запросов в батче
    max_latency_ms=50,          # Максимальное ожидание перед отправкой
)
```

***

## Устранение неполадок

### Сервис не запускается

```
ОШИБКА - не удалось инициализировать Runner
```

**Решения:**

* Проверьте доступность CUDA: `python -c "import torch; print(torch.cuda.is_available())"`
* Проверьте VRAM GPU: `nvidia-smi`
* Проверьте, что загрузка модели завершена (ищите прогресс загрузки в логах)

### Порт 3000 недоступен

```bash
# Убедитесь, что сервис привязан к 0.0.0.0 (а не к localhost)
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

### Высокая задержка при первом запросе

Это нормально — первый запрос запускает загрузку модели (прогрев). Все последующие запросы будут быстрыми. Добавьте вызов endpoint для прогрева после запуска:

```bash
# Прогрев после запуска
sleep 10 && curl -s -o /dev/null http://localhost:3000/healthz
```

### Ошибки импорта

```
ModuleNotFoundError: No module named 'transformers'
```

**Решение:**

```bash
pip install transformers accelerate
```

***

## Рекомендации по GPU для Clore.ai

BentoML — это фреймворк для сервинга, поэтому требования к GPU полностью зависят от модели, которую вы развертываете. Вот чего ожидать для типичных нагрузок:

| GPU        | VRAM  | Цена Clore.ai                               | Пропускная способность LLM (7B Q4) | Диффузия (SDXL)  | Зрение (ResNet50) |
| ---------- | ----- | ------------------------------------------- | ---------------------------------- | ---------------- | ----------------- |
| RTX 3090   | 24 ГБ | $0.07–0.21/ч                                | \~80 ток/с                         | \~4 изображ./мин | \~400 запросов/с  |
| RTX 4090   | 24 ГБ | $0.14–0.42/ч                                | \~140 ток/с                        | \~8 изображ./мин | \~700 запросов/с  |
| A100 40 ГБ | 40 ГБ | [голое железо](https://clore.ai/bare-metal) | \~110 ток/с                        | \~6 изображ./мин | \~1200 запросов/с |
| A100 80 ГБ | 80 ГБ | [голое железо](https://clore.ai/bare-metal) | \~130 ток/с                        | \~7 изображ./мин | \~1400 запросов/с |

**Рекомендации по вариантам использования:**

* **Обслуживание API LLM (7B–13B):** RTX 3090 ($0.07–0.21/час) — оптимальное соотношение цена/производительность
* **API генерации изображений:** RTX 3090 или RTX 4090 в зависимости от требуемой пропускной способности
* **Большие модели (34B–70B Q4):** A100 40GB ([голое железо](https://clore.ai/bare-metal)) — отлично подходит
* **Продакшн-сервинг нескольких моделей:** A100 80GB для запаса по памяти

{% hint style="info" %}
Адаптивный микробатчинг BentoML **особенно эффективен на A100** — аппаратный планировщик эффективно обрабатывает батчинг, извлекая больший throughput на доллар, чем наивный сервинг с одним запросом за раз. Для API с высоким трафиком A100 40GB часто дает лучший ROI, чем две RTX 4090.
{% endhint %}

***

## Полезные ресурсы

* [Официальная документация BentoML](https://docs.bentoml.com)
* [GitHub BentoML](https://github.com/bentoml/BentoML)
* [Примеры BentoML](https://github.com/bentoml/BentoML/tree/main/examples)
* [Сообщество BentoML в Discord](https://l.bentoml.com/join-slack-space)
* [Галерея BentoML](https://www.bentoml.com/gallery)
* [Краткое руководство: обслуживание LLM](https://docs.bentoml.com/en/latest/get-started/quickstart.html)


---

# 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-ru/mlops-i-razvertyvanie/bentoml.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.
