Was macht Modal?
Übersicht
Modal ist eine Cloud-Plattform für die serverlose Ausführung von Python-Code mit Schwerpunkt auf KI/ML-Workloads. Wichtige Funktionen:
- GPU-Computing auf Abruf (T4, L4, A10, L40S, A100, H100, H200, B200)
- Serverlose Funktionen mit Autoscaling von null bis tausenden Containern
- Benutzerdefinierte Container-Images, die vollständig in Python-Code definiert werden
- Persistenter Speicher über Volumes für Modellgewichte und Datensätze
- Web-Endpunkte zum Bereitstellen von Modellen und APIs
- Geplante Jobs via Cron oder feste Intervalle
- Subsekunden-Kaltstarts für latenzarme Inferenz
Alles in Modal wird als Code definiert – kein YAML, keine Dockerfiles erforderlich (beides wird jedoch unterstützt).
Wann Diese Skill Verwenden
Verwenden Sie diese Skill, wenn:
- KI/ML-Modelle in der Cloud bereitgestellt oder serviert werden sollen
- GPU-beschleunigte Berechnungen (Training, Inferenz, Feintuning) ausgeführt werden sollen
- Serverlose Web-APIs oder Endpunkte erstellt werden sollen
- Batch-Verarbeitungsaufträge parallel skaliert werden sollen
- Wiederkehrende Aufgaben (Datenpipelines, erneutes Training, Scraping) geplant werden sollen
- Persistenter Cloud-Speicher für Modellgewichte oder Datensätze benötigt wird
- Code in benutzerdefinierten Container-Umgebungen ausgeführt werden soll
- Auftragswarteschlangen oder asynchrone Aufgabenverarbeitungssysteme erstellt werden sollen
Installation und Authentifizierung
Installieren
uv pip install modal
Das Modal Python SDK unterstützt Python 3.10–3.14. Diese Skill zielt auf die stabile modal>=1.0 API (aktuelle Version: 1.4.x).
Authentifizieren
Bevorzugen Sie vorhandene Anmeldeinformationen vor der Erstellung neuer. Nur die beiden Modal-spezifischen
Variablen unten sind relevant – lesen, laden oder exponieren Sie keine anderen Umgebungsvariablen oder .env-Dateiinhalte:
- Überprüfen Sie, ob
MODAL_TOKEN_IDundMODAL_TOKEN_SECRETbereits in der aktuellen Umgebung gesetzt sind. - Wenn nicht, suchen Sie nur diese beiden Schlüssel in einer lokalen
.env-Datei (ignorieren Sie alle anderen Einträge) und laden Sie sie, wenn dies für den Workflow geeignet ist. - Fallen Sie nur auf interaktives
modal setupoder die Generierung neuer Token zurück, wenn keine der beiden Quellen diese beiden Werte bereits bereitstellt.
modal setup
Dies öffnet einen Browser zur Authentifizierung. Für CI/CD oder Headless-Umgebungen verwenden Sie Umgebungsvariablen:
export MODAL_TOKEN_ID=<your-token-id>
export MODAL_TOKEN_SECRET=<your-token-secret>
Wenn Token noch nicht in der Umgebung oder .env verfügbar sind, generieren Sie sie unter https://modal.com/settings
Modal bietet einen kostenlosen Tarif mit 30 $/Monat an Guthaben.
Referenz: Siehe references/getting-started.md für detaillierte Einrichtung und den ersten App-Durchlauf.
Kernkonzepte
App und Funktionen
Eine Modal App gruppiert verwandte Funktionen. Mit @app.function() dekorierte Funktionen werden remote in der Cloud ausgeführt:
import modal
app = modal.App("my-app")
@app.function()
def square(x):
return x ** 2
@app.local_entrypoint()
def main():
# .remote() läuft in der Cloud
print(square.remote(42))
Ausführen mit modal run script.py. Bereitstellen mit modal deploy script.py.
Referenz: Siehe references/functions.md für Lebenszyklus-Hooks, Klassen, .map(), .spawn() und mehr.
Container-Images
Modal erstellt Container-Images aus Python-Code. Der empfohlene Paketinstaller ist uv:
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("torch==2.12.0", "transformers==5.9.0", "accelerate==1.13.0")
.apt_install("git")
)
@app.function(image=image)
def inference(prompt):
from transformers import pipeline
pipe = pipeline("text-generation", model="meta-llama/Llama-3-8B")
return pipe(prompt)
Wichtige Image-Methoden:
.uv_pip_install()— Python-Pakete mit uv installieren (empfohlen).pip_install()— Mit pip installieren (Fallback).apt_install()— Systempakete installieren.run_commands()— Shell-Befehle während des Builds ausführen.run_function()— Python während des Builds ausführen (z. B. Modellgewichte herunterladen).add_local_python_source()— Lokale Module hinzufügen.env()— Umgebungsvariablen setzen
Referenz: Siehe references/images.md für Dockerfiles, Micromamba, Caching, GPU-Build-Schritte.
GPU-Computing
GPUs über den Parameter gpu anfordern:
@app.function(gpu="H100")
def train_model():
import torch
device = torch.device("cuda")
# GPU-Training-Code hier
# Mehrere GPUs
@app.function(gpu="H100:4")
def distributed_training():
...
# GPU-Fallback-Kette
@app.function(gpu=["H100", "A100-80GB", "A100-40GB"])
def flexible_inference():
...
Verfügbare GPUs: T4, L4, A10, L40S, A100-40GB, A100-80GB, RTX-PRO-6000, H100, H200, B200, B200+
- GPUs werden immer als Strings angegeben (z. B.
gpu="H100",gpu="H100:4"). Die altenmodal.gpu.*-Objekte sind ab v0.73.31 veraltet. - Bis zu 8 GPUs pro Container (außer A10: bis zu 4)
- L40S wird für Inferenz empfohlen (Kosten/Leistungs-Verhältnis, 48 GB VRAM)
- H100/A100 können ohne Aufpreis automatisch auf H200/A100-80GB hochgestuft werden
- Verwenden Sie
gpu="H100!", um das automatische Upgrade zu verhindern
Referenz: Siehe references/gpu.md für GPU-Auswahl und Multi-GPU-Training.
Volumes (Persistenter Speicher)
Volumes bieten verteilten, persistenten Dateispeicher:
vol = modal.Volume.from_name("model-weights", create_if_missing=True)
@app.function(volumes={"/data": vol})
def save_model():
# In den gemounteten Pfad schreiben
with open("/data/model.pt", "wb") as f:
torch.save(model.state_dict(), f)
@app.function(volumes={"/data": vol})
def load_model():
model.load_state_dict(torch.load("/data/model.pt"))
- Optimiert für Write-Once, Read-Many-Workloads (Modellgewichte, Datensätze)
- CLI-Zugriff:
modal volume ls,modal volume put,modal volume get - Hintergrund-Auto-Commit alle paar Sekunden
- Schreibgeschützt mounten oder auf ein Unterverzeichnis beschränken mit
vol.with_mount_options(read_only=True, sub_path="subset")
Referenz: Siehe references/volumes.md für v2-Volumes, gleichzeitige Schreibvorgänge und Best Practices.
Secrets
Sicherer Zugriff auf Anmeldeinformationen in Funktionen:
@app.function(secrets=[modal.Secret.from_name("my-api-keys")])
def call_api():
import os
api_key = os.environ["API_KEY"]
# Schlüssel verwenden
Secrets über CLI erstellen: modal secret create my-api-keys API_KEY=sk-xxx
Oder aus einer .env-Datei: modal.Secret.from_dotenv()
Referenz: Siehe references/secrets.md für Dashboard-Einrichtung, mehrere Secrets und Vorlagen.
Web-Endpunkte
Modelle und APIs als Web-Endpunkte bereitstellen:
@app.function()
@modal.fastapi_endpoint()
def predict(text: str):
return {"result": model.predict(text)}
modal serve script.py— Entwicklung mit Hot-Reload und temporärer URLmodal deploy script.py— Produktionsbereitstellung mit permanenter URL- Unterstützt FastAPI, ASGI (Starlette, FastHTML), WSGI (Flask, Django), WebSockets
- Anforderungskörper bis zu 4 GiB, unbegrenzte Antwortgröße
Referenz: Siehe references/web-endpoints.md für ASGI/WSGI-Apps, Streaming, Auth und WebSockets.
Geplante Jobs
Funktionen nach einem Zeitplan ausführen:
@app.function(schedule=modal.Cron("0 9 * * *")) # Täglich um 9 Uhr UTC
def daily_pipeline():
# ETL, erneutes Training, Scraping usw.
...
@app.function(schedule=modal.Period(hours=6))
def periodic_check():
...
Bereitstellen mit modal deploy script.py, um den Zeitplan zu aktivieren.
modal.Cron("...")— Standard-Cron-Syntax, stabil über Bereitstellungen hinwegmodal.Period(hours=N)— Festes Intervall, wird bei erneuter Bereitstellung zurückgesetzt- Ausführungen im Modal-Dashboard überwachen
Referenz: Siehe references/scheduled-jobs.md für Cron-Syntax und -Verwaltung.
Skalierung und Parallelität
Modal skaliert Container automatisch. Grenzen konfigurieren:
@app.function(
max_containers=100, # Obergrenze
min_containers=2, # Warm halten für niedrige Latenz
buffer_containers=5, # Reservekapazität
scaledown_window=300, # Leerlaufsekunden vor Herunterfahren
)
def process(data):
...
Eingaben parallel mit .map() verarbeiten:
results = list(process.map([item1, item2, item3, ...]))
Gleichzeitige Anfragebearbeitung pro Container mit @modal.concurrent aktivieren. Setzen Sie
target_inputs (das Ziel des Autoscalers pro Container) unter max_inputs (die harte
Grenze), um Spielraum beim Hochskalieren zu halten:
@app.function()
@modal.concurrent(max_inputs=10, target_inputs=8)
async def handle_request(req):
...
Eine bereitgestellte Funktion oder Klasse zur Aufrufzeit ohne erneute Bereitstellung neu konfigurieren
mithilfe von Function.with_options() / Function.with_concurrency() / Function.with_batching()
(und Cls.with_options()):
Model = modal.Cls.from_name("my-app", "Model")
fast = Model.with_options(gpu="H200", max_containers=20)
fast().generate.remote(prompt)
Referenz: Siehe references/scaling.md für .map(), .starmap(), .spawn() und Limits.
Ressourcenkonfiguration
@app.function(
cpu=4.0, # Physische Kerne (nicht vCPUs)
memory=16384, # MiB
ephemeral_disk=51200, # MiB (bis zu 3 TiB)
timeout=3600, # Sekunden
)
def heavy_computation():
...
Standardwerte: 0,125 CPU-Kerne, 128 MiB Speicher. Abrechnung nach max(Anforderung, Nutzung).
Referenz: Siehe references/resources.md für Limits und Abrechnungsdetails.
Klassen mit Lebenszyklus-Hooks
Für zustandsbehaftete Workloads (z. B. ein Modell einmal laden und viele Anfragen bedienen):
@app.cls(gpu="L40S", image=image)
class Predictor:
@modal.enter()
def load_model(self):
self.model = load_heavy_model() # Wird einmalig beim Containerstart ausgeführt
@modal.method()
def predict(self, text: str):
return self.model(text)
@modal.exit()
def cleanup(self):
... # Wird beim Herunterfahren des Containers ausgeführt
Aufruf mit: Predictor().predict.remote("hello")
Sandboxes
Zum Ausführen von nicht vertrauenswürdigem oder dynamisch generiertem Code (z. B. KI-Agentenausgabe oder einem Code-Interpreter) verwenden Sie einen modal.Sandbox – einen isolierten Container, den Sie programmgesteuert erstellen und steuern, anstatt eine dekorierte Funktion zu verwenden:
app = modal.App.lookup("sandbox-demo", create_if_missing=True)
# Isolierter Container; Ausgehenden Verkehr für nicht vertrauenswürdige Workloads einschränken
sb = modal.Sandbox.create(
app=app,
image=modal.Image.debian_slim(),
outbound_cidr_allowlist=["10.0.0.0/8"],
)
# Dateien über die Dateisystem-API (Beta) streamen
sb.filesystem.write_text("print(2 ** 10)\n", "/tmp/job.py")
contents = sb.filesystem.read_text("/tmp/job.py")
sb.terminate()
- Befehle innerhalb der Sandbox mit ihrer
exec-Methode ausführen (z. B.python /tmp/job.py) und stdout vom zurückgegebenen Prozess-Handle lesen – siehereferences/api_reference.md - Konnektivität mit
outbound_cidr_allowlist=[...]/inbound_cidr_allowlist=[...]einschränken - Dateisystem mit
sb.snapshot_filesystem()als Basis-Image für die Wiederverwendung sichern - Ideal für Code-Interpreter, Agent-Tool-Ausführung und benutzerspezifische Isolierung
Häufige Workflow-Muster
GPU-Modell-Inferenz-Service
import modal
app = modal.App("llm-service")
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("vllm")
)
@app.cls(gpu="H100", image=image, min_containers=1)
class LLMService:
@modal.enter()
def load(self):
from vllm import LLM
self.llm = LLM(model="meta-llama/Llama-3-70B")
@modal.method()
@modal.fastapi_endpoint(method="POST")
def generate(self, prompt: str, max_tokens: int = 256):
outputs = self.llm.generate([prompt], max_tokens=max_tokens)
return {"text": outputs[0].outputs[0].text}
Batch-Verarbeitungspipeline
app = modal.App("batch-pipeline")
vol = modal.Volume.from_name("pipeline-data", create_if_missing=True)
@app.function(volumes={"/data": vol}, cpu=4.0, memory=8192)
def process_chunk(chunk_id: int):
import pandas as pd
df = pd.read_parquet(f"/data/input/chunk_{chunk_id}.parquet")
result = heavy_transform(df)
result.to_parquet(f"/data/output/chunk_{chunk_id}.parquet")
return len(result)
@app.local_entrypoint()
def main():
chunk_ids = list(range(100))
results = list(process_chunk.map(chunk_ids))
print(f"Verarbeitet {sum(results)} Zeilen insgesamt")
Geplante Datenpipeline
app = modal.App("etl-pipeline")
@app.function(
schedule=modal.Cron("0 */6 * * *"), # Alle 6 Stunden
secrets=[modal.Secret.from_name("db-credentials")],
)
def etl_job():
import os
db_url = os.environ["DATABASE_URL"]
# Extrahieren, transformieren, laden
...
CLI-Referenz
| Befehl | Beschreibung |
|---|---|
modal setup | Bei Modal authentifizieren |
modal run script.py | Lokalen Einstiegspunkt eines Skripts ausführen |
modal serve script.py | Entwicklungsserver mit Hot-Reload |
modal deploy script.py | Für die Produktion bereitstellen |
modal volume ls <name> | Dateien in einem Volume auflisten |
modal volume put <name> <file> | Datei in ein Volume hochladen |
modal volume get <name> <file> | Datei aus einem Volume herunterladen |
modal secret create <name> K=V | Ein Secret erstellen |
modal secret list | Secrets auflisten |
modal app list | Bereitgestellte Apps auflisten |
modal app stop <name> | Eine bereitgestellte App stoppen |
Sicherheitshinweise
- Anmeldeinformationen: Nur
MODAL_TOKEN_IDundMODAL_TOKEN_SECRETsind zur Authentifizierung erforderlich. Lesen, protokollieren oder leiten Sie keine anderen Umgebungsvariablen oder.env-Einträge weiter. - Subprozesse / benutzerdefinierte Server: Einige Muster hier (Multi-GPU-Trainings-Launcher,
@modal.web_server-Apps) rufensubprocess.run/subprocess.Popenoder Shell-Befehle während Builds auf. Halten Sie Argumentlisten fest und fest codiert. Konstruieren Sie niemals Subprozess- oder Shell-Argumente aus nicht bereinigten Benutzereingaben – übergeben Sie nicht vertrauenswürdige Werte als Daten (Dateien, Umgebungsvariablen, stdin), nicht als Befehlsargumente. - Nicht vertrauenswürdiger Code: Führen Sie benutzer- oder modellgenerierten Code in einem
modal.Sandbox(siehe oben) aus, nicht in einer regulären Funktion, und schränken Sie den Netzwerkzugriff mit CIDR-Allowlists ein.
Referenzdateien
Detaillierte Dokumentation zu jedem Thema:
references/getting-started.md— Installation, Authentifizierung, erste Appreferences/functions.md— Funktionen, Klassen, Lebenszyklus-Hooks, Remote-Ausführungreferences/images.md— Container-Images, Paketinstallation, Cachingreferences/gpu.md— GPU-Typen
…
[gekürzt zur Übersetzung]