Skip to content
Learni
Voir tous les tutoriels
Data Engineering

Comment créer un Data Lake scalable en 2026

Read in English

Introduction

Un data lake est un stockage centralisé de données brutes à grande échelle, suivant le principe schema-on-read : les données arrivent sans schéma imposé, et sont interprétées lors de la lecture. Contrairement aux data warehouses structurés (schema-on-write), il gère volumes massifs de données hétérogènes : logs JSON, images, CSV, vidéos. En 2026, avec l'explosion des données IA (LLM fine-tuning, RAG), les data lakes alimentent les pipelines ML en données historiques riches.

Ce tutoriel intermediate vous guide pour un data lake local scalable avec MinIO (API S3 100% compatible), Python pour ingestion partitionnée (Parquet optimisé), et DuckDB pour queries SQL vectorielles. Analogie : comme un lac naturel où l'eau (données) s'accumule librement, filtrable à la demande. À la fin, setup fonctionnel, extensible à AWS S3 ou Azure. Temps estimé : 30 min. Idéal pour bookmark par data engineers. (142 mots)

Prérequis

  • Docker et Docker Compose installés (version 20+).
  • Python 3.10+ avec pip.
  • Accès internet pour images Docker et paquets PyPI.
  • Connaissances de base en data engineering : Parquet, partitioning, S3-like storage.
Installez les libs Python : pip install minio pandas pyarrow duckdb. Téléchargez mc (MinIO Client) via le code fourni.

Démarrer MinIO avec Docker Compose

docker-compose.yml
version: '3.8'

services:
  minio:
    image: minio/minio:latest
    container_name: minio-server
    ports:
      - "9000:9000"  # API S3
      - "9001:9001"  # Console UI
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"

  volumes:
    minio_data:

networks:
  default:
    name: datalake-net

Ce fichier Docker Compose lance MinIO en serveur S3-like avec persistance de données. Ports 9000 (API) et 9001 (UI web). Utilisez docker compose up -d pour démarrer. Accédez à l'UI : http://localhost:9001 (login: minioadmin/minioadmin). Piège : sans volume, données perdues au redémarrage.

Accéder à l'interface MinIO

Lancez docker compose up -d dans le dossier du fichier. Vérifiez les logs : docker logs minio-server. Ouvrez http://localhost:9001 pour l'UI intuitive : explorer buckets, monitorer usage. MinIO émule parfaitement AWS S3, idéal pour dev/test sans coûts cloud. Prochaine étape : client CLI mc pour automation.

Installer et configurer le client MinIO mc

setup-mc.sh
#!/bin/bash

# Télécharger mc (Linux/macOS, adaptez pour Windows)
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/mc

# Configurer alias pour notre MinIO local
mc alias set myminio http://localhost:9000 minioadmin minioadmin

# Vérifier connexion
echo "Buckets disponibles :"
mc ls myminio

# Nettoyage optionnel : rm mc si pas sudo

Ce script installe mc (MinIO Client, comme AWS CLI pour S3) et crée un alias 'myminio' pointant vers votre instance locale. Exécutez bash setup-mc.sh. Vérifiez : mc admin info myminio. Piège : firewall bloque port 9000 → autorisez-le. mc est essentiel pour scripting buckets/policies.

Créer le bucket Data Lake

Buckets = conteneurs logiques dans S3/MinIO, comme répertoires racine. Pour data lake, un bucket 'datalake' avec policy read publique (local only). Cela permet queries directes sans auth lourde. Utilisez mc pour idempotence : relancez sans erreur.

Créer bucket et policy de lecture

create-bucket.sh
#!/bin/bash

BUCKET=datalake

# Créer bucket si inexistant
mc mb myminio/$BUCKET

# Policy JSON pour read public (local dev only)
cat > public-read.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::$BUCKET/*"]
    }
  ]
}
EOF

mc policy add public-read myminio/$BUCKET public-read.json

# Vérifier
echo "Policy :"
mc policy get-json myminio/$BUCKET
mc ls myminio

Crée bucket 'datalake' et applique policy JSON autorisant lectures anonymes (sûr en local). Exécutez bash create-bucket.sh. Vérifiez UI MinIO. Piège : policy malformée → JSON valide requis ; en prod, utilisez IAM fines. Cela rend le lake queryable sans creds.

Ingérer des données partitionnées

Simulons données de ventes : 1000 lignes (id, date, produit, montant, région). Partitioning par year/month optimise queries (pruning). Convertir CSV → Parquet (colonne, compressé, schema embedded). MinIO lib Python pour upload atomic.

Script Python : Générer et uploader en Parquet

ingest-data.py
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from minio import Minio
from io import BytesIO
import numpy as np
from datetime import datetime, timedelta

client = Minio(
    "localhost:9000",
    access_key="minioadmin",
    secret_key="minioadmin",
    secure=False
)

# Générer data sample
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='H')[:1000]
data = pd.DataFrame({
    'id': range(1000),
    'date': dates,
    'product': np.random.choice(['Laptop', 'Phone', 'Tablet'], 1000),
    'amount': np.random.uniform(100, 1000, 1000),
    'region': np.random.choice(['EU', 'US', 'ASIA'], 1000)
})

# Partitionner par year/month
data['year'] = data['date'].dt.year.astype(str)
data['month'] = data['date'].dt.month.astype(str).str.zfill(2)

for (year, month), group in data.groupby(['year', 'month']):
    table = pa.Table.from_pandas(group)
    buffer = BytesIO()
    pq.write_table(table, buffer)
    buffer.seek(0)
    client.put_object(
        "datalake",
        f"sales/year={year}/month={month}/sales.parquet",
        io.BytesIO(buffer.read()),
        length=buffer.tell(),
        content_type="application/parquet"
    )
    print(f"Uploadé : year={year}/month={month}")

print("Ingestion terminée. Vérifiez UI MinIO.")

Génère dataset ventes partitionné, convertit en Parquet (efficace x10 vs CSV), upload via MinIO SDK. Partition year/month=pruning auto. Exécutez python ingest-data.py. Piège : sans pyarrow, schema perdu ; buffer pour in-memory write. Scalable à TB avec Dask.

Interroger le Data Lake avec SQL

DuckDB : OLAP engine embarqué, lit Parquet direct de S3 sans ETL. Configurez endpoint/credentials pour MinIO. Queries pushdown : filters partition prunés, agrégations vectorisées. Analogie : SQL sur fichiers comme sur DB.

Script Python : Query SQL avec DuckDB

query-lake.py
import duckdb

con = duckdb.connect()

# Installer/charger extension S3
con.execute("INSTALL httpfs; LOAD httpfs;")

# Config MinIO comme S3 (no secure)
con.execute """
SET s3_endpoint='localhost:9000';
SET s3_access_key='minioadmin';
SET s3_secret_key='minioadmin';
SET s3_region='us-east-1';
"""

# Query partitionnée : ventes EU 2023
result = con.execute(
    """
    SELECT 
        product, 
        AVG(amount) as avg_amount,
        COUNT(*) as sales_count
    FROM 's3://datalake/sales/year=2023/*/*.parquet'
    WHERE region = 'EU'
    GROUP BY product
    ORDER BY avg_amount DESC
    """
).fetchdf()

print(result)

# Export optionnel
result.to_csv('query-result.csv', index=False)
print("Résultat exporté en CSV.")

Configure DuckDB pour MinIO/S3, query SQL sur Parquet partitionné (pruning year=2023). Agrégations pushdown ultra-rapides. Exécutez python query-lake.py. Piège : oublie LOAD httpfs → erreur endpoint ; policy read requise. Scalable à PB avec workers.

Ajouter des données incrémentales

Data lakes évoluent : append sans downtime. Réutilisez ingest pour nouveaux lots (schema evolution via Parquet metadata). Vérifiez atomicité.

Script append données 2024

append-data.py
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from minio import Minio
from io import BytesIO
import numpy as np
from datetime import datetime

client = Minio("localhost:9000", access_key="minioadmin", secret_key="minioadmin", secure=False)

# Nouvelles data 2024 (évolue : ajoute 'category')
np.random.seed(123)
dates = pd.date_range('2024-01-01', '2024-03-31', freq='H')[:500]
data_new = pd.DataFrame({
    'id': range(1000, 1500),
    'date': dates,
    'product': np.random.choice(['Laptop Pro', 'Phone Ultra'], 500),
    'amount': np.random.uniform(500, 2000, 500),
    'region': np.random.choice(['EU', 'US'], 500),
    'category': np.random.choice(['Electronics', 'Mobile'], 500)  # Schema evolution
})

data_new['year'] = data_new['date'].dt.year.astype(str)
data_new['month'] = data_new['date'].dt.month.astype(str).str.zfill(2)

for (year, month), group in data_new.groupby(['year', 'month']):
    table = pa.Table.from_pandas(group)
    buffer = BytesIO()
    pq.write_table(table, buffer)
    buffer.seek(0)
    client.put_object(
        "datalake",
        f"sales/year={year}/month={month}/sales.parquet",
        io.BytesIO(buffer.read()),
        length=buffer.tell(),
        content_type="application/parquet"
    )
    print(f"Appended : year={year}/month={month}")

print("Append terminé. Re-query pour vérifier.")

Appende data 2024 avec nouvelle colonne 'category' (evolution schema tolérée). Même pattern partitioning. Exécutez après ingest initial. Piège : overwrite accidentel → utilisez noms fichiers timestamped en prod. DuckDB lit auto nouvelles cols.

Bonnes pratiques

  • Partitionnez toujours : year/month/day pour pruning (gain x100 perf).
  • Utilisez table formats : Migrez vers Iceberg/Delta pour ACID, time-travel (vs Parquet pur).
  • Catalog central : Intégrez Hive Metastore ou AWS Glue pour métadonnées.
  • Data quality : Validez schemas/NULLs à l'ingestion (Great Expectations).
  • Sécurité : Policies least-privilege, encryption SSE-S3 ; pas public en prod.
  • Monitoring : Prometheus + MinIO metrics pour usage/throughput.

Erreurs courantes à éviter

  • Data swamp : Sans gouvernance/catalog, données ingérables → imposez tagging/zones (raw/curated).
  • Oubli partitioning/compression : Scans full → coûts/query lents ; toujours Snappy/Zstd.
  • Schema-on-write forcé : Tue flexibilité lake → laissez schema-on-read, fixez à consommation.
  • Pas de versioning : Perte audits → Iceberg snapshots ou S3 versioning.
  • Ignorer local vs prod : Policy public OK dev, mais VPC/ IAM en cloud.

Pour aller plus loin

  • Production : Migrez vers AWS S3 + Athena + Glue Catalog (remplacez endpoint).
  • Avancé : Apache Iceberg avec Spark/Trino pour tables transactionnelles.
  • Outils : Airflow pour orchestration, dbt pour transformations.
  • Ressources : Doc MinIO, DuckDB S3.
Découvrez nos formations Data Engineering Learni pour masterclass Iceberg/Databricks.