Introduction
Exécuter Apache Spark en production devient simple lorsque vous standardisez les étapes qui comptent : bases de configuration, surveillance, fiabilité, stockage, sécurité, ajustement, maintenance et mises à niveau. Ce guide fournit aux développeurs, consultants DevOps et équipes de démarrage une checklist concise et pratique avec des exemples applicables dès aujourd'hui.
Utilisez-le pour réduire les incidents, raccourcir les temps de récupération et maîtriser les coûts tout en respectant les SLA pour les traitements par lots et en flux continu sur Kafka, HDFS, le stockage objet et les ordonnanceurs comme Airflow ou NiFi.
Vue d'ensemble du flux de travail
Suivez ce flux de bout en bout pour des opérations Spark fiables :
- Planifier
- Définir les SLO : latence des jobs, retard des données, seuils de qualité des données et objectifs de coût.
- Cartographier les dépendances : sources de données (Kafka, HDFS, S3), destinations (Parquet, Delta, JDBC), catalogues et ordonnanceurs.
- Configurer
- Définir les valeurs par défaut du cluster et de Spark une seule fois, puis ne les surcharger que par exception.
- Établir des conventions de nommage, de journalisation et de répertoires de points de contrôle (checkpoints).
- Déployer et valider
- Exécuter un essai à blanc sur des données d'échantillon, puis un déploiement fantôme (shadow) en production avec validation en lecture seule.
- Conditionner la promotion aux métriques de succès et au budget d'erreur.
- Surveiller et alerter
- Instrumenter les métriques, les journaux d'événements et les logs. Connecter les alertes aux indicateurs avancés.
- Maintenir et sauvegarder
- Faire la rotation des logs, compacter les données, nettoyer les checkpoints et sauvegarder les catalogues.
- Mettre à niveau en toute sécurité
- Tester sur la préproduction avec des données réalistes, conserver un plan de retour arrière et un diff de configuration.
Bases de configuration
Verrouillez un profil par défaut sûr avant de monter en charge ou d'intégrer des équipes.
Fichier spark-defaults.conf de base (à ajuster selon la taille de votre charge de travail) :
# Exécution principale
spark.serializer=org.apache.spark.serializer.KryoSerializer
spark.sql.shuffle.partitions=400 # selon la taille des données ; ex. 2000 pour ~1 To
spark.sql.adaptive.enabled=true # AQE (Adaptive Query Execution)
spark.sql.adaptive.coalescePartitions.enabled=true
spark.sql.adaptive.skewJoin.enabled=true
# Élasticité des ressources
spark.dynamicAllocation.enabled=true
spark.shuffle.service.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=100 # plafonné par budget et SLA
spark.dynamicAllocation.initialExecutors=4
# Mémoire et CPU (ajuster par charge de travail)
spark.executor.instances=10
spark.executor.cores=4
spark.executor.memory=8g
spark.driver.memory=4g
spark.memory.fraction=0.6
# Fiabilité et délais d'attente
spark.network.timeout=600s
spark.sql.broadcastTimeout=300
spark.task.maxFailures=4
spark.speculation=true # gérer les tâches lentes
# E/S et compression
spark.sql.parquet.compression.codec=zstd
spark.sql.parquet.filterPushdown=true
spark.sql.files.maxPartitionBytes=134217728 # 128 Mo
Astuces Hadoop et stockage objet (dans spark-defaults ou core-site.xml) :
# Exemples de réglage S3A (si stockage compatible S3)
spark.hadoop.fs.s3a.fast.upload=true
spark.hadoop.fs.s3a.connection.maximum=200
spark.hadoop.mapreduce.fileoutputcommitter.algorithm.version=2
Conventions qui préviennent les erreurs :
- Utiliser des répertoires de checkpoint et de staging distincts par application.
- Marquer les applications avec
spark.app.namequi encode le propriétaire, l'objectif et l'environnement. - Fixer les versions des connecteurs compatibles avec vos versions de Spark et Scala.
Surveillance et alertes
Une pile de télémétrie minimale et efficace comprend les métriques, les journaux d'événements et les logs.
Métriques
Activez le système de métriques Spark et exposez un collecteur (sink) que vous pouvez interroger (par exemple Prometheus JMX ou servlet). Suivez :
- Durée des jobs, étages échoués, réessais de tâches
- Nombre d'exécuteurs, exécuteurs perdus, temps GC, utilisation de la pile JVM
- Lecture/écriture shuffle, débordement disque, mémoire de stockage
- Streaming structuré : lignes d'entrée, lignes traitées, offsets, métriques du state store
Extrait de configuration de métriques exemple (metrics.properties) :
*.sink.jmx.class=org.apache.spark.metrics.sink.JmxSink
master.sources.jvm.class=org.apache.spark.metrics.source.JvmSource
worker.sources.jvm.class=org.apache.spark.metrics.source.JvmSource
driver.sources.jvm.class=org.apache.spark.metrics.source.JvmSource
executor.sources.jvm.class=org.apache.spark.metrics.source.JvmSource
Journaux d'événements et interface utilisateur
- Activer la journalisation d'événements vers un stockage durable pour les analyses post-mortem.
- Conserver une politique de rétention et indexer les journaux d'événements pour la recherche.
Journalisation
- Niveau INFO en production, DEBUG uniquement pour le dépannage ciblé.
- Masquer les secrets (jetons, clés) et les données personnelles dans les logs.
Idées d'alertes
- Lot (batch) : durée de job p95 dépasse le SLO ; taux de réessai d'étage > 5 % ; ratio de débordement shuffle en hausse.
- Streaming : retard d'entrée croissant depuis 10 minutes ; échecs d'écriture de checkpoint ; pics de temps de traitement.
- Infrastructure : exécuteurs perdus > seuil ; temps GC driver > 20 % ; disque plein sur volumes shuffle.
Fiabilité des jobs et ordonnancement
Rendre les jobs redémarrables et idempotents.
Motifs pour le traitement par lots
- Partitionnement d'entrée : définir des limites stables (par date, heure ou plages d'identifiants).
- Écritures idempotentes : utiliser l'écrasement par partition ou la fusion (merge).
- Réessais : autoriser les réessais de tâches Spark, mais les plafonner pour éviter les tempêtes de réessais.
Checklist pour le streaming structuré
- Toujours définir un
checkpointLocationdurable. - Privilégier les destinations exactly-once quand disponibles ; sinon concevoir pour at-least-once avec déduplication.
- Contrôler la charge avec les intervalles de déclenchement et les stratégies de backpressure.
Exemple : Kafka vers Parquet avec checkpointing (Scala)
val df = spark.readStream
.format("kafka
.option("kafka.bootstrap.servers", "broker:9092
.option("subscribe", "events
.option("startingOffsets", "latest
.load()
.selectExpr("CAST(value AS STRING) as json
val parsed = spark.read.json(df.select("json
.as[String])
parsed.writeStream
.format("parquet
.option("path",
.option("checkpointLocation",
.outputMode("append
.start()
Exemple d'intégration à l'ordonnancement (Airflow SparkSubmitOperator)
from airflow.providers.apache.spark.operators.spark_submit import SparkSubmitOperator
spark_job = SparkSubmitOperator(
task_id='spark_batch_job',
application='/path/to/job.jar',
conn_id='spark_default',
conf={
'spark.sql.shuffle.partitions': '400',
'spark.dynamicAllocation.enabled': 'true'
},
retries=3,
retry_delay=timedelta(minutes=5),
retry_exponential_backoff=True,
on_failure_callback=alert_on_failure,
sla=timedelta(hours=2)
)
- Définir les réessais avec backoff exponentiel.
- Émettre des métriques et envoyer des alertes en cas d'échec.
- Appliquer des SLA par tâche de DAG et les afficher sur les tableaux de bord.
Stockage des données et E/S
Formats et partitionnement
- Utiliser des formats colonnaires (Parquet) avec filtrage poussé (predicate pushdown) et projection.
- Partitionner par colonnes de faible cardinalité et haute sélectivité (date, heure, pays).
- Éviter le sur-partitionnement ; viser des tailles de fichiers de sortie de 128 Mo à 512 Mo.
Contrôle des petits fichiers
- Pour le lot : écrire moins de fichiers plus gros via
coalesceou la fusion AQE. - Job de compactage périodique (ex. quotidien) : lire les petits fichiers, réécrire à la taille cible de 256 Mo.
Shuffle et jointures
- Réduire le shuffle en filtrant et sélectionnant tôt.
- Diffuser (broadcast) les petites tables de dimension ; ajuster les seuils :
spark.sql.autoBroadcastJoinThreshold=104857600 # 100 Mo
Ajustement du débit
- Augmenter le parallélisme en ajustant les partitions et divisions d'entrée.
- Pour les stockages type S3, augmenter les connexions et la taille multipart avec prudence.
Sécurité et gouvernance
- Réseau et transport : activer TLS pour l'interface Spark, les RPC et le shuffle si votre plateforme le supporte.
- Authentification et autorisation : utiliser Kerberos ou les rôles IAM ; éviter les clés statiques sur disque.
- Secrets : monter via des fournisseurs sécurisés ; ne jamais les journaliser.
- Données : chiffrer au repos ; restreindre les chemins de buckets ou HDFS par rôle.
- Audit : collecter les logs d'accès pour les opérations sur les données et le metastore.
- Masquage dans les logs : nettoyer les jetons, emails et identifiants dans les journaux.
Coûts et ajustement des performances
Dimensionnement des exécuteurs
- Démarrer avec 4 cœurs et 6 à 8 Go par exécuteur ; ajuster selon le débordement (spill) et le GC.
- Maintenir assez de parallélisme pour saturer le cluster sans trashing.
Exécution de requête adaptative (AQE / Adaptive Query Execution)
spark.sql.adaptive.enabled=true
spark.sql.adaptive.skewJoin.enabled=true
- Permet à Spark de fusionner les partitions et d'atténuer la distorsion (skew) à l'exécution.
Nombre de partitions
- Règle empirique : viser 1 à 3 tâches par cœur CPU ; valider avec les chronologies d'étapes.
Atténuation de la distorsion (skew)
- Saler les clés distordues ou utiliser la jointure skew d'AQE ; vérifier via les cartes de chaleur des temps de tâches dans l'interface Spark.
Mise en cache
- Mettre en cache seulement quand réutilisé plusieurs fois et que la mémoire suffit. Privilégier le checkpoint pour les lignées longues en streaming.
Allocation dynamique
- Définir les min, max et délais d'inactivité selon les objectifs de SLA et de coût.
Instances spot ou préemptibles (si applicable)
- Utiliser avec allocation dynamique et tester la perte d'exécuteurs ; garantir la progression checkpointée.
Maintenance et sauvegardes
État et journaux
- Checkpoints : ne pas supprimer les checkpoints actifs ; nettoyer seulement après une réinitialisation complète.
- Journaux d'événements : rotation et archivage avec politiques de cycle de vie.
- Disques de shuffle : surveiller l'espace et l'usure ; alerter bien avant saturation.
Catalogue et métadonnées
- Sauvegarder le metastore Hive ou le catalogue cloud selon un calendrier.
- Versionner et sauvegarder
spark-defaults.conf, configs de logs et définitions de jobs.
Hygiène des données
- Compactage : exécuter régulièrement des jobs pour fusionner les petits fichiers.
- Vacuum : supprimer les anciens instantanés si le format de table le supporte.
Préparation aux sinistres
- Documenter les runbooks de récupération. Tester les restaurations chaque trimestre.
Mises à niveau et retours arrière
Checklist avant mise à niveau
- Vérifier la matrice de compatibilité : Spark, Scala, Java, connecteurs, bibliothèques Hadoop.
- Examiner les changements de comportement SQL et les valeurs par défaut de configuration qui peuvent affecter les plans.
- Recompiler les fonctions définies par l'utilisateur si les binaires ou ABIs ont changé.
Déploiement par étapes
- Exécuter des jobs canari avec des données et entrées de type production.
- Comparer les métriques à la référence : durée, débordement, shuffle, GC, coût.
Plan de retour arrière
- Conserver les images et configurations précédentes prêtes.
- Automatiser le retour arrière et la bascule des chemins de données quand possible.
Pièges courants en production
- Distorsion des données (data skew)
- Symptôme : quelques tâches s'exécutent beaucoup plus longtemps.
- Correctif : activer la jointure skew AQE, saler les clés ou pré-agréger.
- Inondation de petits fichiers (small files flood)
- Symptôme : trop de minuscules fichiers Parquet ; lectures lentes et coût de métadonnées élevé.
- Correctif : coalesce à l'écriture, activer la fusion AQE, ajouter des jobs de compactage.
- Checkpoints de streaming manquants
- Symptôme : doublons ou retraitement après redémarrages.
- Correctif : définir
checkpointLocationet utiliser des destinations idempotentes.
- Surcharge du driver (driver overload)
- Symptôme : OOM driver ou longues pauses GC.
- Correctif : réduire les
collect()et opérations lourdes côté driver ; augmenter la mémoire driver.
- Surutilisation des UDF Python
- Symptôme : étapes lentes à cause de la surcharge de sérialisation.
- Correctif : utiliser les fonctions Spark SQL intégrées ou les pandas UDF quand approprié.
- Partitions de shuffle mal dimensionnées
- Symptôme : cluster sous-utilisé ou surcharge excessive.
- Correctif : régler
spark.sql.shuffle.partitions; compter sur l'AQE pour fusionner.
Plan de pilote local
Commencer petit, mesurable et inspectable localement avant la production.
Périmètre
- Un job de lot représentatif et un job de streaming court.
- Volume de données : 10 à 50 Go pour le lot ; 1 à 5 partitions pour l'entrée streaming.
Environnement
- Exécuter Spark en mode local pour la première validation :
spark.master=local[*]
Métriques de succès
- Lot : durée p95 dans la cible sur l'échantillon ; 0 étage échoué ; tailles de fichiers de sortie près de 128 à 512 Mo.
- Streaming : temps de traitement par déclenchement stable ; retard sous seuil ; aucune erreur de checkpoint pendant 24 heures.
Exercices de défaillance
- Tuer un exécuteur ou simuler un aléa réseau ; confirmer les réessais et l'idempotence.
- Corrompre un fichier d'entrée ; vérifier que le job isole et signale l'échec sans cascade.
Checklist de promotion
- Configurations épinglées et versionnées ; métriques et alertes actives ; runbooks à jour.
- Étapes de retour arrière testées ; version antérieure prête à redéployer.
Conclusion
Une configuration Apache Spark fiable en production commence par des bases claires, une surveillance actionnable et des jobs redémarrables. Ajoutez des garde-fous de stockage et de sécurité, ajustez pour le coût et la performance, et pratiquez les routines de maintenance et de sauvegarde. Mettez à niveau par étapes avec un plan de retour arrière, et commencez par un pilote étroit que vous pouvez mesurer et inspecter localement. Appliquez cette checklist, suivez un petit ensemble d'indicateurs avancés et itérez après chaque version pour maintenir les opérations Spark prévisibles et rentables.