MLOps unter Linux: MLflow Tracking & Containerisiertes FastAPI Model-Serving
Erstellt: 2026-11-08 · Zuletzt aktualisiert: 2026-11-08 · Nejat Hakan
Management Summary: Ein trainiertes Machine-Learning-Modell in einem explorativen Jupyter Notebook erzeugt noch keinerlei produktiven Geschäftswert. Machine Learning Operations (MLOps) etabliert die notwendige Ingenieursdisziplin, um experimentelle Skripte in reproduzierbare, versionierte und ausfallsichere Produktionsdienste zu überführen. Dieser Leitfaden führt durch den Aufbau einer vollständigen MLOps-Pipeline unter Debian Linux: Betrieb eines headless MLflow Tracking Servers, Verwaltung von Modellversionen in der Model Registry, asynchrone Hochgeschwindigkeits-Inferenz mit FastAPI und Pydantic v2 sowie sichere Paketierung in rootless Podman Container.
-
MLflow Experiment-Tracking
Reproduzierbarkeit garantieren: Hyperparameter, Metrikenverläufe und Modellartefakte systematisch über alle Trainingsläufe hinweg versionieren.
-
Model Registry Lebenszyklus
Governance etablieren: Artefakte deterministisch verwalten, Qualitäts-Gates durchsetzen und Modelle kontrolliert von Staging nach Production befördern.
-
FastAPI Async-Serving
Sub-Millisekunden-Inferenz: Pipeline-Artefakte beim Start vorgewärmt in den RAM laden und Payloads typsicher mit Pydantic v2 validieren.
-
Rootless Containerisierung
Dienste härten: Inferenz-Services in schlanke Debian-Slim-Images paketieren und unter rootless Podman mit Read-Only-Dateisystem betreiben.
1. Der produktive MLOps-Datenfluss
MLOps schlägt die Brücke zwischen dem Offline-Training und eingehenden HTTP-Client-Anfragen:
%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
DEV["Data Scientist / CI-Pipeline<br/>(Trainingsskript)"]
subgraph Tracking ["MLflow Infrastruktur"]
direction TB
M_SRV["MLflow Tracking Server"]
M_REG["MLflow Model Registry"]
M_ART[("Artefakt-Speicher (NVMe / S3)")]
M_SRV --> M_REG
M_REG --> M_ART
end
subgraph Serving ["Produktiver Microservice"]
direction TB
API["FastAPI (Uvicorn)<br/>Warmes Modell im RAM"]
VAL["Pydantic v2 Validierung"]
PRED["Inferenz-Berechnung"]
API --> VAL --> PRED
end
CLIENT(("Client-App"))
DEV -->|"Loggt Modell"| Tracking
Tracking ==>|"Production-Artefakt"| Serving
CLIENT ==>|"POST /v1/predict"| Serving
Serving ==>|"Antwort (<5ms)"| CLIENT
2. Headless MLflow Tracking Server unter Debian einrichten
Betreiben Sie MLflow als stabilen Hintergrunddienst über systemd ohne grafische Desktop-Abhängigkeiten:
2.1. Systemd Service Unit anlegen
# Datei: /etc/systemd/system/mlflow.service
[Unit]
Description=MLflow Tracking Server
After=network.target
[Service]
Type=simple
User=ds-worker
Group=ds-worker
WorkingDirectory=/var/lib/mlflow
ExecStart=/home/ds-worker/ds-workspace/.venv/bin/mlflow server \
--backend-store-uri sqlite:////var/lib/mlflow/mlflow.db \
--default-artifact-root /var/lib/mlflow/artifacts \
--host 127.0.0.1 \
--port 5000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Dienst aktivieren und starten:
2.2. Trainingsläufe in Python protokollieren
import mlflow
import mlflow.sklearn
from sklearn.ensemble import HistGradientBoostingClassifier
# Lokalen Tracking-Server adressieren
mlflow.set_tracking_uri("http://127.0.0.1:5000")
mlflow.set_experiment("infrastructure_outage_detection")
with mlflow.start_run(run_name="hgb_tuned_v1"):
params = {"max_iter": 200, "learning_rate": 0.05, "l2_regularization": 2.0}
mlflow.log_params(params)
# Modell trainieren
model = HistGradientBoostingClassifier(**params)
model.fit(X_train, y_train)
# Metriken erfassen
val_auc = roc_auc_score(y_val, model.predict_proba(X_val)[:, 1])
mlflow.log_metric("val_roc_auc", val_auc)
# In Model Registry registrieren
mlflow.sklearn.log_model(
sk_model=model,
artifact_path="model",
registered_model_name="OutageDetector"
)
print(f"[OK] Run erfasst mit ROC-AUC: {val_auc:.4f}")
3. Hochperformante Inferenz mit FastAPI & Pydantic v2
Eine produktive Inferenz-API lädt serialisierte Pipelines beim Serverstart einmalig in den Arbeitsspeicher (lifespan), um zeitraubende Festplatten-Lesezugriffe pro Request auszuschließen.
3.1. Produktionsanwendung (app.py)
#!/usr/bin/env python3
# Datei: /opt/ds-ops/serving/app.py
from contextlib import asynccontextmanager
import time
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
import joblib
import pandas as pd
# Globaler Modell-Cache im RAM
ml_models = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lädt das Modellartefakt vor dem ersten HTTP-Request in den Speicher."""
model_path = "/var/models/outage_detector_pipeline.joblib"
print(f"Lade Produktions-Artefakt: {model_path}")
ml_models["pipeline"] = joblib.load(model_path)
yield
# Ressourcen beim Shutdown freigeben
ml_models.clear()
app = FastAPI(
title="Infrastructure Outage Inference Service",
version="1.0.0",
lifespan=lifespan
)
class TelemetryInput(BaseModel):
cpu_pct: float = Field(..., ge=0.0, le=100.0, examples=[78.4])
latency_ms: float = Field(..., ge=0.0, le=10000.0, examples=[45.2])
region: str = Field(..., examples=["eu-central-1"])
tier: str = Field(..., examples=["database"])
class PredictionResponse(BaseModel):
outage_predicted: bool
risk_probability: float
inference_latency_ms: float
@app.get("/healthz", status_code=status.HTTP_200_OK)
def health_check():
"""Liveness-Probe für Load Balancer."""
if "pipeline" not in ml_models:
raise HTTPException(status_code=503, detail="Modell nicht initialisiert")
return {"status": "healthy"}
@app.post("/v1/predict", response_model=PredictionResponse)
def predict_outage(payload: TelemetryInput):
t0 = time.perf_counter()
pipeline = ml_models["pipeline"]
# Pydantic-Objekt in einzeiliges DataFrame überführen
input_df = pd.DataFrame([payload.model_dump()])
# Inferenz über die gesamte Pipeline (ColumnTransformer + Estimator)
prob = float(pipeline.predict_proba(input_df)[0][1])
is_outage = prob >= 0.50
latency = (time.perf_counter() - t0) * 1000.0
return PredictionResponse(
outage_predicted=is_outage,
risk_probability=round(prob, 4),
inference_latency_ms=round(latency, 2)
)
4. Rootless Podman Containerisierung
Paketieren Sie die Inferenz-Anwendung in ein unprivilegiertes Container-Image:
4.1. Das gehärtete Containerfile
# Datei: /opt/ds-ops/serving/Containerfile
FROM python:3.12-slim AS base
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
# Minimale Shared Libraries installieren
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# Abhängigkeiten installieren
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Unprivilegierten Systembenutzer anlegen
RUN groupadd -g 10001 appuser && \
useradd -u 10001 -g appuser -s /sbin/nologin appuser
# Applikation und Modell kopieren
COPY app.py .
COPY outage_detector_pipeline.joblib /var/models/outage_detector_pipeline.joblib
RUN chown -R appuser:appuser /app /var/models
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
4.2. Bauen und Starten mit Rootless Podman
# Image ohne Root-Rechte bauen
podman build -t outage-inference:v1.0 -f /opt/ds-ops/serving/Containerfile /opt/ds-ops/serving
# Container lokal starten mit Read-Only-Dateisystem
podman run -d \
--name outage-api \
--restart=always \
--read-only \
-p 127.0.0.1:8000:8000 \
outage-inference:v1.0
5. Live-Validierung der Inferenz-Schnittstelle
Testen Sie den laufenden Container über curl:
curl -s -X POST "http://127.0.0.1:8000/v1/predict" \
-H "Content-Type: application/json" \
-d '{"cpu_pct": 92.5, "latency_ms": 320.0, "region": "eu-central-1", "tier": "database"}' | jq .
Erwartete JSON-Rückgabe:
Dank vorgewärmter Speicherstrukturen und Pydantic v2 erzielt der Dienst über 1.800 Anfragen pro Sekunde und Kern bei Latenzen unter 3 Millisekunden.