Introduction
La sauvegarde Apache Spark n’est pas une opération unique. Spark est un moteur de calcul distribué ; les données et métadonnées à protéger résident le plus souvent en dehors de Spark, dans des systèmes de fichiers, des stockages objet, des catalogues et des services externes. Une stratégie fiable distingue :
- Reprise d’application : votre code Spark, ses dépendances et sa configuration.
- Reprise de données : les jeux de données et métadonnées de tables que Spark lit et écrit.
- Reprise de cluster : la configuration de calcul et de plateforme qui exécute Spark.
Ce guide explique la sauvegarde, la restauration, le rollback, la reprise après sinistre et la validation pour Apache Spark, avec des exemples concrets couvrant HDFS et le stockage objet, Hive Metastore et les catalogues, les formats de table (Iceberg, Delta Lake, Hudi) et les checkpoints Structured Streaming. Il montre comment tester des restaurations en toute sécurité et comment choisir parmi les options de retour arrière.
Que faut-il sauvegarder dans un environnement Spark ?
| Composant | Contenu | Est-il autoritatif ? | Méthode de sauvegarde | Priorité de récupération |
|---|---|---|---|---|
| Code source des applications Spark | Jobs, packages, tests | Oui (pour la logique) | Contrôle de version avec tags de release | Élevée |
| Dépendances JAR et Python | Artéfacts construits, wheels | Oui (pour la reproductibilité) | Registre d’artéfacts ; fichiers de verrouillage | Élevée |
| Configuration spark-submit | Arguments submit, conf Spark, paramètres de job | Oui (pour le comportement) | Dépôt de code et magasin de paramètres | Élevée |
| Configuration d’environnement | Références de secrets, endpoints, rôles IAM | Oui | Dépôt IaC ; magasin de paramètres/secrets | Élevée |
| Jeux de données HDFS ou stockage objet | Données brutes, affinées, curées | Oui (pour les données) | Sauvegardes au niveau stockage, réplication, versioning | Critique |
| Hive Metastore ou base de catalogue | Tables, schémas, emplacements | Oui (pour les métadonnées) | Dump/restauration BD, snapshots | Critique |
| Métadonnées Iceberg/Delta/Hudi | Snapshots, journaux de transactions | Oui (pour l’intégrité des tables) | Sauvegarder métadonnées et fichiers ; rétention maîtrisée | Critique |
| Checkpoints Structured Streaming | Offsets, state store, commits | Oui (pour la continuité du streaming) | Snapshots/copies sur stockage durable | Élevée |
| Journaux d’événements Spark | Historique des jobs pour opérations et audits | Non (support) | Copies de fichiers vers stockage durable | Moyenne |
| Orchestration (Airflow, etc.) | DAGs, horaires, variables | Oui (pour la planification) | VCS + export/snapshots | Élevée |
| Manifests Kubernetes ou valeurs Helm | Spécifications de déploiement, images, quotas | Oui (pour l’infra) | Dépôt GitOps, registre | Élevée |
| Secrets et identifiants | Clés, jetons, mots de passe | Oui et sensibles | Sauvegardes chiffrées du gestionnaire de secrets uniquement | Priorité maximale, sécurisé |
Ne copiez jamais de secrets dans des dépôts de code ni des scripts de sauvegarde. Utilisez un gestionnaire de secrets et sauvegardez son état chiffré selon les recommandations du fournisseur.
Définir les objectifs de reprise avant de choisir une méthode de sauvegarde
- Objectif de point de reprise (RPO) : perte de données maximale tolérable (en temps). Exemple : RPO 4 heures.
- Objectif de temps de reprise (RTO) : délai pour rétablir le service. Exemple : RTO 2 heures.
- Période de rétention : durée de conservation des sauvegardes (ex. 90 jours, 1 an).
- Granularité de restauration : fichier, partition, version de table ou environnement complet.
- Reprise inter‑régions ou hors site : se prémunir contre les sinistres régionaux.
- Sauvegardes immuables : prévenir la falsification et l’impact des ransomwares.
- Chiffrement : en transit et au repos ; rotation et sauvegarde des clés de façon sécurisée.
- Contrôles d’accès : moindre privilège pour lire/restaurer les sauvegardes.
Exemple : un pipeline Spark exécuté tous les 14 jours sur des données de fin de mois peut fixer RPO = 24 heures (on peut réexécuter le dernier jour de staging) et RTO = 4 heures. Conserver 15 mois de sauvegardes des tables curées pour la conformité réglementaire, et garder des copies immuables hors site des métadonnées de tables et des fichiers de données.
Scénario 1 : sauvegarder le code applicatif Spark et la configuration
Exemple de structure de dépôt :
spark-pipeline/
jobs/
config/
requirements.txt
build.sbt
Dockerfile
kubernetes/
airflow/
tests/
Bonnes pratiques :
- Contrôle de version : trunk‑based ou GitFlow ; protéger la branche principale avec des vérifications CI. Utilisez des tags de release annotés.
- Verrouillage des dépendances : requirements.txt ou poetry.lock pour Python ; locks SBT/Maven pour Scala. Publiez les artéfacts dans un registre.
- Registres d’artéfacts : poussez JARs, wheels et images Docker vers un registre. Évitez les tags mutables ; préférez SemVer, p. ex. 1.4.2.
- Séparation de configuration : conservez les paramètres spécifiques à l’environnement dans des fichiers dédiés ou un magasin de paramètres. Ne codez jamais de secrets en dur.
- Images reproductibles : fixez les digests des images de base et les versions de Spark ; documentez les arguments spark-submit.
Exemples :
# Taguer une release
git tag -a v1.4.2 -m 'Améliorations du pipeline de fin de mois'
git push origin v1.4.2
# Créer une archive source reproductible
git archive --format=tar.gz --prefix=spark-pipeline-1.4.2/ v1.4.2 > spark-pipeline-1.4.2.tar.gz
Un dépôt Git n’est pas une sauvegarde des données traitées. Il ne capture que le code et la configuration.
Scénario 2 : sauvegarder des données stockées dans HDFS
La réplication HDFS protège contre la perte de disque/nœud, mais ce n’est pas une sauvegarde. Utilisez DistCp pour copier des données vers un autre cluster HDFS ou vers du stockage objet.
Inspection et planification :
hdfs dfs -ls -h hdfs:///data/curated/sales/
hdfs dfs -count -h hdfs:///data/curated/sales/
hdfs dfs -mkdir -p hdfs:///backups/curated/sales/
Copie avec DistCp :
hadoop distcp \
hdfs:///data/curated/sales/ \
hdfs:///backups/curated/sales/
Vérifier counts et tailles :
hdfs dfs -count -h hdfs:///data/curated/sales/
hdfs dfs -count -h hdfs:///backups/curated/sales/
Restaurer d’abord vers un chemin séparé :
hdfs dfs -mkdir -p hdfs:///restore/curated/sales/
hadoop distcp hdfs:///backups/curated/sales/ hdfs:///restore/curated/sales/
Valider avec Spark avant de remplacer la production :
# Exemple de validation PySpark
src = 'hdfs:///data/curated/sales/'
restored = 'hdfs:///restore/curated/sales/'
df_src = spark.read.parquet(src)
df_rst = spark.read.parquet(restored)
assert df_src.count() == df_rst.count()
assert df_src.schema.simpleString() == df_rst.schema.simpleString()
Restaurer dans un chemin séparé évite d’écraser de bonnes données si la validation échoue.
Scénario 3 : sauvegarder des données dans S3 ou un stockage objet cloud compatible
Spark n’exécute pas de sauvegardes de stockage. Utilisez les capacités natives du stockage :
- Versioning des objets : conserver les versions antérieures pour récupérer des suppressions ou écrasements.
- Politiques de cycle de vie : basculer les données anciennes vers des classes plus froides ; expirer conformément aux règles.
- Réplication : inter‑régions ou inter‑comptes pour la reprise après sinistre.
- Object Lock ou immutabilité : WORM pour bloquer les altérations.
- Contrôles d’accès : identifiants séparés pour les sauvegardes et supervision renforcée.
Les commandes varient entre AWS S3, Azure Data Lake Storage, Google Cloud Storage et les systèmes compatibles S3 (p. ex. MinIO). A minima, activez le versioning, configurez une politique de cycle de vie et vérifiez périodiquement que les données de table, journaux de transactions et manifests sont bien inclus dans les ensembles répliqués ou immuables.
Scénario 4 : sauvegarder un Hive Metastore ou une base de données de catalogue
Copier uniquement des fichiers Parquet ne suffit pas. Il faut préserver les définitions de tables, les schémas, les métadonnées de partitions et les emplacements de tables pour que les lectures se résolvent correctement.
Exemple avec Metastore adossé à PostgreSQL :
Sauvegarde :
pg_dump -h <metastore-host> -U <user> -d <database> -Fc -f /backups/hms_$(date +%F).dump
Restauration vers une base de reprise :
createdb -h <restore-host> -U <user> hms_recovery
pg_restore -h <restore-host> -U <user> -d hms_recovery -c /backups/hms_YYYY-MM-DD.dump
Valider les entrées :
- Vérifier que les emplacements des tables référencent les chemins HDFS ou objets attendus.
- Exécuter une requête de fumée via Spark SQL contre le catalogue restauré et confirmer que les partitions sont détectées.
Scénario 5 : récupération avec Apache Iceberg, Delta Lake et Hudi
Chaque format de table sépare les fichiers de données des métadonnées et maintient une chronologie des changements.
- Iceberg : des fichiers metadata.json et des snapshots référencent des manifests et des fichiers de données. La rétention est contrôlée par expire snapshots.
- Delta Lake : le répertoire _delta_log contient des fichiers JSON et des checkpoints Parquet. L’historique peut être « nettoyé » si la rétention est trop agressive.
- Hudi : une timeline (commit, delta commit, compaction) gère les versions ; les politiques de clean peuvent supprimer d’anciens instants.
Le voyage dans le temps aide au rollback, mais ne remplace pas une sauvegarde indépendante. Si vous exécutez vacuum/expire trop agressivement, les versions plus anciennes deviennent irrécupérables.
Exemples :
- Delta Lake :
spark.read.format('delta').option('versionAsOf', 17).load('/tables/sales_delta').show()
- Iceberg :
spark.read.format('iceberg').option('snapshot-id', '<snapshot_id>').load('prod.catalog.sales_iceberg').show()
- Hudi :
spark.read.format('hudi').option('as.of.instant', '20240101120000').load('/tables/sales_hudi').show()
Scénario 6 : checkpoints Structured Streaming
Un répertoire de checkpoint Structured Streaming stocke les offsets sources, journaux de commit et l’état. Il doit résider sur un stockage durable (HDFS ou stockage objet). Supprimer ou réutiliser un checkpoint pour une autre requête peut entraîner une re‑lecture, des sauts ou une perte d’état.
Exemple :
query = (spark.readStream.format('kafka')
.option('kafka.bootstrap.servers', '<broker1>:9092')
.option('subscribe', 'sales-events')
.load()
.selectExpr('CAST(value AS STRING) as json')
.writeStream
.format('parquet')
.option('path', 's3a://datalake/curated/sales_events/')
.option('checkpointLocation', 's3a://datalake/checkpoints/sales_events/')
.outputMode('append')
.start())
query.awaitTermination()
Les redémarrages sûrs réutilisent le même checkpoint durable avec une logique et un schéma inchangés. Copier un checkpoint ne garantit pas la reprise si le code, le schéma ou la rétention de la source ont changé.
Scénario 7 : restaurer un pipeline batch Spark en échec
Supposons qu’un job lise du Parquet brut, le transforme et écrive une table curée.
- Stopper les écritures dépendantes et mettre en pause les consommateurs.
- Identifier le dernier snapshot ou la dernière version de table valide.
- Conserver la sortie en échec sous un chemin de quarantaine.
- Restaurer les fichiers de données ou sélectionner une version antérieure (voyage dans le temps).
- Restaurer les métadonnées du catalogue si les définitions de tables ont été corrompues.
- Valider dans un emplacement temporaire.
- Comparer comptes, schémas, partitions et totaux métier.
- Bascule des consommateurs après validation.
- Documenter l’incident et les améliorations à apporter.
Exemples de validation :
cur = spark.read.format('delta').load('/tables/sales_delta')
prev = spark.read.format('delta').option('versionAsOf', 42).load('/tables/sales_delta')
# Nombre de lignes
assert cur.count() == prev.count()
# Schéma
assert cur.schema.json() == prev.schema.json()
# Contrôles de nullité
from pyspark.sql.functions import col, sum as _sum
nulls = cur.select([_sum(col(c).isNull().cast('int')).alias(c) for c in cur.columns])
nulls.show()
# Doublons de clé
pk = ['order_id']
cur.groupBy(pk).count().where('count > 1').show()
# Totaux métier
from pyspark.sql.functions import round
cur.groupBy().sum('amount').select(round('sum(amount)', 2).alias('total')).show()
# Échantillons de comparaison
cur.orderBy(pk).limit(10).show(truncate=False)
prev.orderBy(pk).limit(10).show(truncate=False)
Scénario 8 : reprise d’un job Structured Streaming
Considérations pour un flux Kafka‑to‑lake :
- Redémarrer avec le checkpoint durable existant pour reprendre aux offsets stockés.
- Les limites de rétention des offsets Kafka s’appliquent ; si les offsets ont expiré, relire depuis le plus ancien disponible ou un horodatage choisi.
- S’assurer que la cible est idempotente (p. ex. MERGE avec clés primaires) ou conçue pour un « exactly‑once » pragmatique.
- Si le checkpoint est perdu, créer un nouveau checkpoint et retraiter depuis une frontière d’offset choisie, en coordination avec les consommateurs aval.
Architecture de reprise après sinistre
Conception pragmatique :
- Cluster Spark principal (Kubernetes/YARN/VMs) créé par automatisation.
- HDFS ou stockage objet pour données et checkpoints.
- Metastore ou base de catalogue sur une base managée.
- Code applicatif et IaC sous contrôle de version.
- Registres d’artéfacts et d’images.
- Sauvegardes immuables hors site des données et métadonnées.
- Environnement secondaire provisionnable à la demande.
- Supervision et tests périodiques de restauration.
Diagramme texte :
[Code+IaC] -> [CI/CD] -> [Registry Artéfacts/Images]
-> [Cluster Spark Principal]
|
[HDFS/Stockage Objet] <-> [BD Catalogue]
|
[Sauvegardes immuables hors site]
|
[Environnement Secondaire]
Exemple de planning de sauvegarde
| Actif | Fréquence | Rétention | Emplacement | Méthode de validation | Propriétaire |
|---|---|---|---|---|---|
| Code applicatif | À chaque changement | Illimitée | Dépôt Git | Build tagué, tests CI | Data Eng |
| JARs/images | À la release | 2 ans | Registre d’artéfacts | Pull et smoke test | Plateforme |
| Jeux de données HDFS | DistCp quotidien | 90 jours | Cluster HDFS DR | Compte, taille, checksum | Data Ops |
| Jeux de données stockage objet | Versioning continu | 12 mois | Bucket inter‑régions | Restaure d’échantillons de partitions | Data Ops |
| Base de catalogue | Snapshot horaire | 30 jours | Backups BD managés | Restaure en staging et requête | DBA |
| Checkpoints streaming | Snapshot horaire | 14 jours | Stockage durable | Redémarrage du job en staging | Data Eng |
| Config cluster | À chaque changement | 1 an | Dépôt IaC | Recréation en labo | Plateforme |
| Journaux d’événements | Quotidien | 30 jours | Stockage objet | Vérif. via Spark History UI | SRE |
Stratégies de rollback Apache Spark
- Rollback applicatif : déployer un code ou une image antérieurs quand la logique est fautive.
- Rollback de configuration : revenir sur des paramètres Spark ou de job mal réglés.
- Rollback de jeu de données : restaurer des fichiers ou partitions depuis une sauvegarde.
- Rollback par snapshot de table : voyager dans le temps vers une version/snapshot antérieur.
- Restauration du catalogue : récupérer définitions et emplacements de tables depuis un snapshot BD.
- Reprise après sinistre complète : basculement vers l’environnement secondaire.
Quand utiliser quoi :
- Mauvaise release de code : rollback applicatif.
- Job mal tuné : rollback de configuration.
- Fichiers de sortie corrompus : rollback de dataset ou snapshot de table.
- Tables supprimées ou mauvais emplacements : restauration du catalogue.
- Panne régionale : reprise après sinistre complète.
Liste de contrôle de validation de restauration
- Fichiers présents ; comptes et taille totale plausibles.
- Checksums ou etags concordants lorsque disponibles.
- Schémas de tables conformes aux attentes.
- Partitions détectables et interrogeables.
- Entrées du catalogue pointant vers les bons emplacements.
- Comptes d’enregistrements et agrégats métier plausibles.
- Taux de doublons et de nullité dans la tolérance.
- Checkpoints streaming exploitables ; offsets et état valides.
- Les consommateurs aval lisent les sorties restaurées.
- Permissions et ACL correctes.
- Supervision et alertes fonctionnelles après restauration.
Erreurs fréquentes
- Confondre réplication HDFS et sauvegarde.
- Sauvegarder le code mais pas les données, ou l’inverse, sans le catalogue.
- Stocker les checkpoints sur des disques éphémères.
- Écraser la production pendant des tests de restauration.
- Ne jamais tester les restaurations.
- Conserver les sauvegardes dans le même domaine de défaillance.
- Utiliser des tags d’image mutables comme latest.
- Perdre ou ne pas sauvegarder les clés de chiffrement.
- Expirer trop agressivement les snapshots Iceberg ou l’historique Delta.
- Supposer que le voyage dans le temps remplace la reprise après sinistre.
- Ignorer les systèmes externes (Kafka, RDBMS, Airflow, gestion des secrets).
Exercice pratique de récupération
À exécuter en environnement non‑prod, en toute sécurité :
- Préparation : choisir une table de taille moyenne ; noter la version courante ; s’assurer que le versioning et les sauvegardes de catalogue sont activés.
- Panne simulée : écrire une mauvaise partition ou écraser la table avec un petit lot erroné.
- Restauration : voyager dans le temps vers la version précédente ou copier les fichiers depuis la sauvegarde vers un chemin de reprise ; restaurer le catalogue en staging.
- Validation : exécuter comptes, contrôles de schéma, agrégats ; comparer au snapshot pré‑panne.
- Rollback : basculer atomiquement le pointeur de table ou remplacer la partition par des données de reprise validées.
- Documentation : consigner délais, étapes et écarts ; mettre à jour les runbooks.
- Critères de succès : RTO et RPO respectés ; lectures aval OK ; supervision au vert.
Conclusion
Le calcul Apache Spark est généralement reproductible à partir du code, des images et de l’IaC. Les éléments qui exigent une protection délibérée sont les données, les métadonnées, les checkpoints, la configuration et les artéfacts. Ne confondez pas redondance de stockage et sauvegardes, et ne comptez pas uniquement sur le voyage dans le temps des tables. Définissez des cibles RPO et RTO explicites, sauvegardez chaque couche autoritative avec les bons outils, et prouvez votre plan de restauration Spark par des validations régulières et automatisées. Une sauvegarde n’est digne de confiance qu’après un test de restauration réussi.