Inhaltsverzeichnis15 Abschnitte
- TL;DR
- Warum Data-Workloads auf K8s?
- Spark auf Kubernetes
- Architektur
- Jobs submitten
- Node-Pools für Data-Workloads
- Kafka auf Kubernetes mit Strimzi
- Cluster-Definition
- Topic-Management via GitOps
- Pipeline-Orchestrierung mit Argo Workflows
- Häufige Failure-Patterns und Fixes
- Observability für K8s Data-Workloads
- DACH-Spezifika
- FAQ
- Fazit
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=falseist 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 Ansatz | K8s-nativer Ansatz |
|---|---|
| Per-Cluster Spark-YARN | Spark-on-K8s mit Namespace-Isolation |
| Kafka-VMs mit manuellem Broker-Scaling | Strimzi-Operator mit GitOps-Config |
| Airflow auf dedizierten VMs | Airflow K8s-Executor oder Argo Workflows |
| Separate Infra pro Team | Multi-Tenant-Namespaces mit ResourceQuotas |
| Manuelles Node-Provisioning | Karpenter / 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-Modus | Root-Cause | Fix |
|---|---|---|
| Executor OOM-Kills | Memory-Limits zu niedrig oder unbounded Broadcast-Joins | spark.sql.autoBroadcastJoinThreshold=-1, Executor-Memory tunen |
| Driver-Pod evicted | Driver auf Node mit Ressourcen-Druck | PriorityClass: high-priority für Driver-Pods |
| Shuffle-Daten verloren | Executor mid-job evicted | Remote-Shuffle-Service (z. B. Magnet, Uniffle) |
| Kafka-Lag akkumuliert | Consumer-Pod CPU-throttled | Consumer auf Node-Pool ohne CPU-Limits |
| PVC-Provisioning-Delay | StorageClass-Binding-Mode | WaitForFirstConsumer-Binding-Mode |
| Langsamer Pod-Start | Große Container-Images | Auf < 2GB optimieren; Image-Pull-Pre-Warming |
Observability für K8s Data-Workloads
Instrumentiere auf drei Schichten:
- Infrastruktur-Schicht: Node-CPU/Memory/Disk-Sättigung, Pod-Scheduling-Latenz
- Workload-Schicht: Spark-Job-Dauer, Kafka-Consumer-Lag, Executor-Failure-Rate
- 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=falseauf allen Executor-Pods- Argo Workflows für K8s-native DAG-Orchestrierung
- Multi-Layer-Observability: Infra + Workload + Daten
Stand: 14. Mai 2026.
Geschrieben von
Harbinger Team
Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastrukturkritische 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.