Bienvenue dans ce module où tu vas apprendre à déployer Apache Spark sur Kubernetes . C’est l’architecture moderne de référence pour exécuter des workloads Spark en production.
Prérequis
✅ Requis
Module 14
Docker Fundamentals
✅ Requis
Module 15
Kubernetes Fundamentals
✅ Requis
Module 16
Kubernetes for Data Workloads
✅ Requis
Module 19
PySpark Advanced
✅ Requis
Module 20
Spark SQL Deep Dive
💡 Recommandé
-
Expérience avec kubectl et Helm
Objectifs du module
À la fin de ce module, tu seras capable de :
Comprendre l’architecture Spark on Kubernetes
Construire une image Docker Spark optimisée
Configurer les ressources Kubernetes (RBAC, Secrets, PVC)
Lancer des jobs avec spark-submit sur K8s
Utiliser Spark Operator pour la production
Configurer l’autoscaling (Dynamic Allocation, KEDA)
Mettre en place le monitoring (Spark UI, Prometheus, Grafana)
Debugger les erreurs courantes
1. Introduction — Pourquoi Spark sur Kubernetes ?
1.1 L’évolution de l’infrastructure Spark
2010s 2020s+
┌─────────────────┐ ┌─────────────────┐
│ Hadoop/YARN │ │ Kubernetes │
│ ┌─────────┐ │ │ ┌─────────┐ │
│ │ Spark │ │ ────────▶ │ │ Spark │ │
│ └─────────┘ │ │ └─────────┘ │
│ On-premise │ │ Cloud-native │
└─────────────────┘ └─────────────────┘
1.2 Avantages de Kubernetes
Cloud-native
Même infra que le reste de tes applications
Multi-cloud
AWS, GCP, Azure, on-premise
Isolation
Namespaces, RBAC, Network Policies
Autoscaling
HPA, VPA, KEDA, Cluster Autoscaler
CI/CD
Images Docker versionnées, GitOps
Cost optimization
Spot instances, autoscaling down
Standardisation
Plus besoin d’expertise Hadoop/YARN
1.3 Support officiel
Spark 2.3 : Support expérimental K8s
Spark 3.0 : Support stable (GA)
Spark 3.1+ : Améliorations (PVC, Pod templates)
Spark 3.4+ : External shuffle service sur K8s
1.4 Comparaison YARN vs Kubernetes
Écosystème
Hadoop-centric
Cloud-native, polyglot
Scheduling
ResourceManager
kube-scheduler
Isolation
Containers/cgroups
Pods, Namespaces, Network Policies
Scaling
Manual ou scripts
HPA, VPA, KEDA, Cluster Autoscaler
Packaging
JAR/ZIP sur HDFS
Images Docker
Secrets
Hadoop credentials
K8s Secrets, Vault
Monitoring
YARN UI, Ganglia
Prometheus, Grafana, native
Multi-tenancy
Queues
Namespaces + RBAC
Data locality
✅ Excellent
⚠️ Limité (shuffle coûteux)
Courbe d’apprentissage
Hadoop stack
K8s stack
Adoption 2024+
Legacy
Standard moderne
📊 Quand choisir quoi ?
YARN si : Kubernetes si :
├── Cluster Hadoop existant ├── Infrastructure cloud-native
├── Data locality critique ├── Multi-cloud ou hybrid
├── Équipe Hadoop experte ├── CI/CD moderne (GitOps)
└── Pas de migration prévue └── Autoscaling avancé requis
2. Architecture Spark on Kubernetes
2.1 Vue d’ensemble
┌─────────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Namespace: spark │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ Driver Pod │ │ Executor Pods │ │ │
│ │ │ ┌───────────┐ │ │ ┌───────┐ ┌───────┐ │ │ │
│ │ │ │ Spark │ │◀────▶│ │ Exec │ │ Exec │ │ │ │
│ │ │ │ Driver │ │ │ │ #1 │ │ #2 │ │ │ │
│ │ │ └───────────┘ │ │ └───────┘ └───────┘ │ │ │
│ │ │ - Spark UI │ │ ┌───────┐ ┌───────┐ │ │ │
│ │ │ - Coordinateur │ │ │ Exec │ │ Exec │ │ │ │
│ │ └─────────────────┘ │ │ #3 │ │ #4 │ │ │ │
│ │ │ │ └───────┘ └───────┘ │ │ │
│ │ │ └─────────────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ ConfigMaps │ │ Secrets │ │ │
│ │ │ - spark-config │ │ - cloud credentials │ │ │
│ │ └─────────────────┘ └─────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐│ │
│ │ │ PersistentVolumeClaims ││ │
│ │ │ - checkpoints - logs - shuffle (optional) ││ │
│ │ └─────────────────────────────────────────────────────┘│ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 Rôles et responsabilités
Pods scheduling
Kubernetes
Placement sur les nodes
Networking
Kubernetes
Communication Driver ↔︎ Executors
Secrets
Kubernetes
Credentials cloud/DB
Storage
Kubernetes
PVC pour checkpoints/logs
Driver
Spark
Coordination du job
Executors
Spark
Exécution des tasks
Shuffle
Spark
Échange de données entre stages
2.3 Client Mode vs Cluster Mode
CLIENT MODE CLUSTER MODE
┌──────────────┐ ┌──────────────────────────────┐
│ Local Machine│ │ Kubernetes Cluster │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ Driver │ │◀─────────┐ │ │ Driver │◀────┐ │
│ └────────┘ │ │ │ │ Pod │ │ │
└──────────────┘ │ │ └────────┘ │ │
│ │ ▲ │ │
┌─────────────────────────┼─────────┐ │ │ │ │
│ K8s Cluster │ │ │ ▼ ▼ │
│ ┌────────┐ ┌────────┐ │ │ │ ┌────────┐ ┌────────┐ │
│ │Executor│ │Executor│◀─┘ │ │ │Executor│ │Executor│ │
│ │ Pod │ │ Pod │ │ │ │ Pod │ │ Pod │ │
│ └────────┘ └────────┘ │ │ └────────┘ └────────┘ │
└───────────────────────────────────┘ └──────────────────────────────┘
Driver
Machine locale
Pod Kubernetes
Usage
Développement, debug
Production
Spark UI
localhost:4040
Via Service/Ingress
Réseau
Doit atteindre le cluster
Interne au cluster
Résilience
❌ Si local crash → job perdu
✅ Pod peut être reschedulé
CI/CD
❌ Difficile
✅ Natif
💡 Règle : Client mode pour le dev/debug, Cluster mode pour la production .
Exercice 1 : Identifier les composants
Réponds aux questions suivantes :
Dans quel mode le Driver tourne-t-il en tant que Pod K8s ?
Qui gère le scheduling des Executor Pods ?
Où sont stockés les credentials cloud ?
💡 Voir les réponses
Cluster mode — Le Driver est un Pod K8s
Kubernetes (kube-scheduler) — Spark demande des Pods, K8s les place
Kubernetes Secrets — Montés dans les Pods Driver/Executor
3. Construire une Image Docker Spark
3.1 Image de base
Plusieurs options :
apache/spark
Officielle
Basique
bitnami/spark
Bien maintenue, non-root
Taille moyenne
gcr.io/spark-operator/spark
Pour Spark Operator
Spécifique
Custom
Contrôle total
Plus de travail
Voir le code
%% writefile / tmp/ spark- docker/ Dockerfile.basic
# Image Spark de base
FROM bitnami/ spark:3.5
# Passer en root pour installer des packages
USER root
# Installer des dépendances Python
RUN pip install -- no- cache- dir \
boto3 \
pyarrow \
pandas
# Copier l'application
COPY app/ / app/
# Revenir à l'utilisateur non-root (sécurité)
USER 1001
# Définir le working directory
WORKDIR / app
3.2 Image production-grade avec JARs
Voir le code
%% writefile / tmp/ spark- docker/ Dockerfile.production
# ============================================
# Spark Production Image
# ============================================
FROM bitnami/ spark:3.5 AS base
USER root
# ---- Dépendances système ----
RUN apt- get update && apt- get install - y -- no- install- recommends \
curl \
&& rm - rf / var/ lib/ apt/ lists/*
# ---- Dépendances Python ----
COPY requirements.txt / tmp/
RUN pip install -- no- cache- dir - r / tmp/ requirements.txt
# ---- JARs pour connecteurs cloud ----
# AWS S3
RUN curl - sL https:// repo1.maven.org/ maven2/ org/ apache/ hadoop/ hadoop- aws/ 3.3.4 / hadoop- aws- 3.3.4 .jar \
- o / opt/ bitnami/ spark/ jars/ hadoop- aws- 3.3.4 .jar
RUN curl - sL https:// repo1.maven.org/ maven2/ com/ amazonaws/ aws- java- sdk- bundle/ 1.12.262 / aws- java- sdk- bundle- 1.12.262 .jar \
- o / opt/ bitnami/ spark/ jars/ aws- java- sdk- bundle- 1.12.262 .jar
# ---- Application ----
COPY app/ / app/
# ---- Sécurité : non-root ----
RUN chown - R 1001 :1001 / app
USER 1001
WORKDIR / app
# ---- Healthcheck ----
HEALTHCHECK -- interval= 30 s -- timeout= 10 s -- retries= 3 \
CMD curl - f http:// localhost:4040 / api/ v1/ applications || exit 1
Voir le code
%% writefile / tmp/ spark- docker/ requirements.txt
# Python dependencies for Spark jobs
boto3>= 1.28.0
pyarrow>= 14.0.0
pandas>= 2.0.0
requests>= 2.31.0
3.3 Best practices Docker pour Spark
Non-root
Sécurité
USER 1001
Multi-stage builds
Image plus légère
FROM ... AS builder
Layer caching
Builds plus rapides
Dépendances avant code
No cache pip
Image plus légère
--no-cache-dir
Healthcheck
K8s liveness probe
HEALTHCHECK
Versioning
Reproductibilité
Tags explicites (pas latest)
Voir le code
# Commandes pour construire et pousser l'image
docker_commands = """
# Construire l'image
docker build -t my-registry/spark-app:1.0.0 -f Dockerfile.production .
# Tester localement
docker run --rm my-registry/spark-app:1.0.0 spark-submit --version
# Pousser vers un registry
docker push my-registry/spark-app:1.0.0
# Pour Minikube (utiliser le Docker daemon de Minikube)
eval $(minikube docker-env)
docker build -t spark-app:1.0.0 .
"""
print (docker_commands)
Exercice 2 : Construire une image Spark
Objectif : Créer un Dockerfile Spark avec :
Base bitnami/spark:3.5
Dépendance Python : numpy
Un script hello.py qui affiche “Hello Spark on K8s!”
💡 Solution
FROM bitnami/spark:3.5
USER root
RUN pip install --no-cache-dir numpy
COPY hello.py /app/hello.py
USER 1001
WORKDIR /app
# hello.py
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("Hello" ).getOrCreate()
print ("Hello Spark on K8s!" )
spark.stop()
4. Configuration Kubernetes pour Spark
4.1 Namespace dédié
Voir le code
%% writefile / tmp/ spark- k8s/ namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: spark
labels:
app: spark
environment: development
4.2 ServiceAccount & RBAC
Spark a besoin de permissions pour :
Créer des Pods (executors)
Lister/supprimer des Pods
Accéder aux ConfigMaps et Secrets
Voir le code
%% writefile / tmp/ spark- k8s/ rbac.yaml
# ServiceAccount pour Spark
apiVersion: v1
kind: ServiceAccount
metadata:
name: spark- sa
namespace: spark
---
# Role avec permissions minimales
apiVersion: rbac.authorization.k8s.io/ v1
kind: Role
metadata:
name: spark- role
namespace: spark
rules:
# Gérer les pods (executors)
- apiGroups: ["" ]
resources: ["pods" ]
verbs: ["create" , "get" , "list" , "watch" , "delete" ]
# Logs des pods
- apiGroups: ["" ]
resources: ["pods/log" ]
verbs: ["get" , "list" ]
# ConfigMaps pour Spark config
- apiGroups: ["" ]
resources: ["configmaps" ]
verbs: ["create" , "get" , "list" , "watch" , "delete" ]
# Services pour Spark UI
- apiGroups: ["" ]
resources: ["services" ]
verbs: ["create" , "get" , "delete" ]
# PersistentVolumeClaims
- apiGroups: ["" ]
resources: ["persistentvolumeclaims" ]
verbs: ["create" , "get" , "list" , "delete" ]
---
# Binding Role → ServiceAccount
apiVersion: rbac.authorization.k8s.io/ v1
kind: RoleBinding
metadata:
name: spark- role- binding
namespace: spark
subjects:
- kind: ServiceAccount
name: spark- sa
namespace: spark
roleRef:
kind: Role
name: spark- role
apiGroup: rbac.authorization.k8s.io
4.3 Secrets (credentials cloud)
⚠️ Note : En production, utilise un gestionnaire de secrets (Vault, AWS Secrets Manager, etc.)
Voir le code
%% writefile / tmp/ spark- k8s/ secrets.yaml
# Secret pour MinIO (ou S3)
apiVersion: v1
kind: Secret
metadata:
name: minio- credentials
namespace: spark
type : Opaque
stringData:
AWS_ACCESS_KEY_ID: "minioadmin"
AWS_SECRET_ACCESS_KEY: "minioadmin"
S3_ENDPOINT: "http://minio.minio.svc.cluster.local:9000"
Voir le code
# Alternative : créer le secret via CLI
secret_command = """
kubectl create secret generic minio-credentials \
--namespace=spark \
--from-literal=AWS_ACCESS_KEY_ID=minioadmin \
--from-literal=AWS_SECRET_ACCESS_KEY=minioadmin \
--from-literal=S3_ENDPOINT=http://minio.minio.svc.cluster.local:9000
"""
print (secret_command)
4.4 PersistentVolumeClaims
Voir le code
%% writefile / tmp/ spark- k8s/ pvc.yaml
# PVC pour les logs Spark
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: spark- logs- pvc
namespace: spark
spec:
accessModes:
- ReadWriteMany # Plusieurs pods peuvent écrire
resources:
requests:
storage: 10 Gi
storageClassName: standard # Adapter selon ton cluster
---
# PVC pour les checkpoints (streaming)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: spark- checkpoints- pvc
namespace: spark
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20 Gi
storageClassName: standard
4.5 ResourceQuotas & LimitRanges (optionnel mais recommandé)
Voir le code
%% writefile / tmp/ spark- k8s/ quotas.yaml
# Limiter les ressources du namespace Spark
apiVersion: v1
kind: ResourceQuota
metadata:
name: spark- quota
namespace: spark
spec:
hard:
requests.cpu: "20" # Max 20 CPU demandés
requests.memory: "40Gi" # Max 40 Gi mémoire demandée
limits.cpu: "40" # Max 40 CPU limites
limits.memory: "80Gi" # Max 80 Gi mémoire limites
pods: "50" # Max 50 pods
---
# Defaults pour les pods sans spec
apiVersion: v1
kind: LimitRange
metadata:
name: spark- limits
namespace: spark
spec:
limits:
- type : Container
default:
cpu: "1"
memory: "2Gi"
defaultRequest:
cpu: "500m"
memory: "1Gi"
max :
cpu: "8"
memory: "16Gi"
Exercice 3 : Créer les manifests RBAC
Question : Pourquoi le ServiceAccount Spark a-t-il besoin de la permission pods/log ?
💡 Réponse
Le Driver Spark a besoin de lire les logs des Executor Pods pour :
Afficher les erreurs dans le Spark UI
Permettre le debugging via kubectl logs
Collecter les métriques d’exécution
5. spark-submit sur Kubernetes
5.1 Syntaxe de base
Voir le code
spark_submit_basic = """
# Spark-submit basique sur K8s (cluster mode)
spark-submit \
--master k8s://https://<kubernetes-api-server>:6443 \
--deploy-mode cluster \
--name spark-pi \
--conf spark.kubernetes.container.image=spark-app:1.0.0 \
--conf spark.kubernetes.namespace=spark \
--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-sa \
local:///app/pi.py
"""
print (spark_submit_basic)
5.2 Commande complète production-grade
Voir le code
spark_submit_full = """
# Spark-submit complet pour production
spark-submit \
# === Master & Mode ===
--master k8s://https://$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}') \
--deploy-mode cluster \
--name etl-job-$(date +%Y%m %d -%H%M%S) \
\
# === Image & Namespace ===
--conf spark.kubernetes.container.image=my-registry/spark-app:1.0.0 \
--conf spark.kubernetes.namespace=spark \
--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-sa \
\
# === Driver Resources ===
--conf spark.driver.cores=2 \
--conf spark.driver.memory=4g \
--conf spark.kubernetes.driver.request.cores=1 \
--conf spark.kubernetes.driver.limit.cores=2 \
\
# === Executor Resources ===
--conf spark.executor.instances=4 \
--conf spark.executor.cores=2 \
--conf spark.executor.memory=4g \
--conf spark.kubernetes.executor.request.cores=1 \
--conf spark.kubernetes.executor.limit.cores=2 \
\
# === Secrets (environnement) ===
--conf spark.kubernetes.driver.secretKeyRef.AWS_ACCESS_KEY_ID=minio-credentials:AWS_ACCESS_KEY_ID \
--conf spark.kubernetes.driver.secretKeyRef.AWS_SECRET_ACCESS_KEY=minio-credentials:AWS_SECRET_ACCESS_KEY \
--conf spark.kubernetes.executor.secretKeyRef.AWS_ACCESS_KEY_ID=minio-credentials:AWS_ACCESS_KEY_ID \
--conf spark.kubernetes.executor.secretKeyRef.AWS_SECRET_ACCESS_KEY=minio-credentials:AWS_SECRET_ACCESS_KEY \
\
# === S3/MinIO Config ===
--conf spark.hadoop.fs.s3a.endpoint=http://minio.minio.svc.cluster.local:9000 \
--conf spark.hadoop.fs.s3a.path.style.access=true \
--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \
\
# === Application ===
local:///app/etl_job.py \
--input s3a://bronze/data \
--output s3a://silver/data
"""
print (spark_submit_full)
5.3 Configurations essentielles
spark.kubernetes.container.image
Image Docker Spark
registry/spark:1.0
spark.kubernetes.namespace
Namespace K8s
spark
spark.kubernetes.authenticate.driver.serviceAccountName
ServiceAccount
spark-sa
spark.executor.instances
Nombre d’executors
4
spark.executor.cores
Cores par executor
2
spark.executor.memory
Mémoire par executor
4g
spark.kubernetes.driver.secretKeyRef.*
Secrets → env vars
Voir exemple
spark.kubernetes.executor.deleteOnTermination
Cleanup
true
5.4 Accéder au Spark UI
En cluster mode, le Spark UI est dans le Pod Driver.
Voir le code
spark_ui_access = """
# 1. Trouver le pod Driver
kubectl get pods -n spark -l spark-role=driver
# 2. Port-forward vers le Spark UI
kubectl port-forward -n spark <driver-pod-name> 4040:4040
# 3. Ouvrir dans le navigateur
# http://localhost:4040
# Alternative : créer un Service
kubectl expose pod <driver-pod-name> -n spark \
--port=4040 --target-port=4040 \
--name=spark-ui --type=NodePort
"""
print (spark_ui_access)
Exercice 4 : Lancer un job Spark sur Minikube
Étapes : 1. Démarrer Minikube : minikube start --cpus=4 --memory=8g 2. Créer le namespace et RBAC 3. Lancer le job SparkPi intégré
💡 Solution
# 1. Setup
minikube start --cpus = 4 --memory = 8g
kubectl create namespace spark
kubectl apply -f rbac.yaml
# 2. Obtenir l'URL de l'API K8s
K8S_API = $( kubectl config view -o jsonpath='{.clusters[0].cluster.server}' )
# 3. Lancer SparkPi
spark-submit \
--master k8s://$K8S_API \
--deploy-mode cluster \
--name spark-pi \
--conf spark.kubernetes.container.image=apache/spark:3.5.0 \
--conf spark.kubernetes.namespace=spark \
--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-sa \
--conf spark.executor.instances=2 \
local:///opt/spark/examples/src/main/python/pi.py 100
# 4. Vérifier
kubectl get pods -n spark
kubectl logs -n spark < driver-pod> | tail -20
6. Spark Operator — Production-Grade
6.1 Pourquoi utiliser Spark Operator ?
Commande CLI
Manifeste YAML déclaratif
Pas de retry automatique
Retry policy configurable
Pas de scheduling
CronJob-like scheduling
Difficile à intégrer CI/CD
GitOps natif
Logs dispersés
Logs centralisés
6.2 Installation avec Helm
Voir le code
operator_install = """
# Ajouter le repo Helm
helm repo add spark-operator https://kubeflow.github.io/spark-operator
helm repo update
# Installer l'opérateur
helm install spark-operator spark-operator/spark-operator \
--namespace spark-operator \
--create-namespace \
--set webhook.enable=true \
--set sparkJobNamespace=spark
# Vérifier
kubectl get pods -n spark-operator
"""
print (operator_install)
6.3 SparkApplication CRD
Voir le code
%% writefile / tmp/ spark- k8s/ spark- application.yaml
apiVersion: sparkoperator.k8s.io/ v1beta2
kind: SparkApplication
metadata:
name: etl- job
namespace: spark
spec:
type : Python
pythonVersion: "3"
mode: cluster
image: my- registry/ spark- app:1.0.0
imagePullPolicy: Always
mainApplicationFile: local:/// app/ etl_job.py
arguments:
- "--input"
- "s3a://bronze/data"
- "--output"
- "s3a://silver/data"
sparkVersion: "3.5.0"
# Configuration Spark
sparkConf:
"spark.hadoop.fs.s3a.endpoint" : "http://minio.minio.svc.cluster.local:9000"
"spark.hadoop.fs.s3a.path.style.access" : "true"
"spark.hadoop.fs.s3a.impl" : "org.apache.hadoop.fs.s3a.S3AFileSystem"
# Restart policy
restartPolicy:
type : OnFailure
onFailureRetries: 3
onFailureRetryInterval: 30
onSubmissionFailureRetries: 3
# Driver config
driver:
cores: 1
coreLimit: "1200m"
memory: "2g"
serviceAccount: spark- sa
labels:
app: spark- etl
role: driver
envSecretKeyRefs:
AWS_ACCESS_KEY_ID:
name: minio- credentials
key: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
name: minio- credentials
key: AWS_SECRET_ACCESS_KEY
# Executor config
executor:
cores: 2
coreLimit: "2400m"
instances: 4
memory: "4g"
labels:
app: spark- etl
role: executor
envSecretKeyRefs:
AWS_ACCESS_KEY_ID:
name: minio- credentials
key: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
name: minio- credentials
key: AWS_SECRET_ACCESS_KEY
6.4 ScheduledSparkApplication (Cron jobs)
Voir le code
%% writefile / tmp/ spark- k8s/ scheduled- spark.yaml
apiVersion: sparkoperator.k8s.io/ v1beta2
kind: ScheduledSparkApplication
metadata:
name: daily- etl
namespace: spark
spec:
schedule: "0 2 * * *" # Tous les jours à 2h du matin
concurrencyPolicy: Forbid # Ne pas lancer si le précédent tourne encore
successfulRunHistoryLimit: 5
failedRunHistoryLimit: 3
template:
type : Python
pythonVersion: "3"
mode: cluster
image: my- registry/ spark- app:1.0.0
mainApplicationFile: local:/// app/ daily_etl.py
sparkVersion: "3.5.0"
restartPolicy:
type : OnFailure
onFailureRetries: 2
driver:
cores: 1
memory: "2g"
serviceAccount: spark- sa
executor:
cores: 2
instances: 3
memory: "4g"
Voir le code
operator_commands = """
# Appliquer une SparkApplication
kubectl apply -f spark-application.yaml
# Voir le statut
kubectl get sparkapplication -n spark
kubectl describe sparkapplication etl-job -n spark
# Voir les logs du driver
kubectl logs -n spark -l spark-role=driver -f
# Supprimer
kubectl delete sparkapplication etl-job -n spark
"""
print (operator_commands)
Exercice 5 : Déployer avec SparkOperator
Objectif : Créer une SparkApplication qui calcule Pi avec 1000 itérations.
💡 Solution
apiVersion : sparkoperator.k8s.io/v1beta2
kind : SparkApplication
metadata :
name : spark-pi
namespace : spark
spec :
type : Python
pythonVersion : "3"
mode : cluster
image : apache/spark:3.5.0
mainApplicationFile : local:///opt/spark/examples/src/main/python/pi.py
arguments : [ "1000" ]
sparkVersion : "3.5.0"
restartPolicy :
type : Never
driver :
cores : 1
memory : "1g"
serviceAccount : spark-sa
executor :
cores : 1
instances : 2
memory : "1g"
7. Autoscaling & Resource Management
7.1 Dynamic Allocation (Spark natif)
Spark peut ajuster dynamiquement le nombre d’executors.
Voir le code
dynamic_allocation_config = """
# Dans sparkConf ou spark-submit
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=1
spark.dynamicAllocation.maxExecutors=10
spark.dynamicAllocation.initialExecutors=2
spark.dynamicAllocation.executorIdleTimeout=60s
spark.dynamicAllocation.schedulerBacklogTimeout=1s
# Shuffle tracking (requis pour K8s)
spark.dynamicAllocation.shuffleTracking.enabled=true
spark.dynamicAllocation.shuffleTracking.timeout=600s
"""
print (dynamic_allocation_config)
7.2 Options d’autoscaling K8s
Dynamic Allocation
Spark gère les executors
✅ Recommandé pour batch
HPA
Scale sur CPU/memory
⚠️ Pas idéal pour Spark
VPA
Ajuste les ressources
💡 Pour dimensionnement initial
KEDA
Scale sur événements externes
✅ Idéal pour streaming (Kafka lag)
Cluster Autoscaler
Ajoute/retire des nodes
✅ Combiné avec Dynamic Allocation
Voir le code
%% writefile / tmp/ spark- k8s/ keda- scaledobject.yaml
# KEDA ScaledObject pour Spark Streaming basé sur Kafka lag
apiVersion: keda.sh/ v1alpha1
kind: ScaledObject
metadata:
name: spark- streaming- scaler
namespace: spark
spec:
scaleTargetRef:
name: spark- streaming # Deployment à scaler
minReplicaCount: 1
maxReplicaCount: 10
triggers:
- type : kafka
metadata:
bootstrapServers: kafka.kafka.svc.cluster.local:9092
consumerGroup: spark- streaming- group
topic: events
lagThreshold: "100" # Scale si lag > 100
7.3 Node Affinity & Tolerations
Voir le code
%% writefile / tmp/ spark- k8s/ spark- with - affinity.yaml
apiVersion: sparkoperator.k8s.io/ v1beta2
kind: SparkApplication
metadata:
name: gpu- job
namespace: spark
spec:
type : Python
mode: cluster
image: my- registry/ spark- gpu:1.0.0
mainApplicationFile: local:/// app/ gpu_job.py
sparkVersion: "3.5.0"
driver:
cores: 1
memory: "2g"
serviceAccount: spark- sa
executor:
cores: 4
instances: 2
memory: "8g"
# Placer les executors sur des nodes avec GPU
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node- type
operator: In
values:
- gpu
# Tolérer les taints GPU
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
8. Monitoring & Observability
8.1 Spark UI
Voir le code
%% writefile / tmp/ spark- k8s/ spark- ui- ingress.yaml
# Service pour Spark UI
apiVersion: v1
kind: Service
metadata:
name: spark- ui
namespace: spark
spec:
selector:
spark- role: driver
ports:
- port: 4040
targetPort: 4040
type : ClusterIP
---
# Ingress pour accès externe
apiVersion: networking.k8s.io/ v1
kind: Ingress
metadata:
name: spark- ui- ingress
namespace: spark
spec:
rules:
- host: spark- ui.mycompany.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spark- ui
port:
number: 4040
8.2 Spark History Server
Voir le code
%% writefile / tmp/ spark- k8s/ history- server.yaml
apiVersion: apps/ v1
kind: Deployment
metadata:
name: spark- history- server
namespace: spark
spec:
replicas: 1
selector:
matchLabels:
app: spark- history- server
template:
metadata:
labels:
app: spark- history- server
spec:
containers:
- name: history- server
image: bitnami/ spark:3.5
command:
- / opt/ bitnami/ spark/ sbin/ start- history- server.sh
env:
- name: SPARK_HISTORY_OPTS
value: "-Dspark.history.fs.logDirectory=s3a://spark-logs/history"
ports:
- containerPort: 18080
envFrom:
- secretRef:
name: minio- credentials
---
apiVersion: v1
kind: Service
metadata:
name: spark- history- server
namespace: spark
spec:
selector:
app: spark- history- server
ports:
- port: 18080
targetPort: 18080
type : ClusterIP
Exercice 7 : Exposer Spark UI
Objectif : Accéder au Spark UI d’un job en cours.
# 1. Lister les pods driver
kubectl get pods -n spark -l spark-role=driver
# 2. Port-forward
kubectl port-forward -n spark < driver-pod> 4040:4040
# 3. Ouvrir http://localhost:4040
9. Debugging & Troubleshooting
9.1 Erreurs courantes et solutions
ImagePullBackOff
Image introuvable
Vérifier registry, credentials, tag
OOMKilled
Mémoire insuffisante
Augmenter memory, réduire données
Forbidden
RBAC manquant
Vérifier Role/RoleBinding
Pending (pods)
Ressources insuffisantes
Vérifier quotas, cluster capacity
Connection refused
Driver inaccessible
Vérifier network policies, services
ClassNotFoundException
JAR manquant
Ajouter dans l’image Docker
Voir le code
debug_commands = """
# === Debugging Spark on K8s ===
# 1. Voir les pods et leur statut
kubectl get pods -n spark -o wide
# 2. Détails d'un pod (événements)
kubectl describe pod <pod-name> -n spark
# 3. Logs du driver
kubectl logs -n spark -l spark-role=driver
# 4. Logs d'un executor spécifique
kubectl logs -n spark <executor-pod-name>
# 5. Logs en temps réel (follow)
kubectl logs -n spark -l spark-role=driver -f
# 6. Shell dans un pod (debug)
kubectl exec -it <pod-name> -n spark -- /bin/bash
# 7. Vérifier les ressources du namespace
kubectl describe resourcequota -n spark
# 8. Vérifier les events
kubectl get events -n spark --sort-by='.lastTimestamp'
# 9. SparkApplication status
kubectl get sparkapplication -n spark
kubectl describe sparkapplication <name> -n spark
"""
print (debug_commands)
9.2 Debugging OOMKilled
🔍 Symptôme : Pod executor en état OOMKilled
Causes possibles :
├── spark.executor.memory trop bas
├── spark.executor.memoryOverhead mal configuré
├── Trop de données par partition
├── Broadcast variables trop grandes
└── Fuite mémoire dans le code
Solutions :
├── Augmenter spark.executor.memory
├── spark.executor.memoryOverhead = max(0.1 × memory, 384m)
├── Repartitionner : df.repartition(200)
├── Réduire spark.sql.shuffle.partitions
└── Utiliser spark.memory.fraction = 0.6 (default)
9.3 Debugging Shuffle failures
Le shuffle est plus coûteux sur K8s (pas de data locality).
Voir le code
shuffle_optimizations = """
# Optimisations shuffle pour K8s
# Augmenter les timeouts
spark.network.timeout=600s
spark.shuffle.io.maxRetries=10
spark.shuffle.io.retryWait=30s
# Compression
spark.shuffle.compress=true
spark.shuffle.spill.compress=true
# Buffer sizes
spark.shuffle.file.buffer=64k
spark.reducer.maxSizeInFlight=96m
# Shuffle tracking (pour Dynamic Allocation)
spark.dynamicAllocation.shuffleTracking.enabled=true
"""
print (shuffle_optimizations)
Exercice 8 : Diagnostiquer un job qui échoue
Scénario : Un job Spark échoue avec le message “Pod OOMKilled”.
Questions :
Quelle commande pour voir les events du pod ?
Quelles configs Spark vérifier ?
💡 Solution
Commandes :
kubectl describe pod < pod-name> -n spark
kubectl get events -n spark --field-selector involvedObject.name=< pod-name>
Configs à vérifier :
spark.executor.memory
spark.executor.memoryOverhead
spark.kubernetes.executor.limit.memory
10. Optimisations Spark on K8s
10.1 Data Locality
⚠️ Problème : Pas de data locality sur K8s
Sur YARN avec HDFS :
┌──────────────────────┐
│ Node 1 │
│ ┌────────┐ ┌───────┐ │
│ │Executor│◀│ Data │ │ ← Data local (fast)
│ └────────┘ └───────┘ │
└──────────────────────┘
Sur K8s avec Object Storage :
┌──────────────────────┐ ┌─────────────┐
│ Node 1 │ │ S3/MinIO │
│ ┌────────┐ │◀─────│ (remote) │ ← Network transfer
│ │Executor│ │ │ │
│ └────────┘ │ └─────────────┘
└──────────────────────┘
Solutions :
Utiliser des formats colonnaires (Parquet) optimisés
Configurer S3A/GCS pour le parallélisme
External Shuffle Service (Spark 3.4+)
Voir le code
s3a_config = """
# Optimisations S3A pour Spark on K8s
# Parallélisme
spark.hadoop.fs.s3a.connection.maximum=200
spark.hadoop.fs.s3a.threads.max=64
spark.hadoop.fs.s3a.threads.core=16
# Fast upload
spark.hadoop.fs.s3a.fast.upload=true
spark.hadoop.fs.s3a.fast.upload.buffer=bytebuffer
spark.hadoop.fs.s3a.multipart.size=104857600 # 100MB
# Committer (éviter les renames S3)
spark.sql.sources.commitProtocolClass=org.apache.spark.internal.io.cloud.PathOutputCommitProtocol
spark.sql.parquet.output.committer.class=org.apache.spark.internal.io.cloud.BindingParquetOutputCommitter
"""
print (s3a_config)
10.2 Pod Templates
Pour des configurations avancées des pods Spark.
Voir le code
%% writefile / tmp/ spark- k8s/ executor- pod- template.yaml
apiVersion: v1
kind: Pod
spec:
containers:
- name: spark- executor
volumeMounts:
- name: spark- local- dir
mountPath: / tmp/ spark
resources:
requests:
ephemeral- storage: "10Gi"
limits:
ephemeral- storage: "20Gi"
volumes:
- name: spark- local- dir
emptyDir:
medium: Memory # Utiliser RAM pour shuffle local
sizeLimit: "4Gi"
Voir le code
pod_template_usage = """
# Utiliser le pod template dans spark-submit
spark-submit \
--conf spark.kubernetes.executor.podTemplateFile=/path/to/executor-pod-template.yaml \
...
# Ou dans SparkApplication
spec:
executor:
podTemplateFile: /path/to/executor-pod-template.yaml
"""
print (pod_template_usage)
10.3 Spot/Preemptible Instances
Économiser jusqu’à 90% sur les coûts compute.
Voir le code
%% writefile / tmp/ spark- k8s/ spark- with - spot.yaml
apiVersion: sparkoperator.k8s.io/ v1beta2
kind: SparkApplication
metadata:
name: spot- etl
namespace: spark
spec:
type : Python
mode: cluster
image: my- registry/ spark- app:1.0.0
mainApplicationFile: local:/// app/ etl.py
sparkVersion: "3.5.0"
# Driver sur nodes stables (on-demand)
driver:
cores: 1
memory: "2g"
serviceAccount: spark- sa
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node- type
operator: In
values:
- on- demand
# Executors sur spot instances
executor:
cores: 2
instances: 5
memory: "4g"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node- type
operator: In
values:
- spot
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
11. Mini-Projet : ETL Pipeline sur Kubernetes
Objectif
Déployer un pipeline Spark complet sur K8s avec MinIO (Object Storage local).
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌─────────────┐ ┌──────────────────┐ ┌─────────────┐ │
│ │ MinIO │ │ Spark │ │ MinIO │ │
│ │ (source) │────▶│ on K8s │────▶│ (output) │ │
│ │ │ │ │ │ │ │
│ │ bucket: │ │ ┌────────────┐ │ │ bucket: │ │
│ │ bronze/ │ │ │ Driver │ │ │ silver/ │ │
│ │ data.csv │ │ └────────────┘ │ │ data.parquet│ │
│ │ │ │ ┌────┐ ┌────┐ │ │ │ │
│ │ │ │ │Exec│ │Exec│ │ │ │ │
│ │ │ │ └────┘ └────┘ │ │ │ │
│ └─────────────┘ └──────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Structure du projet
spark-k8s-project/
├── docker/
│ ├── Dockerfile
│ └── requirements.txt
├── app/
│ └── etl_job.py
├── manifests/
│ ├── namespace.yaml
│ ├── rbac.yaml
│ ├── minio-secret.yaml
│ └── spark-application.yaml
├── data/
│ └── sample.csv
└── README.md
Étape 1 : Déployer MinIO sur K8s
Voir le code
minio_deploy = """
# Déployer MinIO avec Helm
helm repo add minio https://charts.min.io/
helm repo update
helm install minio minio/minio \
--namespace minio \
--create-namespace \
--set rootUser=minioadmin \
--set rootPassword=minioadmin \
--set mode=standalone \
--set resources.requests.memory=512Mi \
--set persistence.size=10Gi
# Port-forward pour accéder à la console
kubectl port-forward -n minio svc/minio-console 9001:9001
# Créer les buckets
mc alias set myminio http://localhost:9000 minioadmin minioadmin
mc mb myminio/bronze
mc mb myminio/silver
# Uploader des données de test
mc cp data/sample.csv myminio/bronze/
"""
print (minio_deploy)
Étape 2 : Code de l’application ETL
Voir le code
%% writefile / tmp/ spark- k8s- project/ app/ etl_job.py
"""
ETL Job : Bronze → Silver
Lit des CSV depuis MinIO, transforme, et écrit en Parquet.
"""
import argparse
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, upper, current_timestamp
def create_spark_session():
"""Créer une SparkSession configurée pour MinIO."""
return SparkSession.builder \
.appName("ETL Bronze to Silver" ) \
.config("spark.hadoop.fs.s3a.impl" , "org.apache.hadoop.fs.s3a.S3AFileSystem" ) \
.config("spark.hadoop.fs.s3a.path.style.access" , "true" ) \
.getOrCreate()
def extract(spark, input_path):
"""Lire les données brutes depuis le bucket Bronze."""
print (f"📥 Reading from { input_path} " )
df = spark.read \
.option("header" , "true" ) \
.option("inferSchema" , "true" ) \
.csv(input_path)
print (f" Loaded { df. count()} rows" )
return df
def transform(df):
"""Appliquer les transformations métier."""
print ("🔄 Transforming data" )
transformed = df \
.dropDuplicates() \
.dropna() \
.withColumn("processed_at" , current_timestamp())
# Normalisation des colonnes string
for col_name, dtype in transformed.dtypes:
if dtype == "string" :
transformed = transformed.withColumn(col_name, upper(col(col_name)))
print (f" Transformed to { transformed. count()} rows" )
return transformed
def load(df, output_path):
"""Écrire les données transformées dans le bucket Silver."""
print (f"📤 Writing to { output_path} " )
df.write \
.mode("overwrite" ) \
.parquet(output_path)
print (" Done!" )
def main():
parser = argparse.ArgumentParser(description= "ETL Job" )
parser.add_argument("--input" , required= True , help = "Input path (s3a://...)" )
parser.add_argument("--output" , required= True , help = "Output path (s3a://...)" )
args = parser.parse_args()
spark = create_spark_session()
try :
# ETL Pipeline
df = extract(spark, args.input )
transformed = transform(df)
load(transformed, args.output)
print ("✅ ETL completed successfully!" )
except Exception as e:
print (f"❌ ETL failed: { e} " )
raise
finally :
spark.stop()
if __name__ == "__main__" :
main()
Étape 3 : Dockerfile
Voir le code
%% writefile / tmp/ spark- k8s- project/ docker/ Dockerfile
FROM bitnami/ spark:3.5
USER root
# Dépendances Python
RUN pip install -- no- cache- dir pyarrow pandas
# JARs pour S3/MinIO
RUN curl - sL https:// repo1.maven.org/ maven2/ org/ apache/ hadoop/ hadoop- aws/ 3.3.4 / hadoop- aws- 3.3.4 .jar \
- o / opt/ bitnami/ spark/ jars/ hadoop- aws- 3.3.4 .jar && \
curl - sL https:// repo1.maven.org/ maven2/ com/ amazonaws/ aws- java- sdk- bundle/ 1.12.262 / aws- java- sdk- bundle- 1.12.262 .jar \
- o / opt/ bitnami/ spark/ jars/ aws- java- sdk- bundle- 1.12.262 .jar
# Application
COPY app/ / app/
RUN chown - R 1001 :1001 / app
USER 1001
WORKDIR / app
Étape 4 : Manifests Kubernetes
Voir le code
%% writefile / tmp/ spark- k8s- project/ manifests/ spark- application.yaml
apiVersion: sparkoperator.k8s.io/ v1beta2
kind: SparkApplication
metadata:
name: etl- bronze- silver
namespace: spark
spec:
type : Python
pythonVersion: "3"
mode: cluster
image: spark- etl:1.0.0 # Image locale (Minikube)
imagePullPolicy: Never # Pour Minikube
mainApplicationFile: local:/// app/ etl_job.py
arguments:
- "--input"
- "s3a://bronze/sample.csv"
- "--output"
- "s3a://silver/data"
sparkVersion: "3.5.0"
sparkConf:
"spark.hadoop.fs.s3a.endpoint" : "http://minio.minio.svc.cluster.local:9000"
"spark.hadoop.fs.s3a.path.style.access" : "true"
"spark.hadoop.fs.s3a.impl" : "org.apache.hadoop.fs.s3a.S3AFileSystem"
restartPolicy:
type : OnFailure
onFailureRetries: 2
driver:
cores: 1
memory: "1g"
serviceAccount: spark- sa
envSecretKeyRefs:
AWS_ACCESS_KEY_ID:
name: minio- credentials
key: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
name: minio- credentials
key: AWS_SECRET_ACCESS_KEY
executor:
cores: 1
instances: 2
memory: "1g"
envSecretKeyRefs:
AWS_ACCESS_KEY_ID:
name: minio- credentials
key: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
name: minio- credentials
key: AWS_SECRET_ACCESS_KEY
Étape 5 : Déploiement complet
Voir le code
deployment_script = """
#!/bin/bash
set -e
echo "🚀 Déploiement du pipeline ETL Spark on K8s"
# 1. Créer le namespace et RBAC
echo "📁 Création namespace et RBAC..."
kubectl apply -f manifests/namespace.yaml
kubectl apply -f manifests/rbac.yaml
kubectl apply -f manifests/minio-secret.yaml
# 2. Build de l'image (Minikube)
echo "🐳 Build de l'image Docker..."
eval $(minikube docker-env)
docker build -t spark-etl:1.0.0 -f docker/Dockerfile .
# 3. Vérifier que MinIO est prêt
echo "⏳ Attente de MinIO..."
kubectl wait --for=condition=ready pod -l app=minio -n minio --timeout=120s
# 4. Lancer le job Spark
echo "🔥 Lancement du job Spark..."
kubectl apply -f manifests/spark-application.yaml
# 5. Suivre le job
echo "📊 Suivi du job..."
kubectl get sparkapplication -n spark -w
"""
print (deployment_script)
Étape 6 : Vérification
Voir le code
verify_commands = """
# Vérifier le statut du job
kubectl get sparkapplication -n spark
# Voir les logs du driver
kubectl logs -n spark -l spark-role=driver
# Vérifier les outputs dans MinIO
mc ls myminio/silver/data/
# Lire un sample des données
mc cat myminio/silver/data/part-00000.parquet | head
"""
print (verify_commands)
🧪 Quiz de fin de module
❓ Q1. Quelle est la différence entre Client mode et Cluster mode ?
Client mode est plus rapide
En Cluster mode, le Driver tourne dans un Pod K8s
Client mode ne supporte pas les executors
Cluster mode nécessite YARN
💡 Voir la réponse
✅ Réponse : b — En Cluster mode, le Driver est un Pod K8s. En Client mode, il reste sur la machine locale.
❓ Q2. Pourquoi utiliser Spark Operator plutôt que spark-submit directement ?
Spark Operator est plus rapide
spark-submit ne fonctionne pas sur K8s
Spark Operator permet des manifestes YAML déclaratifs et des retries automatiques
Spark Operator ne nécessite pas de ServiceAccount
💡 Voir la réponse
✅ Réponse : c — Spark Operator offre une approche déclarative (YAML), des retry policies, du scheduling, et une meilleure intégration CI/CD.
❓ Q3. Quel problème résout Dynamic Allocation ?
La sécurité des pods
L’ajustement automatique du nombre d’executors
Le stockage des logs
La communication réseau
💡 Voir la réponse
✅ Réponse : b — Dynamic Allocation permet à Spark d’ajuster automatiquement le nombre d’executors selon la charge.
❓ Q4. Quelle config est requise pour Dynamic Allocation sur K8s ?
spark.dynamicAllocation.enabled=true uniquement
spark.dynamicAllocation.shuffleTracking.enabled=true
spark.kubernetes.allocation.batch.size=5
Aucune config spéciale
💡 Voir la réponse
✅ Réponse : b — Sur K8s (sans External Shuffle Service), shuffle tracking est requis pour que Dynamic Allocation fonctionne.
❓ Q5. Que signifie l’erreur OOMKilled ?
Le pod n’a pas d’image
Le pod a dépassé sa limite mémoire
Le réseau est indisponible
Le ServiceAccount est invalide
💡 Voir la réponse
✅ Réponse : b — OOMKilled signifie que le container a dépassé sa limite mémoire et a été tué par K8s.
❓ Q6. Quel est l’avantage principal de K8s sur YARN pour Spark ?
Meilleure data locality
Infrastructure cloud-native et multi-cloud
Plus rapide
Moins de configuration
💡 Voir la réponse
✅ Réponse : b — K8s offre une infrastructure cloud-native, standardisée, multi-cloud, avec autoscaling avancé.
❓ Q8. Pourquoi le shuffle est-il plus coûteux sur K8s que sur YARN/HDFS ?
K8s est plus lent
Pas de data locality — les données doivent transiter par le réseau
Spark n’est pas optimisé pour K8s
Les executors ont moins de mémoire
💡 Voir la réponse
✅ Réponse : b — Sur YARN/HDFS, les données peuvent être locales. Sur K8s avec Object Storage, tout passe par le réseau.
❓ Q9. Quel est le rôle du ServiceAccount dans Spark on K8s ?
Stocker les credentials
Permettre au Driver de créer des Executor Pods
Gérer le Spark UI
Configurer le réseau
💡 Voir la réponse
✅ Réponse : b — Le ServiceAccount donne au Driver les permissions RBAC pour créer, lister et supprimer des Pods (executors).
📚 Ressources pour aller plus loin
🌐 Documentation officielle
➡️ Prochaine étape
Maintenant que tu sais déployer Spark sur Kubernetes, passons au Cloud et Object Storage !
👉 Module suivant : 22_cloud_object_storage — Cloud & Object Storage
Tu vas apprendre :
Cloud Computing : IaaS, PaaS, SaaS
AWS, GCP, Azure : Services Data Engineering
Object Storage : S3, GCS, Azure Blob
MinIO : Pratiquer localement
📝 Récapitulatif de ce module
Architecture
Driver/Executor Pods, Client vs Cluster mode
Docker Image
Build, JARs, best practices
K8s Config
RBAC, Secrets, PVC
spark-submit
Configurations essentielles
Spark Operator
SparkApplication, ScheduledSparkApplication
Autoscaling
Dynamic Allocation, KEDA
Monitoring
Spark UI, Prometheus, Grafana
Debugging
Erreurs courantes, commandes kubectl
Optimisations
S3A config, Pod templates, Spot instances
🎉 Félicitations ! Tu as terminé le module Spark on Kubernetes.
Voir le code
# Commandes de nettoyage
cleanup_commands = """
# Supprimer les ressources Spark
kubectl delete sparkapplication --all -n spark
kubectl delete namespace spark
# Supprimer Spark Operator
helm uninstall spark-operator -n spark-operator
kubectl delete namespace spark-operator
# Supprimer MinIO
helm uninstall minio -n minio
kubectl delete namespace minio
# Arrêter Minikube (optionnel)
minikube stop
"""
print (cleanup_commands)
Retour au sommet