Cloud allgemein

Data-Workloads auf Kubernetes: Patterns und Stolpersteine

Praktischer Leitfaden zu Stateful Data-Workloads auf Kubernetes — Spark on K8s, Kafka via Strimzi, Pipeline-Orchestrierung mit Argo Workflows.

Harbinger Team3. April 20266 Min. LesezeitAktualisiert 14.5.2026
  • kubernetes
  • spark
  • kafka
  • data-pipelines
  • platform-engineering
  • k8s
Inhaltsverzeichnis15 Abschnitte

Data-Workloads auf Kubernetes: Patterns und Stolpersteine

Kubernetes wurde für stateless, containerisierte Anwendungen designt. Data-Workloads sind oft stateful, ressourcen-hungrig und sensibel gegen Scheduling-Jitter. Trotzdem konsolidieren Teams zunehmend Daten-Infrastruktur auf K8s — getrieben vom Versprechen unified Scheduling, besserer Ressourcen-Auslastung und CI/CD-nativer Workflows.

Dieser Leitfaden deckt ab, was in Production wirklich funktioniert: Spark auf Kubernetes laufen lassen, Kafka via Operator betreiben, Data-Pipelines mit Argo Workflows schedulen und die häufigsten Failure-Patterns vermeiden.

TL;DR

  • K8s-natives Data-Infra: bessere Ressourcen-Auslastung, GitOps-konfigurierbar
  • Spark on K8s: dedizierte Node-Pools, safe-to-evict=false ist Pflicht
  • Kafka via Strimzi: Operator handhabt Lifecycle, TLS, Cruise Control
  • Argo Workflows als K8s-native DAG-Alternative zu Airflow-VM-Setups
  • Stolpersteine: Executor-OOMs, Driver-Eviction, Shuffle-Loss bei Autoscaler-Aktivität

Warum Data-Workloads auf K8s?

Der Case für K8s-native Daten-Infrastruktur ist nicht Hype — sondern operativer Hebel:

Klassischer AnsatzK8s-nativer Ansatz
Per-Cluster Spark-YARNSpark-on-K8s mit Namespace-Isolation
Kafka-VMs mit manuellem Broker-ScalingStrimzi-Operator mit GitOps-Config
Airflow auf dedizierten VMsAirflow K8s-Executor oder Argo Workflows
Separate Infra pro TeamMulti-Tenant-Namespaces mit ResourceQuotas
Manuelles Node-ProvisioningKarpenter / Cluster-Autoscaler

Der Trade-off: Stateful-Workloads brauchen Persistent Storage, kontrollierte Eviction und vorsichtige Netzwerk-Konfiguration, die Stateless-Apps nicht brauchen. Richtig gemacht wird K8s zum echten Force-Multiplier.

Spark auf Kubernetes

Architektur

flowchart TB
    subgraph K8s Cluster
        Driver["Spark Driver Pod
(1 pro Job)"]
        E1["Executor Pod 1"]
        E2["Executor Pod 2"]
        E3["Executor Pod N"]
        PVC["PVC: Spark Event Log
(shared storage)"]
        SHS["Spark History Server"]
    end
    S3["S3 / GCS / ADLS
(data lake)"]
    CI["CI/CD System
(spark-submit)"]
    
    CI -->|spark-submit --master k8s://| Driver
    Driver --> E1
    Driver --> E2
    Driver --> E3
    E1 & E2 & E3 -->|read/write| S3
    Driver -->|event logs| PVC
    SHS -->|reads| PVC

Jobs submitten

# spark-submit gegen K8s-Cluster
spark-submit   --master k8s://https://k8s-api.internal:6443   --deploy-mode cluster   --name etl-orders-daily   --conf spark.kubernetes.namespace=data-platform   --conf spark.kubernetes.container.image=company-registry/spark:3.5.1-python3.11   --conf spark.kubernetes.serviceAccountName=spark-executor   --conf spark.executor.instances=10   --conf spark.executor.cores=4   --conf spark.executor.memory=8g   --conf spark.driver.memory=4g   --conf spark.kubernetes.driver.request.cores=2   --conf spark.kubernetes.executor.request.cores=3   --conf spark.eventLog.enabled=true   --conf spark.eventLog.dir=s3a://data-platform-logs/spark-events/   --conf spark.kubernetes.executor.annotation.cluster-autoscaler.kubernetes.io/safe-to-evict=false   local:///opt/spark/work-dir/jobs/orders_daily.py

Die safe-to-evict=false-Annotation ist kritisch — ohne sie evictet der Cluster-Autoscaler Executor-Pods mitten im Job beim Scale-Down, was zu kaskadierenden Task-Failures führt.

Node-Pools für Data-Workloads

Misch nicht Spark-Executors mit API-Servern im selben Node-Pool. Memory-intensive Spark-Jobs verursachen Noisy-Neighbor-Probleme, die in nicht-verwandten Services Latenz-Spikes erzeugen.

# Terraform: dedizierter Node-Pool für Spark-Executors (GKE-Beispiel)
resource "google_container_node_pool" "spark_executor_pool" {
  name       = "spark-executor-pool"
  cluster    = google_container_cluster.main.name
  location   = var.region

  autoscaling {
    min_node_count  = 0
    max_node_count  = 50
    location_policy = "BALANCED"
  }

  node_config {
    machine_type = "n2-highmem-16"  # 16 vCPU, 128 GB RAM
    disk_size_gb = 200
    disk_type    = "pd-ssd"

    taint {
      key    = "workload"
      value  = "spark-executor"
      effect = "NO_SCHEDULE"
    }

    labels = {
      workload = "spark-executor"
      team     = "data-platform"
    }

    oauth_scopes = [
      "https://www.googleapis.com/auth/cloud-platform",
    ]
  }
}

Match mit Toleration in deiner Spark-Config:

--conf spark.kubernetes.executor.node.selector.workload=spark-executor
--conf spark.kubernetes.executor.tolerations=[{"key":"workload","operator":"Equal","value":"spark-executor","effect":"NoSchedule"}]

Kafka auf Kubernetes mit Strimzi

Der Strimzi-Operator ist der Production-grade Weg, Kafka auf K8s zu betreiben. Er handhabt Broker-Lifecycle, Rolling-Upgrades, TLS-Cert-Rotation und Cruise-Control-Integration für Partition-Rebalancing.

Cluster-Definition

apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
  name: data-platform-kafka
  namespace: kafka
spec:
  kafka:
    version: 3.7.0
    replicas: 3
    listeners:
      - name: plain
        port: 9092
        type: internal
        tls: false
      - name: tls
        port: 9093
        type: internal
        tls: true
        authentication:
          type: tls
      - name: external
        port: 9094
        type: loadbalancer
        tls: true
    config:
      offsets.topic.replication.factor: 3
      transaction.state.log.replication.factor: 3
      transaction.state.log.min.isr: 2
      default.replication.factor: 3
      min.insync.replicas: 2
      inter.broker.protocol.version: "3.7"
      log.retention.hours: 168
      log.segment.bytes: 1073741824
      log.retention.check.interval.ms: 300000
    storage:
      type: jbod
      volumes:
        - id: 0
          type: persistent-claim
          size: 500Gi
          class: premium-ssd
          deleteClaim: false
    resources:
      requests:
        memory: 16Gi
        cpu: "4"
      limits:
        memory: 16Gi
        cpu: "8"
    rack:
      topologyKey: topology.kubernetes.io/zone
    template:
      pod:
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                    - key: strimzi.io/name
                      operator: In
                      values:
                        - data-platform-kafka-kafka
                topologyKey: kubernetes.io/hostname
  zookeeper:
    replicas: 3
    storage:
      type: persistent-claim
      size: 50Gi
      class: premium-ssd
      deleteClaim: false
  entityOperator:
    topicOperator: {}
    userOperator: {}
  cruiseControl: {}

Die rack-Konfiguration mappt auf AZ-Topologie — Strimzi verteilt Replicas automatisch über Zonen, was für HA essentiell ist.

Topic-Management via GitOps

# KafkaTopic CR — manage via Git, nicht kafka-topics.sh
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaTopic
metadata:
  name: orders-events-v2
  namespace: kafka
  labels:
    strimzi.io/cluster: data-platform-kafka
spec:
  partitions: 24
  replicas: 3
  config:
    retention.ms: "604800000"    # 7 Tage
    cleanup.policy: delete
    compression.type: lz4
    min.insync.replicas: "2"
    message.timestamp.type: LogAppendTime

Pipeline-Orchestrierung mit Argo Workflows

Für Daten-Pipelines, die DAG-Semantik ohne separates Airflow-Cluster brauchen, ist Argo Workflows eine überzeugende K8s-native Option.

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: daily-etl-pipeline
  namespace: data-platform
spec:
  entrypoint: etl-dag
  serviceAccountName: argo-workflow-sa
  parallelism: 5
  
  templates:
    - name: etl-dag
      dag:
        tasks:
          - name: extract-orders
            template: spark-job
            arguments:
              parameters:
                - name: job-class
                  value: "com.company.etl.ExtractOrders"
                - name: date
                  value: "{{workflow.parameters.date}}"

          - name: extract-customers
            template: spark-job
            arguments:
              parameters:
                - name: job-class
                  value: "com.company.etl.ExtractCustomers"
                - name: date
                  value: "{{workflow.parameters.date}}"

          - name: transform-and-load
            dependencies: [extract-orders, extract-customers]
            template: spark-job
            arguments:
              parameters:
                - name: job-class
                  value: "com.company.etl.TransformAndLoad"
                - name: date
                  value: "{{workflow.parameters.date}}"

    - name: spark-job
      inputs:
        parameters:
          - name: job-class
          - name: date
      resource:
        action: create
        successCondition: status.applicationState.state == COMPLETED
        failureCondition: status.applicationState.state == FAILED
        manifest: |
          apiVersion: sparkoperator.k8s.io/v1beta2
          kind: SparkApplication
          metadata:
            generateName: etl-job-
            namespace: data-platform
          spec:
            type: Scala
            mode: cluster
            image: company-registry/spark-etl:latest
            mainClass: "{{inputs.parameters.job-class}}"
            arguments:
              - "--date={{inputs.parameters.date}}"
            driver:
              cores: 2
              memory: "4g"
              serviceAccount: spark-executor
            executor:
              cores: 4
              instances: 8
              memory: "8g"

Häufige Failure-Patterns und Fixes

Failure-ModusRoot-CauseFix
Executor OOM-KillsMemory-Limits zu niedrig oder unbounded Broadcast-Joinsspark.sql.autoBroadcastJoinThreshold=-1, Executor-Memory tunen
Driver-Pod evictedDriver auf Node mit Ressourcen-DruckPriorityClass: high-priority für Driver-Pods
Shuffle-Daten verlorenExecutor mid-job evictedRemote-Shuffle-Service (z. B. Magnet, Uniffle)
Kafka-Lag akkumuliertConsumer-Pod CPU-throttledConsumer auf Node-Pool ohne CPU-Limits
PVC-Provisioning-DelayStorageClass-Binding-ModeWaitForFirstConsumer-Binding-Mode
Langsamer Pod-StartGroße Container-ImagesAuf < 2GB optimieren; Image-Pull-Pre-Warming

Observability für K8s Data-Workloads

Instrumentiere auf drei Schichten:

  1. Infrastruktur-Schicht: Node-CPU/Memory/Disk-Sättigung, Pod-Scheduling-Latenz
  2. Workload-Schicht: Spark-Job-Dauer, Kafka-Consumer-Lag, Executor-Failure-Rate
  3. Daten-Schicht: Tabellen-Freshness, Row-Count-Deltas, Schema-Drift

DACH-Spezifika

  • Hetzner Cloud + K8s: Hetzners managed-K8s-Service ist eine preiswerte EU-Alternative, wenn du Spark/Kafka selbst betreiben willst. Keine Premium-SSD-Storage-Klassen wie bei AWS/GCP — für stateful Kafka Network-Storage selbst lösen.
  • OpenTelekomCloud: Bietet K8s mit BSI-C5-Testat, relevant für regulierte deutsche Branchen.
  • Datenresidenz: Spark schreibt nach S3-kompatiblem Storage — Region-Pflicht bei personenbezogenen Daten (Frankfurt, Paris).

FAQ

Lohnt sich Spark on K8s gegenüber EMR/Dataproc? Wenn du schon K8s-Expertise hast und multi-cloud sein willst: ja. Wenn du Single-Cloud bist und Ops minimieren willst: managed-Spark (EMR, Dataproc, Databricks).

Strimzi oder Confluent for Kubernetes? Strimzi ist Open-Source, Apache-2.0, größere Community. Confluent for Kubernetes hat mehr Enterprise-Features (RBAC, Schema-Registry integriert) aber kostet.

Wann Argo Workflows statt Airflow? Argo, wenn du schon stark auf K8s/GitOps setzt und einfache DAGs hast. Airflow, wenn du komplexe Backfill-Logik, viele Operators (S3, JDBC, APIs) und größere UI-Anforderungen hast.

Wie skaliert Strimzi mit Cruise Control? Cruise Control rebalanced Partitionen automatisch über Broker. Wichtig ab ~10 Brokern; vorher manuell ausreichend.

Fazit

Data-Workloads auf Kubernetes zu betreiben ist in Production möglich — aber es verlangt, deine Data-Pods als First-Class-Citizens in deiner Scheduling-, Storage- und Observability-Strategie zu behandeln.

Schlüssel-Patterns, die funktionieren:

  • Dedizierte Node-Pools mit Taints für Spark-Executors
  • Strimzi-Operator mit GitOps-managed KafkaTopic-CRs
  • safe-to-evict=false auf allen Executor-Pods
  • Argo Workflows für K8s-native DAG-Orchestrierung
  • Multi-Layer-Observability: Infra + Workload + Daten

Stand: 14. Mai 2026.

H

Geschrieben von

Harbinger Team

Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastruktur­kritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.

Hat dir das geholfen?

Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.

Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.