Veröffentlicht am

Kubeflow auf Kubernetes: MLOps-Pipelines einrichten

Teilen:
Authors

Kubeflow auf Kubernetes: ML-Plattform fuer produktionsreife MLOps-Pipelines

TL;DR

  • Kubeflow buendelt Jupyter Notebooks, ML-Pipelines, Hyperparameter-Tuning und Model Serving auf einer Kubernetes-Basis
  • Kubeflow Pipelines (KFP v2) orchestrieren reproduzierbare ML-Workflows als DAGs aus containerisierten Schritten
  • KServe (ehemals KFServing) ermoeglicht Canary-Deployments und Autoscaling fuer Inference-Endpoints
  • Katib automatisiert Hyperparameter-Suche ohne manuelles Trial-and-Error
  • Der Einstieg gelingt am besten mit einem fokussierten Pilotprojekt und schrittweiser Erweiterung

Warum Kubeflow statt zusammengestueckelter Tools

In vielen Teams sieht der ML-Workflow so aus: Jupyter Notebooks auf lokalen Laptops, Training-Skripte per SSH auf einer GPU-VM, Modelle als pickle-Datei auf einem Fileserver, Serving ueber einen selbstgebauten Flask-Endpunkt. Das funktioniert fuer Prototypen, aber nicht fuer Produktion.

Die Probleme sind immer die gleichen: fehlende Reproduzierbarkeit, kein einheitliches Deployment, manuelle Schritte zwischen Experimentierung und Production, und keinerlei Monitoring der Modellqualitaet nach dem Deployment.

Kubeflow loest das, indem es fuer jeden Schritt im ML-Lifecycle eine Kubernetes-native Komponente bereitstellt. Statt fuenf separate Tools zu integrieren (Jupyter, MLflow, Airflow, Seldon, Prometheus), bietet Kubeflow eine konsistente Plattform auf einer gemeinsamen Infrastruktur.

Architektur: Was laeuft wo

Eine Kubeflow-Installation besteht aus mehreren unabhaengigen Komponenten, die jeweils als eigene Kubernetes-Workloads laufen:

KomponenteFunktionKubernetes-Workload
Central DashboardWeb-UI fuer alle KomponentenDeployment + Service
Kubeflow Pipelines (KFP)Workflow-OrchestrierungDeployment + MySQL + Minio
Jupyter NotebooksInteraktive EntwicklungStatefulSet pro User
KatibHyperparameter-TuningController + DB
KServeModel ServingController + Knative/Istio
Training OperatorsDistributed Training (TF, PyTorch)CRDs + Controller

Alle Komponenten teilen sich den Kubernetes-Cluster und nutzen dessen Scheduling, RBAC und Networking. Das bedeutet: ein Ops-Team verwaltet eine Plattform, nicht sechs separate Tools.

Der typische Ressourcenbedarf fuer eine Basisinstallation liegt bei 8-12 vCPUs und 16-32 GB RAM fuer die Plattform-Komponenten selbst. Die ML-Workloads kommen on top und skalieren unabhaengig.

Installation: Kubeflow mit Kustomize deployen

Die Installation erfolgt ueber Kustomize-Manifeste. Fuer einen produktionsreifen Cluster empfehle ich die offiziellen Kubeflow Manifests:

# Voraussetzungen
# - Kubernetes Cluster >= 1.27
# - kubectl und kustomize installiert
# - Mindestens 16 GB RAM auf den Worker-Nodes

# Kubeflow Manifests klonen
git clone https://github.com/kubeflow/manifests.git
cd manifests

# Zum aktuellen Release wechseln
git checkout v1.9.0

# Alle Komponenten installieren (dauert 10-15 Minuten)
while ! kustomize build example | kubectl apply -f -; do
  echo "Retrying..."
  sleep 10
done

# Status pruefen
kubectl get pods -n kubeflow --watch

Der while-Loop ist kein Hack -- Kubeflow hat CRD-Abhaengigkeiten, die beim ersten Apply manchmal noch nicht registriert sind. Nach 2-3 Durchlaeufen ist alles sauber deployed.

Danach ist das Central Dashboard ueber Port-Forward erreichbar:

kubectl port-forward svc/centraldashboard -n kubeflow 8080:80
# Dashboard unter http://localhost:8080
# Default-Login: user@example.com / 12341234

Fuer einen Produktionsbetrieb sollte ein Ingress mit TLS-Terminierung und einer ordentlichen Authentifizierung (Dex + OIDC-Provider) konfiguriert werden. Details zu Ingress-Setups finden sich in unserem Artikel zu Kubernetes Cluster Setup fuer Produktion.

Kubeflow Pipelines: Reproduzierbare ML-Workflows

Kubeflow Pipelines (KFP) ist das Herzstueck der Plattform. Eine Pipeline definiert einen gerichteten azyklischen Graphen (DAG) aus containerisierten Schritten. Jeder Schritt ist ein Container-Image mit definierten Inputs und Outputs.

Hier ein konkretes Beispiel fuer eine einfache Training-Pipeline mit KFP v2:

from kfp import dsl
from kfp import compiler

@dsl.component(
    base_image="python:3.11-slim",
    packages_to_install=["pandas", "scikit-learn"],
)
def prepare_data(
    input_csv: str,
    train_ratio: float,
    train_data: dsl.Output[dsl.Dataset],
    test_data: dsl.Output[dsl.Dataset],
):
    import pandas as pd
    from sklearn.model_selection import train_test_split

    df = pd.read_csv(input_csv)
    train_df, test_df = train_test_split(df, train_size=train_ratio, random_state=42)

    train_df.to_csv(train_data.path, index=False)
    test_df.to_csv(test_data.path, index=False)


@dsl.component(
    base_image="python:3.11-slim",
    packages_to_install=["pandas", "scikit-learn", "joblib"],
)
def train_model(
    train_data: dsl.Input[dsl.Dataset],
    target_column: str,
    model_artifact: dsl.Output[dsl.Model],
):
    import pandas as pd
    from sklearn.ensemble import RandomForestClassifier
    import joblib

    df = pd.read_csv(train_data.path)
    X = df.drop(columns=[target_column])
    y = df[target_column]

    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X, y)

    joblib.dump(model, model_artifact.path)


@dsl.component(
    base_image="python:3.11-slim",
    packages_to_install=["pandas", "scikit-learn", "joblib"],
)
def evaluate_model(
    test_data: dsl.Input[dsl.Dataset],
    model_artifact: dsl.Input[dsl.Model],
    target_column: str,
) -> float:
    import pandas as pd
    from sklearn.metrics import accuracy_score
    import joblib

    df = pd.read_csv(test_data.path)
    X = df.drop(columns=[target_column])
    y = df[target_column]

    model = joblib.load(model_artifact.path)
    predictions = model.predict(X)

    return accuracy_score(y, predictions)


@dsl.pipeline(name="training-pipeline")
def training_pipeline(
    input_csv: str = "gs://my-bucket/raw-data.csv",
    target_column: str = "label",
    train_ratio: float = 0.8,
):
    prep = prepare_data(input_csv=input_csv, train_ratio=train_ratio)
    train = train_model(
        train_data=prep.outputs["train_data"],
        target_column=target_column,
    )
    evaluate_model(
        test_data=prep.outputs["test_data"],
        model_artifact=train.outputs["model_artifact"],
        target_column=target_column,
    )


# Pipeline kompilieren
compiler.Compiler().compile(training_pipeline, "pipeline.yaml")

Jeder @dsl.component-Dekorator erzeugt einen eigenen Container. KFP uebernimmt die Datenweitergabe zwischen den Schritten ueber Artefakte in einem S3-kompatiblen Object Store (standardmaessig Minio). Die kompilierte pipeline.yaml kann ueber die UI oder die KFP-API submitted werden.

Der Vorteil gegenueber einem reinen Python-Skript: Jeder Schritt ist unabhaengig skalierbar, hat eigene Resource Requests, und die gesamte Pipeline ist versioniert und reproduzierbar.

Katib: Hyperparameter-Tuning automatisieren

Katib ist Kubeflows Loesung fuer automatisierte Hyperparameter-Optimierung. Statt manuell Lernraten, Batch-Groessen und Netzwerk-Architekturen durchzuprobieren, definiert man einen Suchraum und laesst Katib die optimale Kombination finden.

Katib unterstuetzt mehrere Suchstrategien:

AlgorithmusEinsatzgebietEffizienz
Random SearchSchnelle Explorationniedrig
Grid SearchKleiner Suchraumniedrig
Bayesian OptimizationMittelgrosse Suchraeumehoch
HyperbandGrosse Suchraeume, fruehes Stoppinghoch
CMA-ESKontinuierliche Parameterhoch

Ein Katib-Experiment wird als Kubernetes Custom Resource definiert. Man gibt den Suchraum, die Metrik (z.B. Accuracy) und die maximale Anzahl an Trials an. Katib startet dann parallel mehrere Training-Pods und trackt die Ergebnisse.

Der Vorteil gegenueber manuellem Tuning: Katib nutzt die Kubernetes-Skalierung, um Dutzende Trials parallel auszufuehren. Was manuell Tage dauert, ist in Stunden erledigt.

KServe: Modelle in Produktion bringen

Das trainierte Modell muss irgendwann Anfragen beantworten. KServe (der Nachfolger von KFServing) macht das Deployment von Inference-Endpoints auf Kubernetes so einfach wie moeglich.

Ein minimales KServe InferenceService:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: churn-predictor
  namespace: kubeflow
spec:
  predictor:
    model:
      modelFormat:
        name: sklearn
      storageUri: "gs://my-bucket/models/churn-model"
      resources:
        requests:
          cpu: 500m
          memory: 512Mi
        limits:
          cpu: "1"
          memory: 1Gi
  # Optional: Canary-Deployment
  # canaryTrafficPercent: 10

KServe kuemmert sich um:

  • Autoscaling -- skaliert Inference-Pods basierend auf Request-Rate, inklusive Scale-to-Zero bei Inaktivitaet
  • Canary Deployments -- neue Modellversionen koennen schrittweise ausgerollt werden
  • Multi-Framework-Support -- sklearn, TensorFlow, PyTorch, XGBoost, ONNX und custom Container
  • Request Batching -- fasst einzelne Requests zu Batches zusammen fuer hoeheren GPU-Durchsatz

Fuer Teams, die GPUs fuer Inference nutzen, ist Scale-to-Zero besonders relevant. GPU-Nodes sind teuer, und ein Inference-Endpoint, der nachts auf null skaliert, spart erhebliche Kosten. Mehr zum Thema GPU-Workloads in unserem Artikel zu GPU Kubernetes Workloads.

Kostenplanung: Was eine Kubeflow-Plattform kostet

Die Kosten setzen sich aus drei Bereichen zusammen:

BereichTypische Kosten (KMU)Bemerkung
Kubernetes-Cluster (Basis)300-600 EUR/Monat3-4 Nodes, Managed Service
GPU-Nodes (on-demand)1-5 EUR/Stunde pro GPUNur waehrend Training/Inference
Object Storage (Artefakte)20-50 EUR/MonatModelle, Daten, Pipeline-Logs
Ops-Aufwand10-20h/MonatPlattform-Wartung, Updates

Die groesste Variable sind GPU-Kosten. Wer regelmaeessig trainiert, sollte Spot/Preemptible Instances nutzen -- das spart 60-80% gegenueber On-Demand-Preisen. Checkpointing im Training-Code ist dann Pflicht, damit unterbrochene Runs fortgesetzt werden koennen.

Eine detaillierte Kosten-Analyse fuer verschiedene Cluster-Groessen findet sich in unserem Kubernetes Hosting Kostenvergleich.

Multi-User-Isolation: Namespaces richtig nutzen

Kubeflow unterstuetzt Multi-User-Isolation ueber Kubernetes Namespaces. Jeder User bekommt einen eigenen Namespace mit eigenen Notebooks, Pipelines und Artefakten. Das ist wichtig fuer:

  • Ressourcen-Trennung -- ResourceQuotas pro Namespace verhindern, dass ein User den gesamten Cluster belegt
  • Datenzugriff -- Kubernetes RBAC steuert, wer auf welche Daten und Modelle zugreifen darf
  • Compliance -- verschiedene Projekte koennen unterschiedliche Sicherheitsanforderungen haben

Die Konfiguration erfolgt ueber Kubeflow Profiles. Jedes Profil erstellt einen Namespace mit den noetigeen RBAC-Rollen und Service Accounts. In Kombination mit Network Policies laesst sich auch der Netzwerkverkehr zwischen Namespaces einschraenken.

Mehr zum Thema Namespace-Management in unserem Artikel zu Kubernetes Namespace-Strategien.

Haeufige Stolpersteine

Istio-Overhead unterschaetzen. Kubeflow nutzt Istio fuer Service Mesh und Authentifizierung. Istio allein braucht 2-4 GB RAM und kann bei kleinen Clustern eine relevante Last darstellen. Alternative: Einige Kubeflow-Distributionen funktionieren auch ohne Istio, z.B. mit Emissary-Ingress.

Zu viel auf einmal installieren. Kubeflow hat viele Komponenten. Nicht alle braucht man von Anfang an. Starten Sie mit Notebooks und Pipelines, fuegen Sie Katib und KServe hinzu, wenn der Bedarf entsteht.

Storage-Performance ignorieren. ML-Workloads lesen und schreiben grosse Datenmengen. Die Standard-StorageClass des Cloud-Providers reicht oft nicht. Testen Sie die I/O-Performance frueh und wechseln Sie bei Bedarf zu schnellerem Storage (SSD-backed PVs, lokaler NVMe fuer Training).

Keine Pipeline-Versionierung. Pipelines sollten wie Code versioniert werden. Kompilierte Pipeline-YAMLs gehoeren ins Git-Repository, nicht nur in die Kubeflow-UI. Das ermoeglicht Code Reviews und Rollbacks.

Vergleich: Kubeflow vs. Alternativen

KriteriumKubeflowMLflow + SeldonSageMaker
InfrastrukturSelf-hosted (K8s)Self-hosted oder CloudAWS-managed
Vendor Lock-inkeiner (Open Source)geringhoch (AWS)
Setup-Aufwandmittel-hochmittelniedrig
Pipeline-EngineKFPextern (Airflow/Prefect)SageMaker Pipelines
Hyperparameter-TuningKatib (integriert)Optuna (extern)integriert
Model ServingKServe (integriert)Seldon CoreSageMaker Endpoints
Multi-UserProfiles + NamespacesmanuellIAM-basiert
GPU-SupportKubernetes-nativabhaengig vom Setupintegriert

Kubeflow punktet bei Flexibilitaet und Unabhaengigkeit von Cloud-Anbietern. Der Trade-off ist ein hoeherer initialer Setup-Aufwand. Fuer Teams, die bereits Kubernetes betreiben, ist die zusaetzliche Komplexitaet ueberschaubar.

Naechste Schritte

Der pragmatischste Einstieg in Kubeflow:

  1. Pilotprojekt waehlen -- ein konkretes ML-Problem mit vorhandenen Daten und messbarem Business-Wert
  2. Cluster bereitstellen -- Managed Kubernetes mit mindestens 3 Nodes und 16 GB RAM pro Node
  3. Kubeflow installieren -- Kernkomponenten (Notebooks, Pipelines) zuerst, Rest bei Bedarf
  4. Erste Pipeline bauen -- den bestehenden ML-Workflow in KFP-Schritte uebersetzen
  5. Messen und iterieren -- Laufzeiten, Kosten und Modellqualitaet tracken

Wer vorher eine Einschaetzung braucht, ob Kubeflow fuer den eigenen Anwendungsfall die richtige Wahl ist, kann sich gerne an uns wenden. Wir helfen bei der Evaluierung und beim initialen Setup.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen