Spark on Kubernetes
Running Apache Spark on Kubernetes (K8s) allows you to use Kubernetes as a native cluster manager—replacing traditional managers like Standalone, YARN, or Mesos.
Spark achieved full, production-ready support for Kubernetes in version 3.1. Instead of needing a dedicated Spark cluster, Spark workloads can share CPU, memory, and GPU resources directly alongside your microservices and databases on a standard Kubernetes cluster.
Core Architecture & Flow of a Spark Job on K8s
When you run a Spark application on Kubernetes, the Spark Driver runs inside a K8s Pod and talks directly to the Kubernetes API Server to request and manage Executor Pods.
- Submission: You submit an application using
spark-submit(configured with--master k8s://https://<k8s-api-server>) or via a Kubernetes Custom Resource (CRD) using the Spark Operator. - Driver Pod Creation: The Kubernetes API Server creates the Spark Driver Pod.
- Executor Request: Once initialized, the Driver Pod uses its internal
KubernetesClusterSchedulerBackendto authenticate with the K8s API Server (using a assigned ServiceAccount) and requests executor pods based on your configuration (spark.executor.instances). - Executor Scheduling: Kubernetes schedules and launches the Executor Pods.
- Execution & Communication: The Executor Pods connect directly back to the Driver Pod over the internal Pod network to receive tasks, stream heartbeat metrics, and exchange shuffle/broadcast data.
- Teardown: When the Spark job finishes, the Executor Pods are terminated and cleaned up automatically. The Driver Pod remains in a
Completedstate (keeping logs accessible) until explicitly deleted or cleaned up by a Garbage Collection policy.
Key Components Under the Hood
1. Dynamic Allocation (Scaling Up & Down)
Spark on K8s supports Dynamic Resource Allocation. When enabled (spark.dynamicAllocation.enabled=true), the Driver monitors task queue backlogs:
- If tasks accumulate, it requests additional Executor Pods from the K8s API.
- If executors sit idle, the Driver requests the K8s API to terminate them, returning resources back to the cluster.
2. Local Storage & Shuffle Data Handling
Shuffle data (intermediate map outputs) needs fast disk access. On K8s, this is handled via:
emptyDirvolumes: Uses node-local ephemeral storage or RAM-backedtmpfs.- PersistentVolumeClaims (PVCs): Mounts network storage (EBS, Persistent Disk, NFS) to executors.
- External Shuffle Service / Push-Based Shuffle: Modern Spark on K8s uses Push-Based Shuffle (e.g., Remote Shuffle Service or Apache Uniffle) to prevent shuffle data loss when Executor Pods are preempted or dynamically scaled down.
3. Pod Templates & Customization
You can customize the driver and executor pods (adding sidecars, custom environment variables, annotations, or security contexts) using Pod Templates:
# pod-template.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: spark-workload
spec:
containers:
- name: spark-kubernetes-executor
resources:
limits:
nvidia.com/gpu: '1'
You pass these to Spark via spark.kubernetes.executor.podTemplateFile.
Two Ways to Deploy Spark on K8s
| Deployment Method | How It Works | Best Used For |
|---|---|---|
spark-submit CLI |
Direct CLI call pointing to k8s://<api-server-url>. |
Ad-hoc jobs, local testing, or traditional CI/CD pipelines. |
| Spark Operator | Uses a K8s Custom Resource (SparkApplication / ScheduledSparkApplication) managed by an active in-cluster controller. |
Production declarative deployments (GitOps, ArgoCD), scheduled cron jobs, and native K8s monitoring. |
Example: SparkApplication Custom Resource (Spark Operator)
apiVersion: 'sparkoperator.k8s.io/v1beta2'
kind: SparkApplication
metadata:
name: spark-pi
namespace: spark-jobs
spec:
type: Scala
mode: cluster
image: 'apache/spark:v3.5.0'
mainClass: org.apache.spark.examples.SparkPi
mainApplicationFile: 'local:///opt/spark/examples/jars/spark-examples_2.12-3.5.0.jar'
sparkVersion: '3.5.0'
driver:
cores: 1
memory: '512m'
serviceAccount: spark-sa
executor:
cores: 1
instances: 2
memory: '512m'
Major Advantages vs. Challenges
Advantages
- Unified Infrastructure: Consolidates web apps, microservices, and big data ML pipelines on the same K8s clusters (EKS, GKE, AKS, or on-prem).
- Isolation & Multi-Tenancy: Each Spark application can use its own container image, Python version, and dependency isolation without library conflicts.
- Cost Efficiency with Spot Instances: Uses K8s node affinity, tolerations, and preemption handling to run executor pods on cheap Spot/Preemptible instances.
Challenges
- Node Startup Overhead: Spinning up new K8s pods adds a few seconds of cold-start latency compared to long-running YARN or Standalone daemons.
- Shuffle Data Loss on Preemption: If a Spot instance terminates an executor node, its local shuffle data is lost unless using remote/push-based shuffle services.