> 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-de/mlops-and-bereitstellung/bentoml.md).

# BentoML

**BentoML** ist ein modernes Open-Source-Framework für **die Entwicklung, Bereitstellung und Skalierung von KI-Anwendungen**. Es überbrückt die Lücke zwischen ML-Experimenten und dem produktiven Einsatz und ermöglicht es dir, jedes Modell aus jedem Framework in wenigen Minuten in einen produktionsreifen API-Dienst zu verpacken. Führe BentoML auf der GPU-Cloud von Clore.ai für kosteneffizientes Hosting von KI-Anwendungen aus.

***

## Was ist BentoML?

BentoML macht es einfach, ein trainiertes Modell in einen skalierbaren API-Dienst zu verwandeln:

* **Framework-agnostisch:** PyTorch, TensorFlow, JAX, scikit-learn, HuggingFace, XGBoost, LightGBM und mehr
* **Bento:** Ein eigenständiges, reproduzierbares Artefakt (Modell + Code + Abhängigkeiten)
* **Runner:** Skalierbare Modell-Inferenzeinheit mit automatischem Batching
* **Service:** FastAPI-ähnliche HTTP/gRPC-Dienstdefinition
* **BentoCloud:** Optionale verwaltete Bereitstellungsplattform
* **Docker-first:** Jedes Bento kann mit einem Befehl in einen Container umgewandelt werden

**Wichtige Funktionen:**

* Adaptives Micro-Batching zur Optimierung des Durchsatzes
* Integrierte Eingabe-/Ausgabegültigkeitsprüfung mit Pydantic
* OpenAPI-Spezifikation automatisch generiert
* Prometheus-Metriken integriert
* Unterstützung für Streaming-Antworten (LLMs)

***

## Voraussetzungen

| Anforderung | Minimum         | Empfohlen       |
| ----------- | --------------- | --------------- |
| GPU-VRAM    | 8 GB            | 16–24 GB        |
| GPU         | Jede NVIDIA-GPU | RTX 4090 / A100 |
| RAM         | 8 GB            | 16 GB           |
| Speicher    | 20 GB           | 40 GB           |
| Python      | 3.9+            | 3.11+           |

***

## Schritt 1 — Miete eine GPU auf Clore.ai

1. Melde dich an bei [clore.ai](https://clore.ai).
2. Klicken Sie auf **Marktplatz** und wähle eine GPU-Instanz mit ≥ 16 GB VRAM aus.
3. Docker-Image festlegen: Wir verwenden einen benutzerdefinierten Build (siehe Schritt 2).
4. Offene Ports festlegen: `22` (SSH) und `3000` (BentoML-Dienst).
5. Klicken Sie auf **Mieten**.

***

## Schritt 2 — Dockerfile

BentoML hat kein offizielles GPU-Docker-Image, also bauen wir eines:

```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 konfigurieren
RUN mkdir /var/run/sshd && \
    echo 'root:clore123' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# BentoML und gängige ML-Bibliotheken installieren
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
```

### Erstellen und Pushen

Baue das Image und pushe es in dein eigenes Docker-Hub-Konto (ersetze `YOUR_DOCKERHUB_USERNAME` durch deinen tatsächlichen Benutzernamen):

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

{% hint style="info" %}
BentoML stellt auf Docker Hub kein offizielles GPU-Docker-Image bereit. Die `bentoml/bento-server` Images auf Docker Hub dienen zum Bereitstellen vorgepackter Bentos und enthalten keine CUDA-Unterstützung. Baue das Image anhand des obigen Dockerfiles für GPU-fähige Bereitstellungen auf Clore.ai.
{% endhint %}

***

## Schritt 3 — Per SSH verbinden

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

BentoML überprüfen:

```bash
bentoml --version
# Erwartet: bentoml, Version 1.x.x
```

***

## Schritt 4 — Dein erster BentoML-Dienst

### Einfacher Textklassifikator

Erstelle eine Service-Datei:

```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

# Einen Runner definieren (die Modelleinheit)
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 erstellen
classifier_runner = bentoml.Runner(
    TextClassifierRunnable,
    name="text_classifier",
    max_batch_size=32,
    max_latency_ms=100,
)

# Dienst definieren
svc = bentoml.Service(
    name="text_classifier_service",
    runners=[classifier_runner],
)

@svc.api(input=Text(), output=JSON())
async def classify(text: str) -> dict:
    """Sentiment des Eingabetextes klassifizieren."""
    results = await classifier_runner.classify.async_run([text])
    return results[0]
EOF
```

### Dienst starten

```bash
cd /workspace/my-service

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

{% hint style="info" %}
Der `--reload` Die Flag aktiviert Hot-Reload während der Entwicklung. Entferne sie in der Produktion für mehr Stabilität.
{% endhint %}

***

## Schritt 5 — Auf den Dienst zugreifen

Öffne die automatisch generierte Swagger-UI:

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

Oder teste über `curl`:

```bash
curl -X POST http://<clore-host>:<public-port-3000>/classify \\
    -H "Content-Type: text/plain" \\
    -d "This GPU cloud service is amazing!"
```

Erwartete Antwort:

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

***

## Schritt 6 — Bildklassifizierungsdienst

### Vision-Modell-Dienst

```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:
    """Ein Bild mit ResNet50 klassifizieren."""
    results = await image_runner.predict.async_run([image])
    return results[0]
```

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

Mit einem Bild testen:

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

***

## Schritt 7 — LLM-Streaming-Dienst

Für Sprachmodelle mit Streaming-Antworten:

```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)
```

***

## Schritt 8 — Ein Bento speichern und erstellen

Eine **Bento** ist ein paketiertes, reproduzierbares Artefakt:

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

# Modell im BentoML-Modellspeicher ablegen
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"Model saved: {saved_model.tag}")
```

```bash
python /workspace/build_bento.py

# Gespeicherte Modelle auflisten
bentoml models list

# Ein Bento erstellen (erfordert 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

# Erstellte Bentos auflisten
bentoml list

# In einen Container umwandeln
bentoml containerize image_classifier_service:latest \\
    --image-tag YOUR_DOCKERHUB_USERNAME/my-bento:latest
```

***

## Überwachung und Metriken

BentoML stellt Prometheus-Metriken unter bereit `/metrics`:

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

Wichtige Metriken:

```
# Anforderungsrate
bentoml_service_request_total{endpoint="classify", http_status_code="200"}
# Latenz
bentoml_service_request_duration_seconds{endpoint="classify"}
# Runner-Durchsatz  
bentoml_runner_request_total{runner_name="image_classifier"}
```

***

## Konfiguration des adaptiven Batchings

```python
# Batching-Verhalten fein abstimmen
image_runner = bentoml.Runner(
    ImageClassifierRunnable,
    name="image_classifier",
    max_batch_size=64,          # Maximale Anfragen pro Batch
    max_latency_ms=50,          # Maximale Wartezeit vor dem Ausführen
)
```

***

## Fehlerbehebung

### Dienst startet nicht

```
FEHLER - Runner konnte nicht initialisiert werden
```

**Lösungen:**

* CUDA-Verfügbarkeit prüfen: `python -c "import torch; print(torch.cuda.is_available())"`
* GPU-VRAM überprüfen: `nvidia-smi`
* Prüfe, ob der Modell-Download abgeschlossen ist (achte im Protokoll auf den Downloadfortschritt)

### Port 3000 nicht erreichbar

```bash
# Sicherstellen, dass der Dienst an 0.0.0.0 gebunden ist (nicht an localhost)
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

### Hohe Latenz bei der ersten Anfrage

Das ist normal — die erste Anfrage löst das Laden des Modells aus (Warm-up). Alle nachfolgenden Anfragen sind schnell. Füge nach dem Start einen Warm-up-Endpunktaufruf hinzu:

```bash
# Nach dem Start aufwärmen
sleep 10 && curl -s -o /dev/null http://localhost:3000/healthz
```

### Importfehler

```
ModuleNotFoundError: Kein Modul namens 'transformers'
```

**Lösung:**

```bash
pip install transformers accelerate
```

***

## GPU-Empfehlungen für Clore.ai

BentoML ist ein Serving-Framework — die GPU-Anforderungen hängen vollständig von dem Modell ab, das du bereitstellst. Hier ist, was du bei gängigen Workloads erwarten kannst:

| GPU       | VRAM  | Clore.ai-Preis                            | LLM-Durchsatz (7B Q4) | Diffusion (SDXL) | Vision (ResNet50) |
| --------- | ----- | ----------------------------------------- | --------------------- | ---------------- | ----------------- |
| RTX 3090  | 24 GB | ca. 0,07–0,21 $/h                         | \~80 tok/s            | \~4 img/min      | \~400 req/s       |
| RTX 4090  | 24 GB | ca. 0,14–0,42 $/h                         | \~140 tok/s           | \~8 img/min      | \~700 req/s       |
| A100 40GB | 40 GB | [Bare Metal](https://clore.ai/bare-metal) | \~110 tok/s           | \~6 img/min      | \~1200 req/s      |
| A100 80GB | 80 GB | [Bare Metal](https://clore.ai/bare-metal) | \~130 tok/s           | \~7 img/min      | \~1400 req/s      |

**Empfehlungen für Anwendungsfälle:**

* **Bereitstellung von LLM-APIs (7B–13B):** RTX 3090 ($0.07–0.21/Stunde) — optimales Preis-Leistungs-Verhältnis
* **APIs für Bilderzeugung:** RTX 3090 oder RTX 4090, je nach Durchsatzanforderungen
* **Große Modelle (34B–70B Q4):** A100 40 GB ([Bare Metal](https://clore.ai/bare-metal)) — passt bequem
* **Produktives Multi-Modell-Serving:** A100 80 GB für zusätzlichen Speicherpuffer

{% hint style="info" %}
BentoMLs **adaptives Micro-Batching** ist besonders wirksam auf A100s — der Hardware-Scheduler verarbeitet das Batching effizient und erzielt so mehr Durchsatz pro Dollar als naives Serving mit einzelnen Anfragen. Für APIs mit hohem Traffic liefert die A100 40 GB oft einen besseren ROI als zwei RTX 4090s.
{% endhint %}

***

## Nützliche Ressourcen

* [Offizielle BentoML-Dokumentation](https://docs.bentoml.com)
* [BentoML GitHub](https://github.com/bentoml/BentoML)
* [BentoML-Beispiele](https://github.com/bentoml/BentoML/tree/main/examples)
* [BentoML-Discord-Community](https://l.bentoml.com/join-slack-space)
* [BentoML-Galerie](https://www.bentoml.com/gallery)
* [Schnellstart: LLMs bereitstellen](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-de/mlops-and-bereitstellung/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.
