Introduction
Des jobs Spark mal réglés gaspillent du calcul, bloquent des pipelines et cachent un réel potentiel de performance. Ce guide explique les erreurs de configuration Apache Spark les plus fréquentes, comment valider vos changements en sécurité, et comment revenir rapidement en arrière quand le résultat n'est pas au rendez‑vous. Vous verrez des exemples concrets de spark-submit, les vérifications utiles dans l'UI Spark, et un plan pilote local à exécuter avant toute modification en production. Nous pointons aussi les interactions avec Kafka, HDFS, Apache Airflow et NiFi qui amplifient souvent les erreurs de paramétrage.
Ce que vous allez apprendre:
- Un flux de travail sûr pour modifier la configuration sans surprises
- Les principales erreurs et leurs correctifs
- Comment piloter localement et faire un rollback propre
- Un chemin de troubleshooting répétable
Boucle de travail (Workflow Overview)
Utilisez cette boucle pour tout changement de configuration:
- Baseline
- Capturez la version du job, un dump de configuration, la taille d'entrée, le temps d'exécution et les symptômes d'échec.
- Sauvegardez des captures de l'UI Spark ou des event logs d'un run typique.
- Hypothèse
- Choisissez un changement unique ciblant un seul goulot mesurable (ex. skew, taille de shuffle, temps de GC).
- Pilote local
- Testez sur un échantillon représentatif et une fenêtre/seed fixe pour rendre les résultats reproductibles.
- Staging
- Rejouez le dernier run réussi avec la nouvelle conf.
- Comparez le runtime, le skew de stages et la justesse de sortie (comptes de lignes et agrégats clés).
- Déploiement en production
- Activez sur un sous‑ensemble de jobs ou de partitions.
- Définissez des critères d'abandon (ex. runtime +30 %, taux d'erreur au‑dessus du baseline, OOMs).
- Monitoring
- Surveillez dans l'UI: scheduler delay, échecs de tâches, shuffle spill, GC, et débits IO.
- Rollback
- Revenez en arrière en un seul mouvement si un garde‑fou est franchi.
- Analysez les logs driver/executors et la Spark History Server pour affiner l'hypothèse suivante.
Erreurs courantes et correctifs pratiques
Voici des pièges fréquents en batch et streaming, avec correctifs et validations. Ils couvrent les « Apache Spark config mistakes » les plus vus en production.
- Déséquilibre exécuteurs/cores
- Symptôme: faible utilisation CPU ou longues pauses GC.
- Correctif: dimensionnez ensemble cores et mémoire.
- Exemple:
spark-submit \
--conf spark.executor.instances=12 \
--conf spark.executor.cores=4 \
--conf spark.executor.memory=8g \
--conf spark.driver.memory=4g
- Validation: dans l'onglet Executors, le temps de tâche domine le scheduler delay; la GC occupe une faible fraction du temps de tâche.
- Overhead mémoire trop bas
- Symptôme: conteneur tué par OOM pendant le shuffle ou OOM côté worker Python.
- Correctif: augmentez l'overhead, surtout pour les shuffles larges ou les UDFs.
- Exemple:
spark-submit \
--conf spark.executor.memory=6g \
--conf spark.executor.memoryOverhead=1536
- Validation: plus d'OOM dans les logs; le spill reste dans les limites mémoire du conteneur.
- Partitions de shuffle mal réglées
- Symptôme: trop de micro‑tâches (overhead) ou trop peu de tâches massives (OOM, skew).
- Correctif: ajustez spark.sql.shuffle.partitions pour les workloads SQL; commencez près de data_size_in_GB * 3 à 5 et itérez.
- Exemple:
spark-submit --conf spark.sql.shuffle.partitions=400
- Validation: dans l'onglet SQL, les durées des tâches sont proches; peu ou pas d'alertes de skew.
- Allocation dynamique mal configurée
- Symptôme: des exécuteurs inactifs persistent ou le job s'asphyxie sous charge.
- Correctif: bornez la plage et définissez une valeur initiale raisonnable.
- Exemple:
spark-submit \
--conf spark.dynamicAllocation.enabled=true \
--conf spark.dynamicAllocation.initialExecutors=8 \
--conf spark.dynamicAllocation.minExecutors=4 \
--conf spark.dynamicAllocation.maxExecutors=64
- Validation: le nombre d'exécuteurs suit la taille d'entrée; le backlog du scheduler reste faible.
- Broadcast join surdimensionné
- Symptôme: OOM exécuteur lors du join ou pression mémoire sur le driver.
- Correctif: plafonnez la taille du broadcast ou désactivez‑le quand il n'aide pas.
- Exemple:
spark-submit --conf spark.sql.autoBroadcastJoinThreshold=64m
- Validation: le plan physique montre la stratégie de jointure attendue; la mémoire est stable.
- Surcharge de sérialisation dans les pipelines RDD
- Symptôme: CPU élevé en sérialisation et empreinte shuffle volumineuse.
- Correctif: préférez Kryo pour les classes custom quand c'est applicable.
- Exemple:
spark-submit --conf spark.serializer=org.apache.spark.serializer.KryoSerializer
- Validation: tailles de shuffle write/read en baisse; tâches plus courtes.
- Exécution spéculative sur jobs à écriture intensive
- Symptôme: effets de bord dupliqués ou conflits côté sink.
- Correctif: désactivez ou restreignez la spéculation pour ces jobs.
- Exemple:
spark-submit --conf spark.speculation=false
- Validation: plus d'écritures dupliquées; runtime inchangé sur cluster stable.
- Explosion de petits fichiers sur HDFS ou stockage cloud
- Symptôme: beaucoup de fichiers minuscules dégradent les scans et listings downstream.
- Correctif: augmentez la taille de partition ou compactez avant l'écriture.
- Exemples:
# Augmenter la taille de partition pour le scan de fichiers
spark.conf.set("spark.sql.files.maxPartitionBytes", "256m")
# Compacter avant l'écriture
finalDF.coalesce(200).write.mode("overwrite").parquet(path)
- Validation: moins de fichiers, plus gros; requêtes downstream plus rapides.
- Pression en streaming Kafka
- Symptôme: latence de bout en bout en hausse ou backpressure.
- Correctif: limitez l'entrée par trigger et dimensionnez les partitions de shuffle.
- Exemple (Structured Streaming, Scala):
val df = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", servers)
.option("subscribe", topics)
.option("maxOffsetsPerTrigger", 500000)
.load()
spark.conf.set("spark.sql.shuffle.partitions", 200)
- Validation: durée des micro‑batches stable; le consumer lag reste borné.
- Sursouscription par l'orchestrateur (Airflow/NiFi)
- Symptôme: surcharge cluster quand plusieurs tâches d'un DAG démarrent simultanément.
- Correctif: appliquez des limites de concurrence côté orchestrateur; ajustez les exécuteurs aux quotas du cluster.
- Validation: temps en file d'attente acceptables; pas de préemptions ou stragglers généralisés.
Checklist de validation
- Confirmez la configuration effective à l'exécution via l'onglet Environment de l'UI Spark ou sc.getConf.getAll.
- Figez un snapshot de métriques avant/après (runtime, skew, spill, GC, IO).
- Protégez la justesse: comparez le nombre d'enregistrements et les agrégats clés.
- Gardez un seul changement par test pour isoler les effets. C'est au cœur de l'« Apache Spark validation » réussie.
Plan pilote local
Objectif: prouver une amélioration sur un job dans un run contrôlé, inspectable localement avant déploiement.
Scope
- Choisissez un seul job batch ou une fenêtre de streaming.
- Limitez à une seule source de données (ex. un topic Kafka ou un répertoire HDFS).
- Améliorez une seule métrique (ex. temps total, pic mémoire exécuteur, ou skew).
Garde‑fous
- Fixez la taille d'entrée et la fenêtre temporelle.
- Limitez à 2-4 runs.
- Définissez des seuils de succès/échec et un déclencheur de rollback.
Étapes de pilote (exemple)
- Extrayez un échantillon représentatif de 5-10 Go (ou N minutes de données Kafka) vers un bucket de dev.
- Appliquez un changement, par exemple spark.sql.shuffle.partitions=400.
- Lancez spark-submit en local ou sur un cluster de dev avec le même code et la nouvelle conf.
- Collectez le runtime total, les métriques de skew de stage, les tailles de spill et les contrôles de justesse de sortie.
- Décision: si le runtime s'améliore d'au moins 20 % sans régression de justesse, promouvez en staging; sinon faites un Apache Spark rollback et testez l'hypothèse suivante.
Conseils opérationnels
- Stockez les configs des jobs à côté du code avec un versioning clair.
- Imprimez sc.getConf.toDebugString au démarrage du job.
- Étiquetez les logs avec un label de version de conf pour faciliter les diffs.
Workflow de troubleshooting
Suivez ce chemin reproductible pour isoler les problèmes de configuration (Apache Spark troubleshooting):
- Identifiez la signature d'échec
- OOM, long scheduler delay, stages déséquilibrés, GC élevé, ou filesystem lent.
- Reproduisez sur un petit échantillon
- Gardez le dataset constant; désactivez les fonctionnalités adaptatives non pertinentes le temps du debug.
- Vérifiez la configuration effective
- Déversez sc.getConf.getAll et comparez aux réglages souhaités.
- Inspectez l'UI Spark et les logs
- Onglets Job et SQL: stages longs, signaux de skew, tailles de shuffle read/write.
- Onglet Executors: temps de GC, mémoire, échecs de tâches.
- Logs driver/executors: OOM de conteneur, erreurs de sérialisation, timeouts réseau.
- Appliquez le plus petit changement viable
- Ajustez un seul paramètre, relancez et mesurez.
- Décidez et documentez
- Si les métriques s'améliorent sans perte de justesse, conservez le changement.
- Sinon, rollback et testez l'hypothèse suivante.
Conclusion
Le moyen le plus rapide d'améliorer la fiabilité de Spark est d'éviter quelques « Apache Spark config mistakes » et de changer les réglages avec une boucle disciplinée: baseline, pilote à changement unique, replay en staging, déploiement gardé, et rollback instantané. Bâtissez des habitudes solides: un catalogue de conf versionné par job, le logging systématique de la conf effective, un petit jeu de métriques suivies (runtime, skew, GC, IO), et un rollback en une commande pour chaque changement. En cas de doute, testez d'abord localement, mesurez, et privilégiez des pas petits et réversibles. C'est l'essence d'une bonne Apache Spark configuration, d'une Apache Spark validation fiable, et d'un Apache Spark troubleshooting efficace.