Cloud & Object Storage for Data Engineers

Bienvenue dans ce module où tu vas maîtriser le Cloud Computing et l’Object Storage, les fondations de tout Data Lake moderne.


Prérequis

Niveau Module Compétence
✅ Requis Module 14 Docker Fundamentals
✅ Requis Module 19 PySpark Advanced
✅ Requis Module 21 Spark on Kubernetes
💡 Recommandé Module 08 Big Data Introduction (Medallion Architecture)

Objectifs du module

À la fin de ce module, tu seras capable de :

  • Comprendre les modèles Cloud (IaaS, PaaS, SaaS)
  • Connaître les services Data Engineering sur AWS, GCP, Azure
  • Maîtriser les concepts de l’Object Storage (buckets, keys, prefixes)
  • Lire/écrire sur S3, Azure Blob, GCS avec Python et Spark
  • Déployer MinIO pour pratiquer localement
  • Optimiser les performances (formats, partitionnement, small files)
  • Gérer la sécurité et les coûts

1. Introduction — Pourquoi le Cloud pour le Data Engineering ?

1.1 L’évolution du Data Engineering

2000s                    2010s                    2020s+
┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│  On-Premise │         │   Hadoop    │         │    Cloud    │
│  ┌───────┐  │         │  ┌───────┐  │         │  ┌───────┐  │
│  │ RDBMS │  │  ────▶  │  │ HDFS  │  │  ────▶  │  │  S3   │  │
│  └───────┘  │         │  └───────┘  │         │  └───────┘  │
│  Coûteux    │         │  Complexe   │         │  Scalable   │
└─────────────┘         └─────────────┘         └─────────────┘

1.2 Avantages du Cloud pour le Data Engineering

Avantage Description
Scalabilité Stockage et compute illimités à la demande
Coût Pay-as-you-go, pas d’investissement initial
Managed Services Moins d’ops, plus de focus sur les données
Séparation compute/storage Scaler indépendamment
Durabilité 99.999999999% (11 nines) pour S3
Global Données accessibles partout

2. Introduction au Cloud Computing

2.1 C’est quoi le Cloud ?

Le Cloud = des serveurs, du stockage et des services accessibles via Internet, gérés par un provider.

┌─────────────────────────────────────────────────────────────┐
│                        LE CLOUD                             │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐       │
│  │ Compute │  │ Storage │  │ Network │  │  ....   │       │
│  │  (VMs)  │  │ (Disks) │  │  (VPC)  │  │         │       │
│  └─────────┘  └─────────┘  └─────────┘  └─────────┘       │
│                                                             │
│  Tu ne gères pas le hardware, juste les services            │
└─────────────────────────────────────────────────────────────┘
                              ▲
                              │ Internet
                              │
                    ┌─────────┴─────────┐
                    │   Ton application  │
                    └───────────────────┘

2.2 Les 3 modèles de service

┌────────────────────────────────────────────────────────────────┐
│                    RESPONSABILITÉ                              │
│                                                                │
│   On-Premise      IaaS           PaaS           SaaS          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │Application│  │Application│  │Application│  │██████████│ ◀── Provider │
│  │──────────│  │──────────│  │██████████│  │██████████│      │
│  │  Data    │  │  Data    │  │██████████│  │██████████│      │
│  │──────────│  │──────────│  │██████████│  │██████████│      │
│  │ Runtime  │  │ Runtime  │  │██████████│  │██████████│      │
│  │──────────│  │──────────│  │██████████│  │██████████│      │
│  │   OS     │  │   OS     │  │██████████│  │██████████│      │
│  │──────────│  │██████████│  │██████████│  │██████████│      │
│  │ Virtual  │  │██████████│  │██████████│  │██████████│      │
│  │──────────│  │██████████│  │██████████│  │██████████│      │
│  │ Servers  │  │██████████│  │██████████│  │██████████│      │
│  │──────────│  │██████████│  │██████████│  │██████████│      │
│  │ Storage  │  │██████████│  │██████████│  │██████████│      │
│  │──────────│  │██████████│  │██████████│  │██████████│      │
│  │ Network  │  │██████████│  │██████████│  │██████████│      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
│                                                                │
│  █ = Géré par le Cloud Provider                               │
└────────────────────────────────────────────────────────────────┘
Modèle Description Exemples Tu gères
IaaS Infrastructure as a Service EC2, VMs, VPC OS, Runtime, App
PaaS Platform as a Service RDS, Cloud SQL, EKS App, Data
SaaS Software as a Service Snowflake, Databricks Rien (juste utiliser)

2.3 Les 3 grands Cloud Providers

Provider Part de marché Forces Faiblesse
AWS ~32% Leader, plus de services, maturité Complexité, coûts
Azure ~23% Intégration Microsoft, entreprises UX parfois confuse
GCP ~10% BigQuery, ML/AI, Kubernetes Moins de services

2.4 Régions & Zones de disponibilité

┌─────────────────────────────────────────────────────────────┐
│                     AWS Global                              │
│                                                             │
│  ┌─────────────────┐    ┌─────────────────┐                │
│  │  Region:        │    │  Region:        │                │
│  │  eu-west-1      │    │  us-east-1      │                │
│  │  (Ireland)      │    │  (N. Virginia)  │                │
│  │                 │    │                 │                │
│  │ ┌───┐ ┌───┐ ┌───┐│    │ ┌───┐ ┌───┐ ┌───┐│                │
│  │ │AZ1│ │AZ2│ │AZ3││    │ │AZ1│ │AZ2│ │AZ3││                │
│  │ └───┘ └───┘ └───┘│    │ └───┘ └───┘ └───┘│                │
│  └─────────────────┘    └─────────────────┘                │
│                                                             │
│  AZ = Availability Zone = Data Center isolé                 │
└─────────────────────────────────────────────────────────────┘
  • Region : Zone géographique (Paris, Dublin, N. Virginia)
  • Availability Zone : Data center isolé dans une région
  • Latence : Choisir la région proche des utilisateurs
  • Compliance : GDPR → données en Europe

Exercice 1 : Identifier IaaS / PaaS / SaaS

Classe ces services dans la bonne catégorie :

Service IaaS / PaaS / SaaS ?
Amazon EC2 ?
Google BigQuery ?
Snowflake ?
Azure Kubernetes Service ?
Amazon S3 ?
Databricks ?
💡 Voir les réponses
Service Catégorie Explication
Amazon EC2 IaaS Tu gères l’OS et tout ce qu’il y a dessus
Google BigQuery PaaS/SaaS Serverless, tu gères juste les requêtes
Snowflake SaaS Entièrement managé
Azure Kubernetes Service PaaS K8s managé, tu gères les workloads
Amazon S3 PaaS Stockage managé, tu gères les données
Databricks PaaS/SaaS Spark managé

3. Services Cloud pour le Data Engineering

3.1 Tableau comparatif complet

Catégorie AWS GCP Azure
Object Storage S3 GCS Blob Storage
Data Warehouse Redshift BigQuery Synapse Analytics
ETL Serverless Glue Dataflow Data Factory
Query Engine Athena BigQuery Synapse Serverless
Orchestration MWAA (Airflow) Composer (Airflow) Data Factory
Streaming Kinesis Pub/Sub + Dataflow Event Hubs
Catalog Glue Catalog Data Catalog Purview
Kubernetes EKS GKE AKS
Serverless Compute Lambda Cloud Functions Functions
NoSQL DynamoDB Firestore/Bigtable CosmosDB
Message Queue SQS/SNS Pub/Sub Service Bus

3.2 Focus sur les services Data Engineering

┌─────────────────────────────────────────────────────────────────┐
│                  ARCHITECTURE DATA CLOUD                        │
│                                                                 │
│  Sources              Ingestion           Storage               │
│  ┌─────┐             ┌─────────┐         ┌─────────┐           │
│  │ API │────────────▶│ Kinesis │────────▶│   S3    │           │
│  │ DB  │             │ Pub/Sub │         │  GCS    │           │
│  │ Files│             │ EventHub│         │  Blob   │           │
│  └─────┘             └─────────┘         └────┬────┘           │
│                                               │                 │
│                                               ▼                 │
│  Processing                              ┌─────────┐           │
│  ┌──────────────────────────────────────▶│  Glue   │           │
│  │                                       │Dataflow │           │
│  │                                       │  ADF    │           │
│  │                                       └────┬────┘           │
│  │                                            │                 │
│  │                                            ▼                 │
│  │  Serving                              ┌─────────┐           │
│  │  ┌─────────┐                          │Redshift │           │
│  └──│ Athena  │◀─────────────────────────│BigQuery │           │
│     │ Synapse │                          │ Synapse │           │
│     └─────────┘                          └─────────┘           │
└─────────────────────────────────────────────────────────────────┘

4. Storage Models : Block vs File vs Object

4.1 Comparaison détaillée

Critère Block Storage File Storage Object Storage
Structure Blocs bruts Hiérarchie fichiers Clé-valeur plat
Accès Bas niveau (disque) POSIX (NFS, SMB) API HTTP (REST)
Metadata Minimales Basiques Riches, custom
Scalabilité Limitée (TB) Limitée (TB) Illimitée (PB+)
Performance Très haute Moyenne Variable
Coût Élevé Moyen Faible
Use case Bases de données Partage fichiers Data Lakes
Exemples EBS, Azure Disk EFS, Azure Files S3, GCS, Blob

4.2 Pourquoi Object Storage pour les Data Lakes ?

✅ Scalabilité illimitée      ✅ Coût faible
✅ Durabilité 11 nines        ✅ API HTTP standard
✅ Séparation compute/storage ✅ Metadata riches
✅ Formats natifs (Parquet)   ✅ Multi-cloud possible

4.3 Séparation Compute / Storage

AVANT (Hadoop/HDFS)                    APRÈS (Cloud/Object Storage)
┌─────────────────────┐               ┌─────────────────────┐
│      Node 1         │               │    Compute (Spark)   │
│  ┌───────┐ ┌─────┐  │               │    ┌───────────┐     │
│  │Compute│ │Data │  │               │    │  Executor │     │
│  └───────┘ └─────┘  │               │    └─────┬─────┘     │
│      Couplés !      │               │          │           │
└─────────────────────┘               └──────────┼───────────┘
                                                 │ Network
                                                 ▼
                                      ┌─────────────────────┐
                                      │   Storage (S3)      │
                                      │    ┌─────────┐      │
                                      │    │  Data   │      │
                                      │    └─────────┘      │
                                      │   Scale séparément  │
                                      └─────────────────────┘

Exercice 2 : Choisir le bon type de storage

Quel type de storage pour chaque use case ?

Use case Block / File / Object ?
Base de données PostgreSQL ?
Data Lake avec fichiers Parquet ?
Partage de documents entre équipes ?
Logs d’application (PB de données) ?
VM avec OS Windows ?
💡 Voir les réponses
Use case Type Raison
PostgreSQL Block IOPS élevés, accès bas niveau
Data Lake Parquet Object Scalable, coût faible
Partage documents File Accès POSIX, permissions
Logs application Object Volume énorme, coût faible
VM Windows Block Disque système

5. Object Storage — Concepts fondamentaux

5.1 Buckets, Keys, Prefixes

s3://my-bucket/bronze/2024/01/transactions.parquet
     └───┬───┘ └──────────┬─────────┘ └─────┬──────┘
       Bucket           Prefix            Object Key

⚠️ IMPORTANT : Les préfixes NE SONT PAS des dossiers !
   C'est juste une convention de nommage (clé-valeur plat)
Concept Description Exemple
Bucket Conteneur racine, nom unique global my-company-datalake
Key Identifiant unique de l’objet bronze/2024/01/data.parquet
Prefix “Faux dossier”, filtre de listing bronze/2024/
Object Le fichier + ses metadata Parquet, CSV, JSON…

5.2 Metadata & Tags

Chaque objet peut avoir des metadata custom :

Voir le code
# Exemple de metadata sur un objet S3
metadata_example = {
    # Metadata système (automatiques)
    "Content-Type": "application/octet-stream",
    "Content-Length": 1048576,
    "Last-Modified": "2024-01-15T10:30:00Z",
    "ETag": "d41d8cd98f00b204e9800998ecf8427e",
    
    # Metadata custom (x-amz-meta-*)
    "x-amz-meta-source": "kafka-topic-orders",
    "x-amz-meta-pipeline": "etl-daily",
    "x-amz-meta-schema-version": "2.1",
}

# Tags (pour billing, governance)
tags = {
    "Environment": "production",
    "Team": "data-engineering",
    "CostCenter": "DE-001",
}

print("Metadata et Tags permettent de :")
print("- Tracer l'origine des données")
print("- Filtrer pour la gouvernance")
print("- Allouer les coûts par équipe")

5.3 Versioning & Lifecycle Policies

Versioning : Garder plusieurs versions d’un même objet

s3://bucket/data.csv
   │
   ├── Version 1 (2024-01-01) ← Ancienne
   ├── Version 2 (2024-01-15) ← Ancienne
   └── Version 3 (2024-02-01) ← Current

Lifecycle Policies : Automatiser la gestion du stockage

┌─────────────┐    30 jours    ┌─────────────┐   90 jours    ┌─────────────┐
│   Standard  │ ─────────────▶ │ Infrequent  │ ────────────▶ │   Glacier   │
│   $0.023/GB │                │   $0.0125/GB│               │  $0.004/GB  │
└─────────────┘                └─────────────┘               └─────────────┘
     Hot                           Cool                          Archive

5.4 Classes de stockage

Classe Usage Latence Coût stockage
Standard Accès fréquent ms $0.023/GB
IA (Infrequent Access) Accès rare ms $0.0125/GB
Glacier Archivage minutes-heures $0.004/GB
Glacier Deep Archive Compliance heures $0.00099/GB

5.5 Protocoles & Drivers

🔥 Section essentielle — Source de confusion fréquente !

Protocole Cloud Usage Driver/SDK Exemple
s3:// AWS CLI, Python boto3, aws cli s3://bucket/key
s3a:// AWS Spark/Hadoop hadoop-aws s3a://bucket/key
abfs:// Azure Spark (legacy) hadoop-azure abfs://container@account.dfs.core.windows.net/
abfss:// Azure Spark (TLS) hadoop-azure abfss://container@account.dfs.core.windows.net/
gs:// GCP Spark, CLI gcs-connector gs://bucket/key
https:// Tous Direct HTTP requests Signed URLs
⚠️ ATTENTION :

s3://  ≠  s3a://

- s3://  → AWS CLI, boto3 (haut niveau)
- s3a:// → Hadoop/Spark (bas niveau, optimisé Big Data)

Dans Spark, TOUJOURS utiliser s3a://, abfss://, ou gs://

5.6 Metadata Catalogs — Transition vers le module 23

L’Object Storage stocke des fichiers bruts. Mais pour faire du SQL, on a besoin de : - Schéma des tables (colonnes, types) - Localisation des partitions - Statistiques pour l’optimiseur

┌─────────────────────────────────────────────────────────────────┐
│                    METADATA CATALOG                             │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Table: sales                                           │   │
│  │  Location: s3://bucket/silver/sales/                    │   │
│  │  Schema: id INT, amount DOUBLE, date DATE               │   │
│  │  Partitions: date=2024-01-01, date=2024-01-02, ...      │   │
│  └─────────────────────────────────────────────────────────┘   │
└───────────────────────────────┬─────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                    OBJECT STORAGE (S3)                          │
│  s3://bucket/silver/sales/date=2024-01-01/part-00000.parquet   │
│  s3://bucket/silver/sales/date=2024-01-02/part-00000.parquet   │
└─────────────────────────────────────────────────────────────────┘

Solutions Catalog :

Solution Type Utilisé par
Hive Metastore Open-source Spark, Presto, Trino
AWS Glue Catalog Managed Athena, Glue, EMR
GCP Data Catalog Managed BigQuery, Dataproc
Azure Purview Managed Synapse, Databricks

💡 Preview Module 23 : Delta Lake et Iceberg intègrent leur propre Transaction Log directement dans l’Object Storage. Plus besoin de catalogue externe !

5.7 Layout Data Lake — Rappel (réf module 08)

s3://my-datalake/
│
├── bronze/                    ← Raw, immutable, source of truth
│   ├── orders/
│   │   └── 2024/01/01/
│   │       └── orders_raw.json
│   └── customers/
│       └── customers_full.csv
│
├── silver/                    ← Cleaned, validated, deduplicated
│   ├── orders/
│   │   └── date=2024-01-01/
│   │       └── part-00000.parquet
│   └── customers/
│       └── part-00000.parquet
│
└── gold/                      ← Aggregated, business-ready
    ├── daily_sales/
    │   └── part-00000.parquet
    └── customer_360/
        └── part-00000.parquet

Exercice 3 : Calculer le coût de stockage

Scénario : Tu as un Data Lake avec :

  • Bronze : 500 GB (accès rare)
  • Silver : 200 GB (accès fréquent)
  • Gold : 50 GB (accès très fréquent)

Calcule le coût mensuel optimal sur S3.

💡 Voir la solution
Layer Taille Classe Prix/GB Coût
Bronze 500 GB S3 IA $0.0125 $6.25
Silver 200 GB Standard $0.023 $4.60
Gold 50 GB Standard $0.023 $1.15
Total 750 GB $12.00/mois

Sans optimisation (tout en Standard) : 750 × $0.023 = $17.25/mois

Économie : 30% !


6. AWS S3 — Deep Dive

6.1 Concepts & Classes de stockage

Classe Durabilité Disponibilité Min storage Retrieval
Standard 11 nines 99.99% - Immédiat
Intelligent-Tiering 11 nines 99.9% - Immédiat
Standard-IA 11 nines 99.9% 30 jours Immédiat
Glacier Instant 11 nines 99.9% 90 jours ms
Glacier Flexible 11 nines 99.99% 90 jours 1-12h
Glacier Deep Archive 11 nines 99.99% 180 jours 12-48h

6.2 Opérations CLI

Voir le code
s3_cli_commands = """
# Lister les buckets
aws s3 ls

# Lister le contenu d'un bucket
aws s3 ls s3://my-bucket/bronze/

# Copier un fichier local vers S3
aws s3 cp data.csv s3://my-bucket/bronze/data.csv

# Copier un fichier S3 vers local
aws s3 cp s3://my-bucket/bronze/data.csv ./data.csv

# Synchroniser un dossier
aws s3 sync ./local-folder/ s3://my-bucket/bronze/

# Supprimer un fichier
aws s3 rm s3://my-bucket/bronze/old-data.csv

# Supprimer récursivement
aws s3 rm s3://my-bucket/temp/ --recursive
"""
print(s3_cli_commands)

6.3 Python avec boto3

Voir le code
# Installation : pip install boto3

import boto3
from botocore.exceptions import ClientError

# Créer un client S3
s3 = boto3.client('s3')

# --- Upload ---
def upload_file(file_path, bucket, key):
    """Upload un fichier vers S3."""
    s3.upload_file(file_path, bucket, key)
    print(f"✅ Uploaded {file_path} to s3://{bucket}/{key}")

# --- Download ---
def download_file(bucket, key, file_path):
    """Download un fichier depuis S3."""
    s3.download_file(bucket, key, file_path)
    print(f"✅ Downloaded s3://{bucket}/{key} to {file_path}")

# --- List objects ---
def list_objects(bucket, prefix=""):
    """Liste les objets dans un bucket."""
    response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix)
    if 'Contents' in response:
        for obj in response['Contents']:
            print(f"  {obj['Key']} ({obj['Size']} bytes)")
    return response.get('Contents', [])

# --- Presigned URL ---
def generate_presigned_url(bucket, key, expiration=3600):
    """Génère une URL temporaire pour accès direct."""
    url = s3.generate_presigned_url(
        'get_object',
        Params={'Bucket': bucket, 'Key': key},
        ExpiresIn=expiration
    )
    return url

# Exemple d'utilisation (commenté car pas de credentials)
# upload_file('data.csv', 'my-bucket', 'bronze/data.csv')
# list_objects('my-bucket', 'bronze/')
print("📝 Fonctions boto3 définies (upload, download, list, presigned URL)")

6.4 S3 avec Spark (s3a://)

Voir le code
# Configuration Spark pour S3
spark_s3_config = """
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("S3 Access") \
    .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem") \
    .config("spark.hadoop.fs.s3a.access.key", "YOUR_ACCESS_KEY") \
    .config("spark.hadoop.fs.s3a.secret.key", "YOUR_SECRET_KEY") \
    .config("spark.hadoop.fs.s3a.endpoint", "s3.amazonaws.com") \
    .getOrCreate()

# Lire depuis S3
df = spark.read.parquet("s3a://my-bucket/silver/sales/")

# Écrire vers S3
df.write.mode("overwrite").parquet("s3a://my-bucket/gold/aggregates/")
"""

# Optimisations S3A
s3a_optimizations = """
# Performance optimizations
spark.hadoop.fs.s3a.connection.maximum=200
spark.hadoop.fs.s3a.threads.max=64
spark.hadoop.fs.s3a.fast.upload=true
spark.hadoop.fs.s3a.fast.upload.buffer=bytebuffer
spark.hadoop.fs.s3a.multipart.size=104857600  # 100MB
"""
print(spark_s3_config)
print("\n--- Optimisations S3A ---")
print(s3a_optimizations)

6.5 Authentification & IAM

Méthode Sécurité Usage Production ?
Access Keys ⚠️ Faible Dev local ❌ Non
Instance Profile ✅ Haute EC2 ✅ Oui
IRSA (IAM Roles for Service Accounts) ✅ Haute EKS/K8s ✅ Oui
AssumeRole ✅ Haute Cross-account ✅ Oui
🔐 Best Practice : JAMAIS de credentials dans le code !

Utiliser :
- Variables d'environnement
- Instance Profiles (EC2)
- IRSA (Kubernetes) ← Module 21
- AWS Secrets Manager

Exercice 4 : Upload/Download avec boto3

Écris un script Python qui :

  1. Crée un fichier CSV local avec 3 lignes
  2. L’upload vers S3 (ou MinIO)
  3. Liste les fichiers du bucket
  4. Télécharge le fichier sous un autre nom
💡 Voir la solution
import boto3
import csv

# 1. Créer un fichier CSV
with open('test_data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['id', 'name', 'amount'])
    writer.writerow([1, 'Alice', 100])
    writer.writerow([2, 'Bob', 200])
    writer.writerow([3, 'Charlie', 150])

# 2. Upload
s3 = boto3.client('s3')
s3.upload_file('test_data.csv', 'my-bucket', 'bronze/test_data.csv')

# 3. List
response = s3.list_objects_v2(Bucket='my-bucket', Prefix='bronze/')
for obj in response.get('Contents', []):
    print(obj['Key'])

# 4. Download
s3.download_file('my-bucket', 'bronze/test_data.csv', 'downloaded_data.csv')

7. Azure Blob Storage — Deep Dive

7.1 Concepts

┌─────────────────────────────────────────────────────────────┐
│                   AZURE STORAGE ACCOUNT                     │
│                   (mystorageaccount)                        │
│  ┌─────────────────┐  ┌─────────────────┐                  │
│  │   Container:    │  │   Container:    │                  │
│  │   bronze        │  │   silver        │                  │
│  │  ┌───────────┐  │  │  ┌───────────┐  │                  │
│  │  │ data.csv  │  │  │  │ data.parq │  │                  │
│  │  └───────────┘  │  │  └───────────┘  │                  │
│  └─────────────────┘  └─────────────────┘                  │
└─────────────────────────────────────────────────────────────┘
Concept Azure Équivalent S3
Storage Account (pas d’équivalent, niveau compte)
Container Bucket
Blob Object
ADLS Gen2 S3 + Glue Catalog intégré

7.2 Access Tiers

Tier Usage Coût stockage Coût accès
Hot Fréquent Élevé Faible
Cool Rare (30+ jours) Moyen Moyen
Archive Archivage (180+ jours) Très faible Élevé
Voir le code
# Installation : pip install azure-storage-blob

azure_blob_example = """
from azure.storage.blob import BlobServiceClient, BlobClient

# Connection string (dev only !)
connection_string = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net"

# Créer le client
blob_service = BlobServiceClient.from_connection_string(connection_string)

# Upload
blob_client = blob_service.get_blob_client(container="bronze", blob="data.csv")
with open("data.csv", "rb") as f:
    blob_client.upload_blob(f, overwrite=True)

# Download
with open("downloaded.csv", "wb") as f:
    f.write(blob_client.download_blob().readall())

# List blobs
container_client = blob_service.get_container_client("bronze")
for blob in container_client.list_blobs():
    print(blob.name)
"""
print(azure_blob_example)

7.3 Azure Blob avec Spark (abfss://)

Voir le code
spark_azure_config = """
# Configuration Spark pour Azure Blob (ADLS Gen2)
storage_account = "mystorageaccount"
container = "bronze"

# Méthode 1 : Access Key (dev only)
spark.conf.set(
    f"fs.azure.account.key.{storage_account}.dfs.core.windows.net",
    "YOUR_ACCESS_KEY"
)

# Méthode 2 : Service Principal (production)
spark.conf.set(f"fs.azure.account.auth.type.{storage_account}.dfs.core.windows.net", "OAuth")
spark.conf.set(f"fs.azure.account.oauth.provider.type.{storage_account}.dfs.core.windows.net",
               "org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider")
spark.conf.set(f"fs.azure.account.oauth2.client.id.{storage_account}.dfs.core.windows.net", "CLIENT_ID")
spark.conf.set(f"fs.azure.account.oauth2.client.secret.{storage_account}.dfs.core.windows.net", "CLIENT_SECRET")
spark.conf.set(f"fs.azure.account.oauth2.client.endpoint.{storage_account}.dfs.core.windows.net",
               "https://login.microsoftonline.com/TENANT_ID/oauth2/token")

# Lire
df = spark.read.parquet(f"abfss://{container}@{storage_account}.dfs.core.windows.net/data/")
"""
print(spark_azure_config)

7.4 Authentification Azure

Méthode Sécurité Usage Production ?
Access Keys ⚠️ Faible Dev ❌ Non
SAS Token ⚠️ Moyenne Temporaire, externe ⚠️ Limité
Service Principal ✅ Haute Apps, CI/CD ✅ Oui
Managed Identity ✅ Très haute VMs, AKS ✅ Oui

Exercice 5 : Générer un SAS Token

Un SAS Token permet de donner un accès temporaire à un blob.

💡 Voir la solution
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions
from datetime import datetime, timedelta

account_name = "mystorageaccount"
account_key = "YOUR_KEY"
container = "bronze"
blob_name = "data.csv"

# Générer SAS Token (valide 1 heure)
sas_token = generate_blob_sas(
    account_name=account_name,
    container_name=container,
    blob_name=blob_name,
    account_key=account_key,
    permission=BlobSasPermissions(read=True),
    expiry=datetime.utcnow() + timedelta(hours=1)
)

# URL complète
url = f"https://{account_name}.blob.core.windows.net/{container}/{blob_name}?{sas_token}"
print(url)

8. Google Cloud Storage — Deep Dive

8.1 Concepts

Classe Usage SLA Coût
Standard Fréquent 99.99% $0.020/GB
Nearline 1x/mois 99.9% $0.010/GB
Coldline 1x/trimestre 99.9% $0.004/GB
Archive 1x/an 99.9% $0.0012/GB
Voir le code
gcs_cli_commands = """
# gsutil - CLI pour GCS

# Lister les buckets
gsutil ls

# Lister le contenu d'un bucket
gsutil ls gs://my-bucket/bronze/

# Copier
gsutil cp data.csv gs://my-bucket/bronze/
gsutil cp gs://my-bucket/bronze/data.csv ./

# Synchroniser (comme rsync)
gsutil rsync -r ./local/ gs://my-bucket/bronze/

# Copie parallèle (gros fichiers)
gsutil -m cp -r ./data/ gs://my-bucket/bronze/
"""
print(gcs_cli_commands)
Voir le code
# Installation : pip install google-cloud-storage

gcs_python_example = """
from google.cloud import storage

# Créer le client (utilise GOOGLE_APPLICATION_CREDENTIALS)
client = storage.Client()

# Accéder au bucket
bucket = client.bucket("my-bucket")

# Upload
blob = bucket.blob("bronze/data.csv")
blob.upload_from_filename("data.csv")

# Download
blob.download_to_filename("downloaded.csv")

# List blobs
blobs = bucket.list_blobs(prefix="bronze/")
for blob in blobs:
    print(blob.name)

# Signed URL (temporaire)
url = blob.generate_signed_url(expiration=3600)  # 1 heure
"""
print(gcs_python_example)

8.3 GCS avec Spark (gs://)

Voir le code
spark_gcs_config = """
# Configuration Spark pour GCS

# Option 1 : Service Account Key (dev)
spark.conf.set("spark.hadoop.google.cloud.auth.service.account.enable", "true")
spark.conf.set("spark.hadoop.google.cloud.auth.service.account.json.keyfile", "/path/to/keyfile.json")

# Option 2 : Application Default Credentials (GKE, Cloud Functions)
# Pas de config nécessaire si ADC est configuré

# Lire depuis GCS
df = spark.read.parquet("gs://my-bucket/silver/data/")

# Écrire vers GCS
df.write.mode("overwrite").parquet("gs://my-bucket/gold/aggregates/")
"""
print(spark_gcs_config)

8.4 Authentification GCP

Méthode Sécurité Usage Production ?
Service Account Key ⚠️ Moyenne Dev, CI/CD ⚠️ Avec précaution
Workload Identity ✅ Haute GKE ✅ Oui
ADC (Application Default Credentials) ✅ Haute Cloud Functions, Cloud Run ✅ Oui

Exercice 6 : Lister et télécharger depuis GCS

Écris un script qui liste tous les fichiers .parquet dans un bucket et télécharge le premier.

💡 Voir la solution
from google.cloud import storage

client = storage.Client()
bucket = client.bucket("my-bucket")

# Lister les fichiers .parquet
parquet_files = []
for blob in bucket.list_blobs(prefix="silver/"):
    if blob.name.endswith('.parquet'):
        parquet_files.append(blob)
        print(f"Found: {blob.name}")

# Télécharger le premier
if parquet_files:
    first_file = parquet_files[0]
    first_file.download_to_filename("downloaded.parquet")
    print(f"Downloaded: {first_file.name}")

9. MinIO — Object Storage Local

9.1 Pourquoi MinIO ?

Avantage Description
100% S3 compatible Même API, même code boto3
Gratuit Open-source, pas de compte cloud
Local Parfait pour dev/test
Léger Docker, un seul binaire
Production-ready Utilisé aussi en production (on-premise)
💡 Le code écrit pour MinIO fonctionne sur S3 sans modification !
   Il suffit de changer l'endpoint.

9.2 Installation avec Docker

Voir le code
%%writefile /tmp/minio/docker-compose.yaml
version: '3.8'

services:
  minio:
    image: minio/minio:latest
    container_name: minio
    ports:
      - "9000:9000"   # API S3
      - "9001:9001"   # Console Web
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    command: server /data --console-address ":9001"
    volumes:
      - minio-data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  minio-data:
Voir le code
minio_commands = """
# Démarrer MinIO
docker-compose up -d

# Accéder à la console web
# http://localhost:9001
# Login: minioadmin / minioadmin

# Installer mc (MinIO Client)
# Linux
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/

# Mac
brew install minio/stable/mc

# Configurer mc
mc alias set myminio http://localhost:9000 minioadmin minioadmin

# Créer des buckets
mc mb myminio/bronze
mc mb myminio/silver
mc mb myminio/gold

# Lister
mc ls myminio/

# Upload
mc cp data.csv myminio/bronze/
"""
print(minio_commands)

9.4 Python avec MinIO (boto3)

Voir le code
# Le même code boto3 fonctionne avec MinIO !
# Il suffit de spécifier endpoint_url

minio_boto3_example = """
import boto3
from botocore.client import Config

# Configuration pour MinIO
s3 = boto3.client(
    's3',
    endpoint_url='http://localhost:9000',  # ← La seule différence !
    aws_access_key_id='minioadmin',
    aws_secret_access_key='minioadmin',
    config=Config(signature_version='s3v4')
)

# Créer un bucket
s3.create_bucket(Bucket='bronze')

# Upload (identique à S3)
s3.upload_file('data.csv', 'bronze', 'raw/data.csv')

# List (identique à S3)
response = s3.list_objects_v2(Bucket='bronze')
for obj in response.get('Contents', []):
    print(obj['Key'])

# Download (identique à S3)
s3.download_file('bronze', 'raw/data.csv', 'downloaded.csv')
"""
print(minio_boto3_example)
print("\n💡 Ce code fonctionne sur S3 en enlevant juste endpoint_url !")

9.5 Spark avec MinIO

Voir le code
spark_minio_config = """
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("MinIO Access") \
    .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem") \
    .config("spark.hadoop.fs.s3a.endpoint", "http://localhost:9000") \
    .config("spark.hadoop.fs.s3a.access.key", "minioadmin") \
    .config("spark.hadoop.fs.s3a.secret.key", "minioadmin") \
    .config("spark.hadoop.fs.s3a.path.style.access", "true") \
    .getOrCreate()

# Lire depuis MinIO (même syntaxe que S3 !)
df = spark.read.csv("s3a://bronze/raw/data.csv", header=True)

# Écrire vers MinIO
df.write.mode("overwrite").parquet("s3a://silver/clean/data/")
"""
print(spark_minio_config)

Exercice 7 : Déployer MinIO et créer un bucket

  1. Lance MinIO avec Docker
  2. Accède à la console (http://localhost:9001)
  3. Crée les buckets bronze, silver, gold
  4. Upload un fichier via la console
# Solution rapide
docker run -d --name minio \
  -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=minioadmin \
  -e MINIO_ROOT_PASSWORD=minioadmin \
  minio/minio server /data --console-address ":9001"

10. Performance & Optimisation

10.1 Le problème des petits fichiers

❌ MAUVAIS : 10,000 fichiers × 1 MB = 10 GB
   - 10,000 requêtes API (LIST + GET)
   - Overhead énorme pour Spark
   - Temps de lecture : minutes

✅ BON : 100 fichiers × 100 MB = 10 GB
   - 100 requêtes API
   - Parallélisme optimal
   - Temps de lecture : secondes

Taille idéale : 100 MB - 1 GB par fichier

Solutions : - df.coalesce(n) ou df.repartition(n) avant écriture - Compaction périodique - Delta Lake / Iceberg (Module 23) = compaction automatique

Voir le code
# Éviter les petits fichiers avec Spark
small_files_solution = """
# Lecture de beaucoup de petits fichiers
df = spark.read.parquet("s3a://bronze/data/")  # 10,000 fichiers

# ❌ Écriture directe = même nombre de fichiers
# df.write.parquet("s3a://silver/data/")

# ✅ Repartitionner avant d'écrire
df.repartition(100) \  # 100 fichiers de ~100 MB
  .write.mode("overwrite") \
  .parquet("s3a://silver/data/")

# ✅ Ou coalesce (moins de shuffle)
df.coalesce(100) \
  .write.mode("overwrite") \
  .parquet("s3a://silver/data/")
"""
print(small_files_solution)

10.2 Partitionnement

s3://bucket/silver/sales/
├── year=2024/
│   ├── month=01/
│   │   ├── day=01/
│   │   │   └── part-00000.parquet
│   │   └── day=02/
│   │       └── part-00000.parquet
│   └── month=02/
│       └── ...
└── year=2023/
    └── ...

Avantages : - Partition pruning (Spark ne lit que les partitions nécessaires) - Requêtes plus rapides

⚠️ Attention au over-partitioning :

  • Trop de partitions = trop de petits fichiers
  • Règle : max 10,000 partitions
Voir le code
# Partitionnement avec Spark
partitioning_example = """
# Écriture partitionnée
df.write \
  .partitionBy("year", "month") \
  .mode("overwrite") \
  .parquet("s3a://silver/sales/")

# Lecture avec partition pruning
df = spark.read.parquet("s3a://silver/sales/")

# Cette requête ne lit QUE year=2024/month=01
df.filter("year = 2024 AND month = 1").show()
"""
print(partitioning_example)

10.3 Formats de fichiers

Format Type Compression Lecture colonnes Use case
CSV Row Non Échange, debug
JSON Row Non APIs, logs
Avro Row Oui Streaming, Kafka
Parquet Columnar Oui Analytics, Data Lake
ORC Columnar Oui Hive, analytics
💡 Pour le Data Engineering : PARQUET est le standard
   - Compression excellente (snappy, zstd)
   - Lecture par colonnes
   - Predicate pushdown
   - Schema intégré

Exercice 8 : Comparer Parquet vs CSV

Voir le code
# Exercice : Comparer la taille et le temps de lecture

format_comparison = """
import time
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("Format Comparison").getOrCreate()

# Créer un DataFrame de test (1M lignes)
df = spark.range(1000000).toDF("id") \
    .withColumn("name", lit("test_name")) \
    .withColumn("amount", rand() * 1000)

# Écrire en CSV
start = time.time()
df.write.mode("overwrite").csv("s3a://test/csv/")
csv_write_time = time.time() - start

# Écrire en Parquet
start = time.time()
df.write.mode("overwrite").parquet("s3a://test/parquet/")
parquet_write_time = time.time() - start

# Lire CSV
start = time.time()
df_csv = spark.read.csv("s3a://test/csv/").count()
csv_read_time = time.time() - start

# Lire Parquet
start = time.time()
df_parquet = spark.read.parquet("s3a://test/parquet/").count()
parquet_read_time = time.time() - start

print(f"CSV Write: {csv_write_time:.2f}s, Read: {csv_read_time:.2f}s")
print(f"Parquet Write: {parquet_write_time:.2f}s, Read: {parquet_read_time:.2f}s")
"""
print(format_comparison)
print("\n💡 Résultat attendu : Parquet 5-10x plus rapide et 5-10x plus petit")

Exercice 9 : Impact de la taille des fichiers

Objectif : Mesurer l’impact du nombre de fichiers sur les performances.

# Scénario A : 1000 petits fichiers
df.repartition(1000).write.parquet("s3a://test/small-files/")

# Scénario B : 10 gros fichiers
df.repartition(10).write.parquet("s3a://test/large-files/")

# Mesurer le temps de lecture pour chaque

Résultat attendu : Scénario B sera beaucoup plus rapide.


11. Sécurité & Gouvernance

11.1 Encryption

Type Description Qui gère la clé ?
SSE-S3 Encryption côté serveur, clé AWS AWS
SSE-KMS Encryption avec AWS KMS AWS (tu choisis la clé)
SSE-C Encryption avec clé client Toi
Client-side Encryption avant upload Toi
💡 Best Practice : SSE-KMS pour la plupart des cas
   - Rotation automatique des clés
   - Audit dans CloudTrail
   - Contrôle d'accès fin

11.2 Access Control

Voir le code
# Exemple de bucket policy S3
bucket_policy_example = """
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowDataTeamRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789:role/DataEngineerRole"
            },
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my-datalake",
                "arn:aws:s3:::my-datalake/*"
            ]
        },
        {
            "Sid": "DenyPublicAccess",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my-datalake/*",
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            }
        }
    ]
}
"""
print(bucket_policy_example)

11.3 Audit & Compliance

Service Cloud Ce qu’il trace
S3 Access Logs AWS Qui accède à quoi
CloudTrail AWS Toutes les API calls
Activity Logs Azure Opérations sur Blob
Audit Logs GCP Accès aux ressources

12. Coûts & Optimisation financière

12.1 Composantes du coût

Composante Description Exemple S3
Stockage GB/mois $0.023/GB (Standard)
PUT/POST Écritures $0.005 / 1000 requêtes
GET Lectures $0.0004 / 1000 requêtes
LIST Listing $0.005 / 1000 requêtes
Egress Sortie du cloud $0.09/GB (vers Internet)

12.2 Coûts cachés

⚠️ Attention aux coûts cachés :

1. LISTING fréquent
   - Spark fait un LIST avant chaque lecture
   - 1M de fichiers = 1000 LIST calls = $5

2. EGRESS
   - Données sortant du cloud = coûteux
   - Cross-region = $0.02/GB
   - Vers Internet = $0.09/GB

3. Small files
   - Plus de requêtes API
   - Plus de listing

12.3 Lifecycle Policies

Voir le code
# Exemple de lifecycle policy S3
lifecycle_policy = """
{
    "Rules": [
        {
            "ID": "ArchiveBronzeData",
            "Status": "Enabled",
            "Filter": {
                "Prefix": "bronze/"
            },
            "Transitions": [
                {
                    "Days": 30,
                    "StorageClass": "STANDARD_IA"
                },
                {
                    "Days": 90,
                    "StorageClass": "GLACIER"
                }
            ],
            "Expiration": {
                "Days": 365
            }
        }
    ]
}
"""
print(lifecycle_policy)

Exercice 10 : Estimer le coût mensuel d’un Data Lake

Scénario :

  • Bronze : 1 TB (accès rare)
  • Silver : 500 GB (accès fréquent)
  • Gold : 100 GB (accès très fréquent)
  • 100,000 requêtes GET/jour
  • 10,000 requêtes PUT/jour
  • 50 GB egress/mois

Calcule le coût mensuel sur AWS S3.

💡 Voir la solution
Élément Calcul Coût
Bronze (S3 IA) 1000 GB × $0.0125 $12.50
Silver (Standard) 500 GB × $0.023 $11.50
Gold (Standard) 100 GB × $0.023 $2.30
GET requests 100K × 30 / 1000 × $0.0004 $1.20
PUT requests 10K × 30 / 1000 × $0.005 $1.50
Egress 50 GB × $0.09 $4.50
Total ~$33.50/mois

13. Mini-Projet : Data Lake avec MinIO

Objectif

Construire un Data Lake local complet avec MinIO.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                       MinIO (Docker)                            │
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐        │
│  │   bronze/   │    │   silver/   │    │    gold/    │        │
│  │             │    │             │    │             │        │
│  │  raw.csv    │───▶│ clean.parq  │───▶│  agg.parq   │        │
│  │             │    │             │    │             │        │
│  └─────────────┘    └─────────────┘    └─────────────┘        │
│        ▲                  ▲                  ▲                  │
│        │                  │                  │                  │
│     Upload            Transform          Aggregate              │
│    (boto3)           (PySpark)         (Spark SQL)             │
└─────────────────────────────────────────────────────────────────┘

Étapes du projet

Voir le code
# Étape 1 : Démarrer MinIO
print("""
# Terminal 1 : Démarrer MinIO
docker run -d --name minio \
  -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=minioadmin \
  -e MINIO_ROOT_PASSWORD=minioadmin \
  minio/minio server /data --console-address ":9001"

# Vérifier que MinIO tourne
curl http://localhost:9000/minio/health/live
""")
Voir le code
# Étape 2 : Créer les buckets et uploader les données

step2_code = """
import boto3
from botocore.client import Config
import csv

# Connexion à MinIO
s3 = boto3.client(
    's3',
    endpoint_url='http://localhost:9000',
    aws_access_key_id='minioadmin',
    aws_secret_access_key='minioadmin',
    config=Config(signature_version='s3v4')
)

# Créer les buckets
for bucket in ['bronze', 'silver', 'gold']:
    try:
        s3.create_bucket(Bucket=bucket)
        print(f"✅ Bucket '{bucket}' créé")
    except Exception as e:
        print(f"⚠️ Bucket '{bucket}' existe déjà")

# Créer des données de test
with open('sales_data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['date', 'product', 'category', 'amount', 'quantity'])
    writer.writerow(['2024-01-01', 'Laptop', 'Electronics', 1200, 1])
    writer.writerow(['2024-01-01', 'Mouse', 'Electronics', 25, 3])
    writer.writerow(['2024-01-02', 'Desk', 'Furniture', 350, 1])
    writer.writerow(['2024-01-02', 'Chair', 'Furniture', 150, 2])
    writer.writerow(['2024-01-03', 'Laptop', 'Electronics', 1200, 2])
    writer.writerow(['2024-01-03', 'Monitor', 'Electronics', 400, 1])

# Upload vers bronze
s3.upload_file('sales_data.csv', 'bronze', 'raw/sales_data.csv')
print("✅ Données uploadées vers bronze/raw/sales_data.csv")
"""
print(step2_code)
Voir le code
# Étape 3 : Transformer avec PySpark (Bronze → Silver)

step3_code = """
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, to_date, upper

# Créer SparkSession avec config MinIO
spark = SparkSession.builder \
    .appName("Bronze to Silver") \
    .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem") \
    .config("spark.hadoop.fs.s3a.endpoint", "http://localhost:9000") \
    .config("spark.hadoop.fs.s3a.access.key", "minioadmin") \
    .config("spark.hadoop.fs.s3a.secret.key", "minioadmin") \
    .config("spark.hadoop.fs.s3a.path.style.access", "true") \
    .getOrCreate()

# Lire depuis Bronze
df_bronze = spark.read.csv(
    "s3a://bronze/raw/sales_data.csv",
    header=True,
    inferSchema=True
)

print("📥 Données Bronze:")
df_bronze.show()

# Transformer
df_silver = df_bronze \
    .withColumn("date", to_date(col("date"))) \
    .withColumn("category", upper(col("category"))) \
    .withColumn("total", col("amount") * col("quantity")) \
    .dropDuplicates()

print("🔄 Données transformées:")
df_silver.show()

# Écrire vers Silver (Parquet partitionné)
df_silver.write \
    .mode("overwrite") \
    .partitionBy("category") \
    .parquet("s3a://silver/sales/")

print("✅ Données écrites vers silver/sales/")
"""
print(step3_code)
Voir le code
# Étape 4 : Agréger avec Spark SQL (Silver → Gold)

step4_code = """
# Lire depuis Silver
df_silver = spark.read.parquet("s3a://silver/sales/")

# Créer une vue temporaire
df_silver.createOrReplaceTempView("sales")

# Agrégation avec Spark SQL
df_gold = spark.sql(\"\"\"
    SELECT 
        category,
        COUNT(*) as num_transactions,
        SUM(quantity) as total_quantity,
        SUM(total) as total_revenue,
        AVG(total) as avg_transaction
    FROM sales
    GROUP BY category
    ORDER BY total_revenue DESC
\"\"\")

print("📊 Agrégations Gold:")
df_gold.show()

# Écrire vers Gold
df_gold.coalesce(1) \
    .write \
    .mode("overwrite") \
    .parquet("s3a://gold/category_summary/")

print("✅ Données écrites vers gold/category_summary/")
"""
print(step4_code)
Voir le code
# Étape 5 : Vérifier les résultats

step5_code = """
# Lister les fichiers créés
import subprocess

# Avec mc CLI
print("📁 Contenu de bronze/:")
!mc ls myminio/bronze/ --recursive

print("\n📁 Contenu de silver/:")
!mc ls myminio/silver/ --recursive

print("\n📁 Contenu de gold/:")
!mc ls myminio/gold/ --recursive

# Ou avec boto3
for bucket in ['bronze', 'silver', 'gold']:
    print(f"\n📁 {bucket}/")
    response = s3.list_objects_v2(Bucket=bucket)
    for obj in response.get('Contents', []):
        print(f"   {obj['Key']} ({obj['Size']} bytes)")
"""
print(step5_code)

Quiz de fin de module


❓ Q1. Quelle est la différence entre un prefix et un dossier dans S3 ?

  1. Aucune différence
  2. Un prefix est un vrai dossier créé par S3
  3. Un prefix est juste une convention de nommage, S3 est un key-value store plat
  4. Un dossier peut contenir des sous-dossiers, pas un prefix
💡 Voir la réponse

Réponse : c — S3 est un key-value store plat. Les “/” dans les clés sont juste des caractères comme les autres.


❓ Q2. Quel protocole utiliser pour lire S3 avec Spark ?

  1. s3://
  2. s3a://
  3. https://
  4. hdfs://
💡 Voir la réponse

Réponse : bs3a:// est le protocole Hadoop optimisé pour Spark. s3:// est pour AWS CLI/boto3.


❓ Q3. Pourquoi les petits fichiers sont-ils un problème ?

  1. Ils prennent plus de place
  2. Ils génèrent trop de requêtes API et d’overhead
  3. Ils ne sont pas supportés par Parquet
  4. Ils ne peuvent pas être partitionnés
💡 Voir la réponse

Réponse : b — Chaque fichier = une requête API. 10,000 fichiers = 10,000 GET requests = lent et coûteux.


❓ Q4. Quelle est la différence entre SAS Token et Managed Identity sur Azure ?

  1. SAS Token est plus sécurisé
  2. Managed Identity est temporaire, SAS est permanent
  3. SAS Token est temporaire et partageable, Managed Identity est liée à une ressource Azure
  4. Aucune différence
💡 Voir la réponse

Réponse : c — SAS Token = URL temporaire partageable. Managed Identity = identité attachée à une VM/AKS, plus sécurisé.


❓ Q5. Pourquoi MinIO est-il compatible avec S3 ?

  1. C’est un produit AWS
  2. Il implémente la même API REST que S3
  3. Il utilise les mêmes serveurs
  4. Il copie les données depuis S3
💡 Voir la réponse

Réponse : b — MinIO implémente l’API S3 (REST). Le même code boto3 fonctionne avec les deux.


❓ Q6. Quelle classe de stockage pour des données rarement lues ?

  1. S3 Standard
  2. S3 Intelligent-Tiering
  3. S3 Glacier
  4. S3 One Zone-IA
💡 Voir la réponse

Réponse : c — Glacier pour l’archivage (données rarement lues). Intelligent-Tiering si le pattern d’accès est imprévisible.


❓ Q7. Quel est l’avantage du partitionnement dans un Data Lake ?

  1. Les fichiers sont plus petits
  2. Spark peut ignorer les partitions non pertinentes (partition pruning)
  3. Le stockage coûte moins cher
  4. Les données sont automatiquement compressées
💡 Voir la réponse

Réponse : b — Partition pruning = Spark lit uniquement les partitions qui matchent le filtre.


❓ Q8. Comment authentifier Spark on K8s vers S3 en production ?

  1. Access Keys dans le code
  2. Variables d’environnement
  3. IAM Roles for Service Accounts (IRSA)
  4. Fichier de config local
💡 Voir la réponse

Réponse : c — IRSA permet d’associer un IAM Role à un ServiceAccount K8s. Pas de credentials dans le code.


❓ Q9. Qu’est-ce qu’un Metadata Catalog (Glue, Hive Metastore) ?

  1. Un système de stockage
  2. Un registre des schémas et partitions des tables
  3. Un outil de requêtage SQL
  4. Un système de cache
💡 Voir la réponse

Réponse : b — Le catalog stocke les métadonnées : schéma, localisation, partitions, stats. L’Object Storage ne stocke que les fichiers bruts.


❓ Q10. Quel format de fichier est recommandé pour un Data Lake analytique ?

  1. CSV
  2. JSON
  3. Parquet
  4. XML
💡 Voir la réponse

Réponse : c — Parquet est columnar, compressé, avec schema intégré. Idéal pour l’analytics.


📚 Ressources pour aller plus loin

🌐 Documentation officielle

📖 Articles & Tutoriels


➡️ Prochaine étape

Maintenant que tu maîtrises l’Object Storage, passons aux Table Formats pour transformer ton Data Lake en Lakehouse !

👉 Module suivant : 23_table_formats_delta_iceberg — Delta Lake & Apache Iceberg

Tu vas apprendre :

  • Delta Lake : ACID, Time Travel, Schema Evolution
  • Apache Iceberg : Table format open-source
  • Transaction Log : Comment ça remplace le Metastore
  • Optimisations : Compaction, Z-Ordering, Vacuum

📝 Récapitulatif de ce module

Concept Ce que tu as appris
Cloud Computing IaaS, PaaS, SaaS, régions
Services Cloud DE S3, Glue, BigQuery, Synapse…
Storage Models Block vs File vs Object
Object Storage Buckets, keys, prefixes, protocols
AWS S3 boto3, s3a://, IAM
Azure Blob SDK, abfss://, SAS, Managed Identity
GCS gsutil, gs://, Workload Identity
MinIO Object Storage local S3-compatible
Performance Small files, partitioning, formats
Sécurité Encryption, bucket policies
Coûts Classes de stockage, lifecycle

🎉 Félicitations ! Tu as terminé le module Cloud & Object Storage.

Retour au sommet